import { test, describe, assert, afterEach, vi } from "vitest"; import { cleanup, render } from "@gradio/tootils"; import event from "@testing-library/user-event"; import { setupi18n } from "../app/src/i18n"; import CheckboxGroup from "./Index.svelte"; import type { LoadingStatus } from "@gradio/statustracker"; const loading_status: LoadingStatus = { eta: 0, queue_position: 1, queue_size: 1, status: "complete" as LoadingStatus["status"], scroll_to_output: false, visible: true, fn_index: 0, show_progress: "full" }; beforeEach(() => { setupi18n(); }); afterEach(cleanup); describe("Values", () => { test("renders correct value when passed as string: single value", async () => { const { getByLabelText } = await render(CheckboxGroup, { value: ["choice_one"], label: "Dropdown", choices: [ ["Choice One", "choice_one"], ["Choice Two", "choice_two"] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; const item_two = getByLabelText("Choice Two") as HTMLInputElement; expect(item_one).toBeChecked(); expect(item_two).not.toBeChecked(); }); test("renders correct value when passed as string: multiple values", async () => { const { getByLabelText } = await render(CheckboxGroup, { value: ["choice_one", "choice_three"], label: "Dropdown", choices: [ ["Choice One", "choice_one"], ["Choice Two", "choice_two"], ["Choice Three", "choice_three"] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; const item_two = getByLabelText("Choice Two") as HTMLInputElement; const item_three = getByLabelText("Choice Three") as HTMLInputElement; assert.equal(item_one.checked, true); assert.equal(item_two.checked, false); assert.equal(item_three.checked, true); expect(item_one).toBeChecked(); expect(item_two).not.toBeChecked(); expect(item_three).toBeChecked(); }); test("renders correct value when passed as number: single value", async () => { const { getByLabelText } = await render(CheckboxGroup, { value: [1], label: "Dropdown", choices: [ ["Choice One", 1], ["Choice Two", 2] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; const item_two = getByLabelText("Choice Two") as HTMLInputElement; expect(item_one).toBeChecked(); expect(item_two).not.toBeChecked(); }); test("renders correct value when passed as number: multiple values", async () => { const { getByLabelText } = await render(CheckboxGroup, { value: [1, 3], label: "Dropdown", choices: [ ["Choice One", 1], ["Choice Two", 2], ["Choice Three", 3] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; const item_two = getByLabelText("Choice Two") as HTMLInputElement; const item_three = getByLabelText("Choice Three") as HTMLInputElement; expect(item_one).toBeChecked(); expect(item_two).not.toBeChecked(); expect(item_three).toBeChecked(); }); test("component value and rendered value are in sync", async () => { const { getByLabelText, debug, component } = await render(CheckboxGroup, { value: [1, 3], label: "Dropdown", choices: [ ["Choice One", 1], ["Choice Two", 2], ["Choice Three", 3] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; const item_two = getByLabelText("Choice Two") as HTMLInputElement; const item_three = getByLabelText("Choice Three") as HTMLInputElement; expect(item_one).toBeChecked(); expect(item_two).not.toBeChecked(); expect(item_three).toBeChecked(); expect(component.value).toEqual([1, 3]); }); test("changing the component value updates the checkboxes", async () => { const { getByLabelText, debug, component } = await render(CheckboxGroup, { value: [], label: "Dropdown", choices: [ ["Choice One", 1], ["Choice Two", 2], ["Choice Three", 3] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; const item_two = getByLabelText("Choice Two") as HTMLInputElement; const item_three = getByLabelText("Choice Three") as HTMLInputElement; expect(item_one).not.toBeChecked(); expect(item_two).not.toBeChecked(); expect(item_three).not.toBeChecked(); component.value = [1, 3]; expect(item_one).toBeChecked(); expect(item_two).not.toBeChecked(); expect(item_three).toBeChecked(); }); test("setting a value that does not exist does nothing", async () => { const { getByLabelText, component } = await render(CheckboxGroup, { value: [], label: "Dropdown", choices: [ ["Choice One", 1], ["Choice Two", 2], ["Choice Three", 3] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; const item_two = getByLabelText("Choice Two") as HTMLInputElement; const item_three = getByLabelText("Choice Three") as HTMLInputElement; expect(item_one).not.toBeChecked(); expect(item_two).not.toBeChecked(); expect(item_three).not.toBeChecked(); component.value = ["choice_one"]; expect(item_one).not.toBeChecked(); expect(item_two).not.toBeChecked(); expect(item_three).not.toBeChecked(); }); }); describe("Events", () => { test("changing the value via the UI emits a change event", async () => { const { getByLabelText, listen } = await render(CheckboxGroup, { loading_status, value: [], label: "Dropdown", choices: [ ["Choice One", 1], ["Choice Two", 2], ["Choice Three", 3] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; const item_two = getByLabelText("Choice Two") as HTMLInputElement; const item_three = getByLabelText("Choice Three") as HTMLInputElement; expect(item_one).not.toBeChecked(); expect(item_two).not.toBeChecked(); expect(item_three).not.toBeChecked(); const mock = listen("change"); await event.click(item_one); expect(mock.callCount).toBe(1); await event.click(item_three); expect(mock.callCount).toBe(2); }); test("changing the value from outside emits a change event", async () => { const { getByLabelText, component, listen } = await render(CheckboxGroup, { value: [], label: "Dropdown", choices: [ ["Choice One", 1], ["Choice Two", 2], ["Choice Three", 3] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; const item_two = getByLabelText("Choice Two") as HTMLInputElement; const item_three = getByLabelText("Choice Three") as HTMLInputElement; expect(item_one).not.toBeChecked(); expect(item_two).not.toBeChecked(); expect(item_three).not.toBeChecked(); const mock = listen("change"); await (component.value = [1]); expect(mock.callCount).toBe(1); }); test("changing the value from the UI emits an input event", async () => { const { getByLabelText, listen } = await render(CheckboxGroup, { value: [], label: "Dropdown", choices: [ ["Choice One", 1], ["Choice Two", 2], ["Choice Three", 3] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; const mock = listen("input"); await event.click(item_one); expect(mock.callCount).toBe(1); }); test("changing the value from outside DOES NOT emit an input event", async () => { const { getByLabelText, component, listen } = await render(CheckboxGroup, { value: [], label: "Dropdown", choices: [ ["Choice One", 1], ["Choice Two", 2], ["Choice Three", 3] ] }); const mock = listen("input"); await (component.value = [1]); expect(mock.callCount).toBe(0); }); test("changing the value via the UI emits a select event", async () => { const { getByLabelText, listen } = await render(CheckboxGroup, { value: [], label: "Dropdown", choices: [ ["Choice One", 1], ["Choice Two", 2], ["Choice Three", 3] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; const mock = listen("select"); await event.click(item_one); expect(mock.callCount).toBe(1); }); test("select event payload contains the selected value and index", async () => { const { getByLabelText, listen } = await render(CheckboxGroup, { value: [], label: "Dropdown", choices: [ ["Choice One", "val"], ["Choice Two", "val_two"], ["Choice Three", 3] ] }); const item = getByLabelText("Choice Two") as HTMLInputElement; const mock = listen("select"); await event.click(item); expect(mock.calls[0][0].detail.data.value).toBe("val_two"); expect(mock.calls[0][0].detail.data.index).toBe(1); }); test("select event payload contains the correct selected state", async () => { const { getByLabelText, listen } = await render(CheckboxGroup, { value: [], label: "Dropdown", choices: [ ["Choice One", "val"], ["Choice Two", "val_two"], ["Choice Three", 3] ] }); const item = getByLabelText("Choice Two") as HTMLInputElement; const mock = listen("select"); await event.click(item); expect(mock.calls[0][0].detail.data.selected).toBe(true); await event.click(item); expect(mock.calls[1][0].detail.data.selected).toBe(false); }); }); describe("interactive vs static", () => { test("interactive component can be checked", async () => { const { getByLabelText } = await render(CheckboxGroup, { value: [], label: "Dropdown", interactive: true, choices: [ ["Choice One", 1], ["Choice Two", 2] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; await event.click(item_one); expect(item_one).toBeChecked(); }); test("static component cannot be checked", async () => { const { getByLabelText } = await render(CheckboxGroup, { value: [], label: "Dropdown", interactive: false, choices: [ ["Choice One", 1], ["Choice Two", 2] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; await event.click(item_one); expect(item_one).not.toBeChecked(); }); test("interactive component updates the value", async () => { const { getByLabelText, component } = await render(CheckboxGroup, { value: [], label: "Dropdown", interactive: true, choices: [ ["Choice One", 1], ["Choice Two", 2] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; await event.click(item_one); expect(component.value).toEqual([1]); }); test("static component doe not update the value", async () => { const { getByLabelText, component } = await render(CheckboxGroup, { value: [], label: "Dropdown", interactive: false, choices: [ ["Choice One", 1], ["Choice Two", 2] ] }); const item_one = getByLabelText("Choice One") as HTMLInputElement; await event.click(item_one); expect(component.value).toEqual([]); }); });