From f3f2778f1fdb5c3a8bb104d3931f560e63356335 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 11 Aug 2026 15:25:22 +0200 Subject: [PATCH] test: cover public custom widgets for guests (#6558) --- apps/docs/docs/widgets/custom-api/index.mdx | 3 +++ .../api/src/router/test/widgets/custom-api-v2.spec.ts | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/docs/docs/widgets/custom-api/index.mdx b/apps/docs/docs/widgets/custom-api/index.mdx index 62d557b9a..0e745077b 100644 --- a/apps/docs/docs/widgets/custom-api/index.mdx +++ b/apps/docs/docs/widgets/custom-api/index.mdx @@ -38,6 +38,9 @@ Enable the definition before you use it. The definition must also be available t Each named request declares its minimum board permission. This permission does not depend on the HTTP method. +Anonymous visitors can load queries from a Custom API widget on a public board. Definitions placed only on private +boards remain unavailable to them. + Queries and actions can use GET, POST, PUT, or PATCH. DELETE is valid only for actions so it can never run automatically while a board loads. The `kind` value controls the remaining runtime behavior. diff --git a/packages/api/src/router/test/widgets/custom-api-v2.spec.ts b/packages/api/src/router/test/widgets/custom-api-v2.spec.ts index 8bfc75319..f4bd463a2 100644 --- a/packages/api/src/router/test/widgets/custom-api-v2.spec.ts +++ b/packages/api/src/router/test/widgets/custom-api-v2.spec.ts @@ -113,17 +113,22 @@ async function setup( describe("Custom JSX v2 board router", () => { beforeEach(() => mocks.executeRequest.mockClear()); - test("allows public queries only through the definition placed on an accessible board", async () => { + test("loads placed widgets and public queries for anonymous visitors on public boards", async () => { const { db, itemId } = await setup(); const caller = customApiRouter.createCaller({ db, deviceType: undefined, session: null }); + await expect(caller.getData({ itemId })).resolves.toMatchObject({ + template: definition.template, + data: { "load-status": { value: 42 } }, + status: { "load-status": { ok: true } }, + }); await expect(caller.queryRequest({ itemId, requestId: "status", params: { name: "homarr" } })).resolves.toEqual( expect.objectContaining({ ok: true, data: { value: 42 } }), ); await expect(caller.queryRequest({ itemId, requestId: "missing", params: {} })).rejects.toMatchObject({ code: "NOT_FOUND", }); - expect(mocks.executeRequest).toHaveBeenCalledOnce(); + expect(mocks.executeRequest).toHaveBeenCalledTimes(2); }); test("uses current defaults when an updated option no longer accepts the stored value", async () => { @@ -150,6 +155,7 @@ describe("Custom JSX v2 board router", () => { await expect( anonymous.queryRequest({ itemId: privateSetup.itemId, requestId: "status", params: { name: "homarr" } }), ).rejects.toMatchObject({ code: "NOT_FOUND" }); + await expect(anonymous.getData({ itemId: privateSetup.itemId })).rejects.toMatchObject({ code: "NOT_FOUND" }); const enabledSetup = await setup(); await enabledSetup.db