import Router from "@koa/router"; import getPort from "get-port"; import { Browser, Page } from "playwright"; import type { Context } from "koa"; import Koa from "koa"; import { mount } from "../../mount.js"; import { CheckboxedListField } from "../fields/checkboxed-list.js"; import { Form } from "../form.js"; import { CheckboxedListInput } from "./checkboxed-list-input.js"; import { getBrowser } from "../../utils/browser-creator.js"; import assert from "assert"; import { sleep } from "../../utils/sleep.js"; describe("checkboxed-list", () => { describe("basic tests", async () => { let server: ReturnType; let browser: Browser; let page: Page; afterEach(() => { server.close(); }); it("sends the values", async () => { const port = await getPort(); console.info(`Using port ${port} form checkboxed-list-input.test.ts`); const app = new Koa(); const router = new Router(); const fields = { syllables: new CheckboxedListField(true, async () => { return ["sa", "mple", "co", "de"].map((s) => ({ value: s, label: s.toLocaleUpperCase(), })); }), }; let received_values: unknown; mount( router, "/", new (class extends Form { getFields = () => fields; getControls = () => [new CheckboxedListInput(fields.syllables)]; async onSubmit(ctx: Context) { received_values = await this.getParsedValues(ctx); } async canAccess(_: Context) { return { canAccess: true, message: "" }; } })(), true ); app.use(router.routes()).use(router.allowedMethods()); server = app.listen(port); browser = await getBrowser(); const context = await browser.newContext(); page = await context.newPage(); await page.goto(`http://localhost:${port}`); await page.getByLabel("CO").check(); await page.getByLabel("DE").check(); await page.getByRole("button", { name: "Wyślij" }).click(); assert.deepStrictEqual(received_values, { syllables: { co: "on", de: "on" } }); }); describe("behavior with (non)required", () => { it("allows to send a form when no value is selected and fiels is not required", async () => { const port = await getPort(); console.info(`Using port ${port} form checkboxed-list-input.test.ts`); const app = new Koa(); const router = new Router(); const fields = { syllables: new CheckboxedListField(false, async () => { await sleep(10); return ["sa", "mple", "co", "de"].map((s) => ({ value: s, label: s.toLocaleUpperCase(), })); }), }; let submit_called = false; mount( router, "/", new (class extends Form { getFields = () => fields; getControls = () => [new CheckboxedListInput(fields.syllables)]; async onSubmit(ctx: Context) { submit_called = true; } async canAccess(_: Context) { return { canAccess: true, message: "" }; } })(), true ); app.use(router.routes()).use(router.allowedMethods()); server = app.listen(port); browser = await getBrowser(); const context = await browser.newContext(); page = await context.newPage(); await page.goto(`http://localhost:${port}`); await page.getByRole("button", { name: "Wyślij" }).click(); await sleep(100); assert(submit_called); }); it("does not allow to send a form when no value is selected and field IS required", async () => { const port = await getPort(); console.info(`Using port ${port} form checkboxed-list-input.test.ts`); const app = new Koa(); const router = new Router(); const fields = { syllables: new CheckboxedListField(true, async () => { await sleep(10); return ["sa", "mple", "co", "de"].map((s) => ({ value: s, label: s.toLocaleUpperCase(), })); }), }; let submit_called = false; mount( router, "/", new (class extends Form { getFields = () => fields; getControls = () => [new CheckboxedListInput(fields.syllables)]; async onSubmit(ctx: Context) { submit_called = true; } async canAccess(_: Context) { return { canAccess: true, message: "" }; } })(), true ); app.use(router.routes()).use(router.allowedMethods()); server = app.listen(port); browser = await getBrowser(); const context = await browser.newContext(); page = await context.newPage(); await page.goto(`http://localhost:${port}`); await page.getByRole("button", { name: "Wyślij" }).click(); await sleep(100); assert(submit_called == false); await page.getByText(/empty value/).click({ timeout: 500 }); // should display an error message for that field }); it("allows to send a form when no value is selected and field IS required", async () => { const port = await getPort(); console.info(`Using port ${port} form checkboxed-list-input.test.ts`); const app = new Koa(); const router = new Router(); const fields = { syllables: new CheckboxedListField(true, async () => { await sleep(10); return ["sa", "mple", "co", "de"].map((s) => ({ value: s, label: s.toLocaleUpperCase(), })); }), }; let submit_called = false; mount( router, "/", new (class extends Form { getFields = () => fields; getControls = () => [new CheckboxedListInput(fields.syllables)]; async onSubmit(ctx: Context) { submit_called = true; } async canAccess(_: Context) { return { canAccess: true, message: "" }; } })(), true ); app.use(router.routes()).use(router.allowedMethods()); server = app.listen(port); browser = await getBrowser(); const context = await browser.newContext(); page = await context.newPage(); await page.goto(`http://localhost:${port}`); await page.getByLabel("DE").check(); await page.getByRole("button", { name: "Wyślij" }).click(); await sleep(100); assert(submit_called); }); }); it("renders the label", async () => { const port = await getPort(); console.info(`Using port ${port} form checkboxed-list-input.test.ts`); const app = new Koa(); const router = new Router(); const fields = { syllables: new CheckboxedListField(true, async () => { return ["sa", "mple", "co", "de"].map((s) => ({ value: s, label: s.toLocaleUpperCase(), })); }), }; mount( router, "/", new (class extends Form { getFields = () => fields; getControls = () => [ new CheckboxedListInput(fields.syllables, { label: "ABC" }), ]; async onSubmit(ctx: Context) { // noop } async canAccess(_: Context) { return { canAccess: true, message: "" }; } })(), true ); app.use(router.routes()).use(router.allowedMethods()); server = app.listen(port); browser = await getBrowser(); const context = await browser.newContext(); page = await context.newPage(); await page.goto(`http://localhost:${port}`); await page.getByRole("button", { name: "Wyślij" }).click(); await page.getByText(/ABC/).click({ timeout: 500 }); // should display an error message for that field }); it("groups checkboxes based on the value of the 'group' attribute", async () => { const port = await getPort(); console.info(`Using port ${port} form checkboxed-list-input.test.ts`); const app = new Koa(); const router = new Router(); const fields = { syllables: new CheckboxedListField(true, async () => { return [ ...["e", "y", "u", "i", "o", "a"].map((s, index) => ({ value: "vowel" + index, label: s, group: "vowels", })), ...["q", "w", "r", "t", "p", "s", "test_label"].map((s, index) => ({ value: "consonant" + index, label: s, group: "consonants", })), ]; }), }; mount( router, "/", new (class extends Form { getFields = () => fields; getControls = () => [ new CheckboxedListInput(fields.syllables, { label: "Letters" }), ]; async onSubmit(ctx: Context) { // noop } async canAccess(_: Context) { return { canAccess: true, message: "" }; } })(), true ); app.use(router.routes()).use(router.allowedMethods()); server = app.listen(port); browser = await getBrowser(); const context = await browser.newContext(); page = await context.newPage(); await page.goto(`http://localhost:${port}`); await page.getByRole("button", { name: "Wyślij" }).click(); await page.getByText(/vowels/).click({ timeout: 500 }); // should display an error message for that field await page.getByText(/consonants/).click({ timeout: 500 }); // should display an error message for that field await page.getByText("test_label").click({ timeout: 500 }); }); }); });