diff --git a/src/features/housekeeping/domains/content/queries/content-queries.test.ts b/src/features/housekeeping/domains/content/queries/content-queries.test.ts index b45f3885..48a3616d 100644 --- a/src/features/housekeeping/domains/content/queries/content-queries.test.ts +++ b/src/features/housekeeping/domains/content/queries/content-queries.test.ts @@ -471,6 +471,164 @@ describe("Content query", () => { expect(queryMocks.execute).toHaveBeenCalledTimes(5); }); + it("rejects a poll detail with 101 questions before loading results", async () => { + queryMocks.execute + .mockResolvedValueOnce([ + [{ id: 7, title: "Oversized poll", status: "draft" }], + ]) + .mockResolvedValueOnce([ + Array.from({ length: 101 }, (_, index) => ({ + id: index + 1, + question: `Question ${index + 1}`, + type: "single", + sort_order: index, + options: "Yes\nNo", + })), + ]); + + await expect( + loadProductionContentQuery({ + routeId: "content.engagement.poll-detail", + params: { id: "7" }, + list: { + ...normalizedResponsePage, + search: "", + pageSize: 25, + offset: 0, + }, + }), + ).rejects.toThrow("poll question limit"); + expect(queryMocks.execute).toHaveBeenCalledTimes(2); + }); + + it("rejects 10001 grouped poll result rows before aggregating them", async () => { + queryMocks.execute + .mockResolvedValueOnce([ + [{ id: 7, title: "Oversized results", status: "active" }], + ]) + .mockResolvedValueOnce([ + [ + { + id: 21, + question: "Favourite colour?", + type: "single", + sort_order: 0, + options: "Red\nBlue", + }, + ], + ]) + .mockResolvedValueOnce([ + Array.from({ length: 10_001 }, (_, index) => ({ + question_id: 21, + answer: `Choice ${index + 1}`, + answer_count: 1, + })), + ]); + + await expect( + loadProductionContentQuery({ + routeId: "content.engagement.poll-detail", + params: { id: "7" }, + list: { + ...normalizedResponsePage, + search: "", + pageSize: 25, + offset: 0, + }, + }), + ).rejects.toThrow("poll aggregate limit"); + expect(queryMocks.execute).toHaveBeenCalledTimes(3); + }); + + it("rejects a poll question with more than 100 parsed options", async () => { + queryMocks.execute + .mockResolvedValueOnce([ + [{ id: 7, title: "Oversized options", status: "draft" }], + ]) + .mockResolvedValueOnce([ + [ + { + id: 21, + question: "Choose one", + type: "single", + sort_order: 0, + options: Array.from( + { length: 101 }, + (_, index) => `Option ${index + 1}`, + ).join("\n"), + }, + ], + ]); + + await expect( + loadProductionContentQuery({ + routeId: "content.engagement.poll-detail", + params: { id: "7" }, + list: { + ...normalizedResponsePage, + search: "", + pageSize: 25, + offset: 0, + }, + }), + ).rejects.toThrow("poll option limit"); + expect(queryMocks.execute).toHaveBeenCalledTimes(2); + }); + + it.each([ + ["a foreign question", "999"], + ["a non-text question", "21"], + ])( + "falls back to the first text question for %s", + async (_label, requestedId) => { + queryMocks.execute + .mockResolvedValueOnce([ + [{ id: 7, title: "Response fallback", status: "active" }], + ]) + .mockResolvedValueOnce([ + [ + { + id: 21, + question: "Favourite colour?", + type: "single", + sort_order: 0, + options: "Red\nBlue", + }, + { + id: 22, + question: "What should improve?", + type: "text", + sort_order: 1, + options: "", + }, + ], + ]) + .mockResolvedValueOnce([[]]) + .mockResolvedValueOnce([[{ total: 0 }]]) + .mockResolvedValueOnce([[]]); + + const result = await loadProductionContentQuery({ + routeId: "content.engagement.poll-detail", + params: { id: "7" }, + list: { + ...normalizedResponsePage, + search: "", + pageSize: 25, + offset: 0, + responseQuestionId: requestedId, + }, + }); + + expect(result.items[0]?.privatePayload).toMatchObject({ + textResponses: { questionId: "22" }, + }); + expect(JSON.stringify(queryMocks.execute.mock.calls.slice(3))).toContain( + "22", + ); + expect(queryMocks.execute).toHaveBeenCalledTimes(5); + }, + ); + it("returns a valid empty detail result when the selected poll is missing", async () => { queryMocks.execute.mockResolvedValueOnce([[]]); diff --git a/src/features/housekeeping/foundation/foundation-source-contract.test.ts b/src/features/housekeeping/foundation/foundation-source-contract.test.ts index c7e77109..0cb710c1 100644 --- a/src/features/housekeeping/foundation/foundation-source-contract.test.ts +++ b/src/features/housekeeping/foundation/foundation-source-contract.test.ts @@ -294,6 +294,21 @@ const approvedRuntimeImports = new Map>([ "src/features/housekeeping/domains/content/queries/content-queries", ]), ], + [ + "src/features/housekeeping/domains/content/pages/poll-workflow.tsx", + new Set([ + "src/features/housekeeping/domains/content/pages/content-command-form", + "src/features/housekeeping/domains/content/pages/poll-results", + "src/features/housekeeping/domains/content/queries/content-queries", + ]), + ], + [ + "src/features/housekeeping/domains/content/pages/poll-results.tsx", + new Set([ + "src/features/housekeeping/domains/content/pages/poll-workflow", + "src/features/housekeeping/domains/content/queries/content-queries", + ]), + ], [ "src/features/housekeeping/domains/content/pages/brand.tsx", new Set([ @@ -328,6 +343,7 @@ const approvedRuntimeImports = new Map>([ "src/features/housekeeping/domains/content/pages/content-command-form", "src/features/housekeeping/domains/content/pages/content-page-frame", "src/features/housekeeping/domains/content/pages/event-workflow", + "src/features/housekeeping/domains/content/pages/poll-workflow", "src/features/housekeeping/domains/content/queries/content-queries", ]), ], diff --git a/src/features/housekeeping/migration/content.ts b/src/features/housekeeping/migration/content.ts index f90e3085..e4722923 100644 --- a/src/features/housekeeping/migration/content.ts +++ b/src/features/housekeeping/migration/content.ts @@ -300,7 +300,7 @@ export const contentMigrationEntries: readonly MigrationEntry[] = [ targetPath: "/ase-next/content/engagement/polls/:id", decision: "REBUILD", capabilities: { - read: [PERMS.POLLS_EDIT], + read: [PERMS.POLLS_VIEW], mutate: [PERMS.POLLS_EDIT], }, dependencies: { diff --git a/src/messages/ar.json b/src/messages/ar.json index c02721b2..a326a95c 100644 --- a/src/messages/ar.json +++ b/src/messages/ar.json @@ -281,10 +281,12 @@ "delete": "الأحداث · حذف" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "سؤال الاستطلاع · حذف" }, "poll": { - "change": "Poll · Change" + "change": "Poll · Change", + "delete": "استطلاع · حذف" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/bg.json b/src/messages/bg.json index d4aeed82..0e9d2ffe 100644 --- a/src/messages/bg.json +++ b/src/messages/bg.json @@ -3525,10 +3525,12 @@ "delete": "Събития · Изтрий" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Въпрос от анкетата · Изтриване" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Анкета · Изтриване" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/cs.json b/src/messages/cs.json index 5c397903..3d5c8999 100644 --- a/src/messages/cs.json +++ b/src/messages/cs.json @@ -3525,10 +3525,12 @@ "delete": "Události · Smazat" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Otázka ankety · Smazat" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Anketa · Smazat" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/da.json b/src/messages/da.json index 8a620428..f91f60e1 100644 --- a/src/messages/da.json +++ b/src/messages/da.json @@ -3525,10 +3525,12 @@ "delete": "Begivenheder · Slet" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Spørgsmål i afstemning · Slet" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Afstemning · Slet" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/de.json b/src/messages/de.json index aaff805e..a23d3ba2 100644 --- a/src/messages/de.json +++ b/src/messages/de.json @@ -3526,10 +3526,12 @@ "delete": "Ereignisse · Löschen" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Umfragefrage · Löschen" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Umfrage · Löschen" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/el.json b/src/messages/el.json index 38f06429..0a913826 100644 --- a/src/messages/el.json +++ b/src/messages/el.json @@ -3525,10 +3525,12 @@ "delete": "Εκδηλώσεις · Διαγραφή" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Ερώτηση δημοσκόπησης · Διαγραφή" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Δημοσκόπηση · Διαγραφή" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/en.json b/src/messages/en.json index 7071b2ff..3de1f35b 100644 --- a/src/messages/en.json +++ b/src/messages/en.json @@ -3537,10 +3537,12 @@ "delete": "Events · Delete" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Poll question · Delete" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Polls · Delete" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/es.json b/src/messages/es.json index be24a485..e51c9300 100644 --- a/src/messages/es.json +++ b/src/messages/es.json @@ -3526,10 +3526,12 @@ "delete": "Eventos · Eliminar" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Pregunta de encuesta · Eliminar" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Encuesta · Eliminar" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/fi.json b/src/messages/fi.json index fdd7d416..2eb1dd6b 100644 --- a/src/messages/fi.json +++ b/src/messages/fi.json @@ -281,10 +281,12 @@ "delete": "Tapahtumat · Poista" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Kyselyn kysymys · Poista" }, "poll": { - "change": "Poll · Change" + "change": "Poll · Change", + "delete": "Kysely · Poista" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/fr.json b/src/messages/fr.json index f750c078..d5f8c502 100644 --- a/src/messages/fr.json +++ b/src/messages/fr.json @@ -3526,10 +3526,12 @@ "delete": "Événements · Supprimer" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Question du sondage · Supprimer" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Sondage · Supprimer" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/hr.json b/src/messages/hr.json index 290ea565..19edb0ed 100644 --- a/src/messages/hr.json +++ b/src/messages/hr.json @@ -3525,10 +3525,12 @@ "delete": "Događaji · Izbriši" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Pitanje ankete · Izbriši" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Anketa · Izbriši" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/hu.json b/src/messages/hu.json index 93919a24..11f23255 100644 --- a/src/messages/hu.json +++ b/src/messages/hu.json @@ -3525,10 +3525,12 @@ "delete": "Események · Törlés" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Szavazási kérdés · Törlés" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Szavazás · Törlés" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/it.json b/src/messages/it.json index adaec8b9..e855f24a 100644 --- a/src/messages/it.json +++ b/src/messages/it.json @@ -3535,10 +3535,12 @@ "delete": "Eventi · Elimina" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Domanda del sondaggio · Elimina" }, "poll": { - "change": "Sondaggi · Change" + "change": "Sondaggi · Change", + "delete": "Sondaggi · Elimina" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/ja.json b/src/messages/ja.json index e0cd2379..797ea0e3 100644 --- a/src/messages/ja.json +++ b/src/messages/ja.json @@ -281,10 +281,12 @@ "delete": "イベント · 削除" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "アンケートの質問 · 削除" }, "poll": { - "change": "Poll · Change" + "change": "Poll · Change", + "delete": "アンケート · 削除" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/nl.json b/src/messages/nl.json index cdd0970c..c8f11d03 100644 --- a/src/messages/nl.json +++ b/src/messages/nl.json @@ -3440,10 +3440,12 @@ "delete": "Evenementen · Verwijderen" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Pollvraag · Verwijderen" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Polls · Verwijderen" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/no.json b/src/messages/no.json index 1eff7fda..12de862d 100644 --- a/src/messages/no.json +++ b/src/messages/no.json @@ -3525,10 +3525,12 @@ "delete": "Arrangementer · Slett" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Spørsmål i avstemning · Slett" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Avstemning · Slett" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/pl.json b/src/messages/pl.json index e39e9f73..6a64c25d 100644 --- a/src/messages/pl.json +++ b/src/messages/pl.json @@ -3526,10 +3526,12 @@ "delete": "Wydarzenia · Usuń" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Pytanie ankiety · Usuń" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Ankieta · Usuń" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/pt.json b/src/messages/pt.json index 5db731bc..9c7cae72 100644 --- a/src/messages/pt.json +++ b/src/messages/pt.json @@ -3526,10 +3526,12 @@ "delete": "Eventos · Eliminar" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Pergunta da sondagem · Eliminar" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Sondagem · Eliminar" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/ro.json b/src/messages/ro.json index 17812a9e..39316fb8 100644 --- a/src/messages/ro.json +++ b/src/messages/ro.json @@ -3525,10 +3525,12 @@ "delete": "Evenimente · Șterge" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Întrebare de sondaj · Șterge" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Sondaj · Șterge" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/ru.json b/src/messages/ru.json index efe51f22..51a5e671 100644 --- a/src/messages/ru.json +++ b/src/messages/ru.json @@ -3524,10 +3524,12 @@ "delete": "События · Удалить" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Вопрос опроса · Удалить" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Опрос · Удалить" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/sk.json b/src/messages/sk.json index 47f00bdc..05c7195d 100644 --- a/src/messages/sk.json +++ b/src/messages/sk.json @@ -3525,10 +3525,12 @@ "delete": "Udalosti · Odstrániť" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Otázka ankety · Odstrániť" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Anketa · Odstrániť" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/sr.json b/src/messages/sr.json index 14ac5494..3257ad5e 100644 --- a/src/messages/sr.json +++ b/src/messages/sr.json @@ -3525,10 +3525,12 @@ "delete": "Događaji · Obriši" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Питање анкете · Избриши" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Анкета · Избриши" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/sv.json b/src/messages/sv.json index 37bac2f8..53d93c37 100644 --- a/src/messages/sv.json +++ b/src/messages/sv.json @@ -3525,10 +3525,12 @@ "delete": "Evenemang · Ta bort" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Omröstningsfråga · Ta bort" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Omröstning · Ta bort" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/tr.json b/src/messages/tr.json index 2f7040c3..63d052a8 100644 --- a/src/messages/tr.json +++ b/src/messages/tr.json @@ -3526,10 +3526,12 @@ "delete": "Etkinlikler · Sil" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Anket sorusu · Sil" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Anket · Sil" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change" diff --git a/src/messages/uk.json b/src/messages/uk.json index d7b8be68..f01f49f5 100644 --- a/src/messages/uk.json +++ b/src/messages/uk.json @@ -3525,10 +3525,12 @@ "delete": "Події · Видалити" }, "poll-question": { - "change": "Poll Question change · Change" + "change": "Poll Question change · Change", + "delete": "Питання опитування · Видалити" }, "poll": { - "change": "Polls · Change" + "change": "Polls · Change", + "delete": "Опитування · Видалити" }, "prefix-blacklist": { "change": "Prefix Blacklist change · Change"