mirror of
https://github.com/gradio-app/gradio.git
synced 2024-11-21 01:01:05 +08:00
3e4e680a52
* fixes * changes * add changeset * add changeset --------- Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com> Co-authored-by: Hannah <hannahblair@users.noreply.github.com>
496 lines
12 KiB
TypeScript
496 lines
12 KiB
TypeScript
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 Dropdown 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"
|
|
};
|
|
|
|
describe("Dropdown", () => {
|
|
afterEach(() => {
|
|
cleanup();
|
|
vi.useRealTimers();
|
|
});
|
|
beforeEach(() => {
|
|
setupi18n();
|
|
});
|
|
test("renders provided value", async () => {
|
|
const { getByLabelText } = await render(Dropdown, {
|
|
show_label: true,
|
|
loading_status,
|
|
max_choices: null,
|
|
value: "choice",
|
|
label: "Dropdown",
|
|
choices: [
|
|
["choice", "choice"],
|
|
["choice2", "choice2"]
|
|
],
|
|
filterable: false,
|
|
interactive: false
|
|
});
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
assert.equal(item.value, "choice");
|
|
});
|
|
|
|
test("selecting the textbox should show the options", async () => {
|
|
const { getByLabelText, getAllByTestId } = await render(Dropdown, {
|
|
show_label: true,
|
|
loading_status,
|
|
max_choices: 10,
|
|
value: "choice",
|
|
label: "Dropdown",
|
|
choices: [
|
|
["choice", "choice"],
|
|
["name2", "choice2"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
});
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
|
|
await item.focus();
|
|
|
|
const options = getAllByTestId("dropdown-option");
|
|
|
|
expect(options).toHaveLength(2);
|
|
expect(options[0]).toContainHTML("choice");
|
|
expect(options[1]).toContainHTML("name2");
|
|
});
|
|
|
|
test("editing the textbox value should trigger the type event and filter the options", async () => {
|
|
const { getByLabelText, listen, getAllByTestId } = await render(Dropdown, {
|
|
show_label: true,
|
|
loading_status,
|
|
max_choices: 10,
|
|
value: "",
|
|
label: "Dropdown",
|
|
choices: [
|
|
["apple", "apple"],
|
|
["zebra", "zebra"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
});
|
|
|
|
const key_up_event = listen("key_up");
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
|
|
await item.focus();
|
|
const options = getAllByTestId("dropdown-option");
|
|
|
|
expect(options).toHaveLength(2);
|
|
|
|
item.value = "";
|
|
await event.keyboard("z");
|
|
|
|
const options_new = getAllByTestId("dropdown-option");
|
|
|
|
await expect(options_new).toHaveLength(1);
|
|
await expect(options[0]).toContainHTML("zebra");
|
|
await assert.equal(key_up_event.callCount, 1);
|
|
});
|
|
|
|
test("blurring the textbox should cancel the filter", async () => {
|
|
const { getByLabelText, listen } = await render(Dropdown, {
|
|
show_label: true,
|
|
loading_status,
|
|
value: "default",
|
|
label: "Dropdown",
|
|
max_choices: undefined,
|
|
choices: [
|
|
["default", "default"],
|
|
["other", "other"]
|
|
],
|
|
filterable: false,
|
|
interactive: true
|
|
});
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
|
|
item.focus();
|
|
await event.keyboard("other");
|
|
});
|
|
|
|
test("blurring the textbox should save the input value", async () => {
|
|
const { getByLabelText, listen } = await render(Dropdown, {
|
|
show_label: true,
|
|
loading_status,
|
|
value: "new ",
|
|
label: "Dropdown",
|
|
max_choices: undefined,
|
|
allow_custom_value: true,
|
|
choices: [
|
|
["dwight", "dwight"],
|
|
["michael", "michael"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
});
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
const change_event = listen("change");
|
|
|
|
item.focus();
|
|
await event.keyboard("kevin");
|
|
await item.blur();
|
|
|
|
assert.equal(item.value, "new kevin");
|
|
assert.equal(change_event.callCount, 1);
|
|
});
|
|
|
|
test("focusing the label should toggle the options", async () => {
|
|
const { getByLabelText, listen } = await render(Dropdown, {
|
|
show_label: true,
|
|
loading_status,
|
|
value: "default",
|
|
label: "Dropdown",
|
|
choices: [
|
|
["default", "default"],
|
|
["other", "other"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
});
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
const blur_event = listen("blur");
|
|
const focus_event = listen("focus");
|
|
|
|
item.focus();
|
|
item.blur();
|
|
|
|
assert.equal(blur_event.callCount, 1);
|
|
assert.equal(focus_event.callCount, 1);
|
|
});
|
|
|
|
test("deselecting and reselcting a filtered dropdown should show all options again", async () => {
|
|
vi.useFakeTimers();
|
|
const { getByLabelText, getAllByTestId } = await render(Dropdown, {
|
|
show_label: true,
|
|
loading_status,
|
|
max_choices: 10,
|
|
value: "",
|
|
label: "Dropdown",
|
|
choices: [
|
|
["apple", "apple"],
|
|
["zebra", "zebra"],
|
|
["pony", "pony"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
});
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
|
|
item.focus();
|
|
item.value = "";
|
|
await event.keyboard("z");
|
|
const options = getAllByTestId("dropdown-option");
|
|
|
|
expect(options).toHaveLength(1);
|
|
|
|
await item.blur();
|
|
// Mock 100ms delay between interactions.
|
|
vi.runAllTimers();
|
|
await item.focus();
|
|
const options_new = getAllByTestId("dropdown-option");
|
|
|
|
expect(options_new).toHaveLength(3);
|
|
});
|
|
|
|
test("passing in a new set of identical choices when the dropdown is open should not filter the dropdown", async () => {
|
|
const { getByLabelText, getAllByTestId, component } = await render(
|
|
Dropdown,
|
|
{
|
|
show_label: true,
|
|
loading_status,
|
|
value: "",
|
|
label: "Dropdown",
|
|
choices: [
|
|
["apple", "apple"],
|
|
["zebra", "zebra"],
|
|
["pony", "pony"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
}
|
|
);
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
|
|
await item.focus();
|
|
|
|
const options = getAllByTestId("dropdown-option");
|
|
|
|
expect(options).toHaveLength(3);
|
|
|
|
component.$set({
|
|
value: "",
|
|
choices: [
|
|
["apple", "apple"],
|
|
["zebra", "zebra"],
|
|
["pony", "pony"]
|
|
]
|
|
});
|
|
|
|
item.focus();
|
|
|
|
const options_new = getAllByTestId("dropdown-option");
|
|
expect(options_new).toHaveLength(3);
|
|
});
|
|
|
|
test("setting a custom value when allow_custom_choice is false should revert to the first valid choice", async () => {
|
|
const { getByLabelText, getAllByTestId, component } = await render(
|
|
Dropdown,
|
|
{
|
|
show_label: true,
|
|
loading_status,
|
|
value: "",
|
|
allow_custom_value: false,
|
|
label: "Dropdown",
|
|
choices: [
|
|
["apple", "apple"],
|
|
["zebra", "zebra"],
|
|
["pony", "pony"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
}
|
|
);
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
|
|
await item.focus();
|
|
await event.keyboard("pie");
|
|
expect(item.value).toBe("applepie");
|
|
await item.blur();
|
|
expect(item.value).toBe("apple");
|
|
});
|
|
|
|
test("setting a custom value when allow_custom_choice is true should keep the value", async () => {
|
|
const { getByLabelText, getAllByTestId, component } = await render(
|
|
Dropdown,
|
|
{
|
|
show_label: true,
|
|
loading_status,
|
|
value: "",
|
|
allow_custom_value: true,
|
|
label: "Dropdown",
|
|
choices: [
|
|
["apple", "apple"],
|
|
["zebra", "zebra"],
|
|
["pony", "pony"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
}
|
|
);
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
|
|
await item.focus();
|
|
await event.keyboard("pie");
|
|
expect(item.value).toBe("applepie");
|
|
await item.blur();
|
|
expect(item.value).toBe("applepie");
|
|
});
|
|
|
|
test("setting a value should update the displayed value and selected indices", async () => {
|
|
const { getByLabelText, getAllByTestId, component } = await render(
|
|
Dropdown,
|
|
{
|
|
show_label: true,
|
|
loading_status,
|
|
value: "",
|
|
allow_custom_value: false,
|
|
label: "Dropdown",
|
|
choices: [
|
|
["apple", "apple"],
|
|
["zebra", "zebra"],
|
|
["pony", "pony"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
}
|
|
);
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
|
|
expect(item.value).toBe("apple");
|
|
await item.focus();
|
|
let options = getAllByTestId("dropdown-option");
|
|
expect(options[0]).toHaveClass("selected");
|
|
|
|
await component.$set({ value: "zebra" });
|
|
expect(item.value).toBe("zebra");
|
|
options = getAllByTestId("dropdown-option");
|
|
expect(options[0]).toHaveClass("selected");
|
|
|
|
await component.$set({ value: undefined });
|
|
expect(item.value).toBe("");
|
|
options = getAllByTestId("dropdown-option");
|
|
expect(options[0]).not.toHaveClass("selected");
|
|
|
|
await component.$set({ value: "zebra" });
|
|
expect(item.value).toBe("zebra");
|
|
options = getAllByTestId("dropdown-option");
|
|
expect(options[0]).toHaveClass("selected");
|
|
});
|
|
|
|
test("blurring a dropdown should set the input text to the previously selected value", async () => {
|
|
const { getByLabelText, getAllByTestId, component } = await render(
|
|
Dropdown,
|
|
{
|
|
show_label: true,
|
|
loading_status,
|
|
value: "",
|
|
allow_custom_value: false,
|
|
label: "Dropdown",
|
|
choices: [
|
|
["apple", "apple_internal_value"],
|
|
["zebra", "zebra_internal_value"],
|
|
["pony", "pony_internal_value"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
}
|
|
);
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
|
|
expect(item.value).toBe("apple");
|
|
await item.focus();
|
|
let options = getAllByTestId("dropdown-option");
|
|
expect(options[0]).toHaveClass("selected");
|
|
await item.blur();
|
|
expect(item.value).toBe("apple");
|
|
|
|
await item.focus();
|
|
await event.keyboard("z");
|
|
expect(item.value).toBe("applez");
|
|
await item.blur();
|
|
expect(item.value).toBe("apple");
|
|
});
|
|
|
|
test("updating choices should keep the dropdown focus-able and change the value appropriately if custom values are not allowed", async () => {
|
|
const { getByLabelText, component } = await render(Dropdown, {
|
|
show_label: true,
|
|
loading_status,
|
|
value: "apple_internal_value",
|
|
allow_custom_value: false,
|
|
label: "Dropdown",
|
|
choices: [
|
|
["apple_choice", "apple_internal_value"],
|
|
["zebra_choice", "zebra_internal_value"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
});
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
|
|
await expect(item.value).toBe("apple_choice");
|
|
|
|
component.$set({
|
|
choices: [
|
|
["apple_new_choice", "apple_internal_value"],
|
|
["zebra_new_choice", "zebra_internal_value"]
|
|
]
|
|
});
|
|
|
|
await item.focus();
|
|
await item.blur();
|
|
await expect(item.value).toBe("apple_new_choice");
|
|
});
|
|
|
|
test("updating choices should not reset the value if custom values are allowed", async () => {
|
|
const { getByLabelText, component } = await render(Dropdown, {
|
|
show_label: true,
|
|
loading_status,
|
|
value: "apple_internal_value",
|
|
allow_custom_value: true,
|
|
label: "Dropdown",
|
|
choices: [
|
|
["apple_choice", "apple_internal_value"],
|
|
["zebra_choice", "zebra_internal_value"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
});
|
|
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
|
|
await expect(item.value).toBe("apple_choice");
|
|
|
|
component.$set({
|
|
choices: [
|
|
["apple_new_choice", "apple_internal_value"],
|
|
["zebra_new_choice", "zebra_internal_value"]
|
|
]
|
|
});
|
|
|
|
await expect(item.value).toBe("apple_choice");
|
|
});
|
|
|
|
test("ensure dropdown can have an empty value", async () => {
|
|
const { getByLabelText } = await render(Dropdown, {
|
|
show_label: true,
|
|
loading_status,
|
|
allow_custom_value: false,
|
|
label: "Dropdown",
|
|
choices: [
|
|
["apple_choice", "apple_internal_value"],
|
|
["zebra_choice", "zebra_internal_value"]
|
|
],
|
|
filterable: true,
|
|
interactive: true
|
|
});
|
|
const item: HTMLInputElement = getByLabelText(
|
|
"Dropdown"
|
|
) as HTMLInputElement;
|
|
await expect(item.value).toBe("");
|
|
});
|
|
});
|