import { describe, test, expect, afterEach, vi, beforeEach } from "vitest";
import { enableAutoUnmount, mount } from "@vue/test-utils";
import { nextTick } from "vue";

import type { TableColumn } from "../types";

import OTable from "@/components/table/Table.vue";

describe("OTable tests", () => {
    enableAutoUnmount(afterEach);

    const data = [
        {
            id: 1,
            first_name: "Jesse",
            last_name: "Simmons",
            date: "2016-10-15 13:43:27",
            gender: "Male",
        },
        {
            id: 2,
            first_name: "John",
            last_name: "Jacobs",
            date: "2016-12-15 06:00:53",
            gender: "Male",
        },
        {
            id: 3,
            first_name: "Tina",
            last_name: "Gilbert",
            date: "2016-04-26 06:26:28",
            gender: "Female",
        },
        {
            id: 4,
            first_name: "Clarence",
            last_name: "Flores",
            date: "2016-04-10 10:28:46",
            gender: "Male",
        },
        {
            id: 5,
            first_name: "Anne",
            last_name: "Lee",
            date: "2016-12-06 14:38:38",
            gender: "Female",
        },
        {
            id: 6,
            first_name: "Bonita",
            last_name: "Cortez",
            date: "2016-03-03 10:28:46",
            gender: "Female",
        },
        {
            id: 7,
            first_name: "Randolf",
            last_name: "Mayor",
            date: "2016-07-04 14:38:39",
            gender: "Male",
        },
    ];

    const columns: TableColumn<(typeof data)[number]>[] = [
        {
            field: "id",
            label: "ID",
            width: "40",
            numeric: true,
            sortable: true,
        },
        {
            field: "first_name",
            label: "First Name",
            sortable: true,
        },
        {
            field: "last_name",
            label: "Last Name",
            sortable: true,
        },
        {
            field: "date",
            label: "Date",
            position: "centered",
            sortable: true,
        },
        {
            field: "gender",
            label: "Gender",
            sortable: true,
        },
    ];

    test("render correctly", async () => {
        const wrapper = mount(OTable, {
            props: { data, columns },
        });
        await nextTick(); // await child component rendering

        expect(!!wrapper.vm).toBeTruthy();
        expect(wrapper.exists()).toBeTruthy();
        expect(wrapper.attributes("data-oruga")).toBe("table");
        expect(wrapper.html()).toMatchSnapshot();
    });

    test("holds columns", async () => {
        const wrapper = mount(OTable, {
            props: {
                columns: [
                    { label: "default", width: "100px" },
                    { label: "pecent", width: "50%" },
                    { label: "fixed_num", width: 100 },
                    { label: "fixed_str", width: "100" },
                ],
            },
        });
        await nextTick();

        const headers = wrapper.findAll("th");

        expect(headers).toHaveLength(4);

        const cols = headers.filter((th) => th.find("span").exists());
        expect(cols).toHaveLength(4);

        expect(cols[0].attributes("style")).toBe(
            "width: 100px; min-width: 100px;",
        );
        expect(cols[1].attributes("style")).toBe("width: 50%; min-width: 50%;");
        expect(cols[2].attributes("style")).toBe(
            "width: 100px; min-width: 100px;",
        );
        expect(cols[3].attributes("style")).toBe(
            "width: 100px; min-width: 100px;",
        );
    });

    test("displays all data", async () => {
        const wrapper = mount(OTable, {
            props: {
                columns: [
                    { label: "ID", field: "id", numeric: true },
                    { label: "Name", field: "name", filterable: true },
                ],
                data,
            },
        });
        await nextTick();

        const bodyRows = wrapper.findAll("tbody tr");
        expect(bodyRows).toHaveLength(data.length);
    });

    describe("test column props", () => {
        test("test column label", async () => {
            const wrapper = mount(OTable, { props: { data, columns } });
            await nextTick(); // await child component rendering

            const table = wrapper.find("table");
            expect(table.exists()).toBeTruthy();
            const ths = table.findAll("th");
            expect(ths).toHaveLength(columns.length);

            for (let i = 0; i < ths.length; i++) {
                expect(ths[i].text()).toBe(columns[i].label);
            }
        });

        test("test column field", async () => {
            const wrapper = mount(OTable, { props: { data, columns } });
            await nextTick(); // await child component rendering

            const table = wrapper.find("table");
            expect(table.exists()).toBeTruthy();
            const trs = table.findAll("tr");
            expect(trs).toHaveLength(data.length + 1);

            for (let i = 1; i < trs.length; i++) {
                const tds = trs[i].findAll("td");
                expect(tds).toHaveLength(columns.length);

                for (let j = 0; j < columns.length; j++) {
                    expect(tds[j].text()).toBe(
                        String(data[i - 1][columns[j].field!]),
                    );
                }
            }
        });

        test("test column formatter", async () => {
            const columns: TableColumn<(typeof data)[number]>[] = [
                {
                    label: "ID",
                    width: "40",
                    numeric: true,
                    sortable: true,
                    thAttrs: { style: { "min-width": "40px" } },
                    formatter: (a, b): string => {
                        expect(a).toBe(b);
                        return "abc";
                    },
                },
                {
                    label: "First Name",
                    sortable: true,
                    formatter: (a, b) => {
                        expect(a).toBe(b);
                        return "abc";
                    },
                },
            ];

            const wrapper = mount(OTable, { props: { data, columns } });
            await nextTick(); // await child component rendering

            const table = wrapper.find("table");
            expect(table.exists()).toBeTruthy();
            const trs = table.findAll("tr");
            expect(trs).toHaveLength(data.length + 1);

            for (let i = 1; i < trs.length; i++) {
                const tds = trs[i].findAll("td");
                expect(tds).toHaveLength(columns.length);

                for (let j = 0; j < columns.length; j++) {
                    expect(tds[j].text()).toBe(
                        columns[j].formatter?.(data[i], data[i]),
                    );
                }
            }
        });

        test("test column thAttrs and tdAttrs", async () => {
            const columns: TableColumn<(typeof data)[number]>[] = [
                {
                    field: "id",
                    label: "ID",
                    width: "40",
                    numeric: true,
                    sortable: true,
                    thAttrs: { class: "th-id" },
                    tdAttrs: { class: "td-id" },
                },
                {
                    field: "abc",
                    label: "ABC",
                    sortable: true,
                    thAttrs: { class: "th-abc" },
                    tdAttrs: { class: "td-abc" },
                },
            ];

            const wrapper = mount(OTable, { props: { data, columns } });
            await nextTick(); // await child component rendering

            const table = wrapper.find("table");
            expect(table.exists()).toBeTruthy();
            const ths = table.findAll("th");
            expect(ths).toHaveLength(columns.length);
            expect(ths[0].classes("th-id")).toBeTruthy();
            expect(ths[1].classes("th-abc")).toBeTruthy();

            const tds = table.findAll("td");
            expect(tds).toHaveLength(columns.length * data.length);

            for (let i = 0; i < tds.length; i++) {
                expect(
                    tds[i].classes(i % 2 === 0 ? "td-id" : "td-abc"),
                ).toBeTruthy();
            }
        });
    });

    describe("test filterable", () => {
        const data = [
            { id: 1, name: "Jesse" },
            { id: 2, name: "João" },
            { id: 3, name: "Tina" },
            { id: 4, name: "Anne" },
            { id: 5, name: "Clarence" },
        ];

        beforeEach(() => {
            vi.useFakeTimers();
        });

        afterEach(() => {
            vi.clearAllTimers();
            vi.restoreAllMocks();
        });

        test("displays filter row when at least one column is filterable", async () => {
            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", filterable: true },
                    ],
                    data,
                },
            });
            await nextTick();

            const header = wrapper.find("thead");
            expect(header.exists()).toBeTruthy();

            const headRows = header.findAll("tr");
            expect(headRows).toHaveLength(2);

            const inputs = headRows[1].findAll("input");
            expect(inputs).toHaveLength(1);
        });

        test("displays filter input only on filterable columns", async () => {
            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", filterable: true },
                    ],
                    data,
                },
            });
            await nextTick();

            const headRows = wrapper.findAll("thead tr");
            expect(headRows).toHaveLength(2);
            const filterCells = headRows[1].findAll("th");

            expect(filterCells[0].find("input").exists()).toBeFalsy(); // ID column is not filterable
            expect(filterCells[1].find("input").exists()).toBeTruthy(); // Name column is filterable
        });

        test("displays filtered data when filtering", async () => {
            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", filterable: true },
                    ],
                    data,
                },
            });
            await nextTick();

            let bodyRows = wrapper.findAll("tbody tr");
            expect(bodyRows).toHaveLength(data.length); // check all is shown

            const header = wrapper.find("thead");
            const inputs = header.findAll("input");
            expect(inputs).toHaveLength(1);

            const input = inputs[0];

            // displays a subset of data when filtering by "J"
            await input.setValue("J");
            await input.trigger("input");
            vi.runAllTimers(); // run debounce timer
            await nextTick();

            bodyRows = wrapper.findAll("tbody tr");
            expect(bodyRows).toHaveLength(2); // Jesse and João

            expect(wrapper.emitted("filters-change")).toHaveLength(1);

            // displays a subset of data when filtering by name without accent
            await input.setValue("Joao");
            await input.trigger("input");
            vi.runAllTimers(); // run debounce timer
            await nextTick();

            bodyRows = wrapper.findAll("tbody tr");
            expect(bodyRows).toHaveLength(1); // João

            expect(wrapper.emitted("filters-change")).toHaveLength(2);

            // displays a subset of  data when filtering by name with accent
            await input.setValue("João");
            await input.trigger("input");
            vi.runAllTimers(); // run debounce timer
            await nextTick();

            bodyRows = wrapper.findAll("tbody tr");
            expect(bodyRows).toHaveLength(1); // João

            expect(wrapper.emitted("filters-change")).toHaveLength(3);

            // reset filter
            await input.setValue("");
            await input.trigger("input");
            vi.runAllTimers(); // run debounce timer
            await nextTick();

            bodyRows = wrapper.findAll("tbody tr");
            expect(bodyRows).toHaveLength(data.length); // check all is shown again

            expect(wrapper.emitted("filters-change")).toHaveLength(4);
        });

        test("displays filtered data when filtering and updating data", async () => {
            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", filterable: true },
                    ],
                    data,
                },
            });
            await nextTick();

            const input = wrapper.find("thead input");
            expect(input.exists()).toBeTruthy();

            await input.setValue("J");
            await input.trigger("input");
            vi.runAllTimers(); // run debounce timer
            await nextTick();

            let bodyRows = wrapper.findAll("tbody tr");
            expect(bodyRows).toHaveLength(2); // Jesse and João

            await wrapper.setProps({
                data: [...data, { id: 6, name: "Justin" }],
            });

            bodyRows = wrapper.findAll("tbody tr");
            expect(bodyRows).toHaveLength(3); // Jesse, João and Justin
        });

        test("debounce search filtering when filter-debounce is defined", async () => {
            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", filterable: true },
                    ],
                    data,
                    filterDebounce: 1000,
                },
            });
            await nextTick(); // await child component rendering

            const input = wrapper.find("thead input");
            expect(input.exists()).toBeTruthy();

            for (let i = 0; i < 10; i++) {
                await input.setValue("J".repeat(10 - i));
                await input.trigger("input");
                const bodyRows = wrapper.findAll("tbody tr");
                expect(bodyRows).toHaveLength(5); // No filtering yet
            }

            vi.runAllTimers(); // run debounce timer

            const bodyRows = wrapper.findAll("tbody tr");
            expect(bodyRows).toHaveLength(5); // Filtering after debounce
        });

        test("hide detail row when data is filtered out", async () => {
            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", filterable: true },
                    ],
                    data,
                    detailed: true,
                    // open detail for first data row
                    detailedRows: [data[0]],
                },
                slots: { detail: "DETAIL" },
            });
            await nextTick();

            const input = wrapper.find("thead input");
            expect(input.exists()).toBeTruthy();

            let bodyRows = wrapper.findAll("tbody tr");
            expect(bodyRows).toHaveLength(data.length + 1); // data rows + detail for row 1
            expect(bodyRows[1].text()).toBe("DETAIL"); // check second row is detal row

            await input.setValue("T");
            await input.trigger("input");
            vi.runAllTimers(); // run debounce timer
            await nextTick();

            bodyRows = wrapper.findAll("tbody tr");
            expect(bodyRows).toHaveLength(1); // Tina
        });
    });

    describe("test sorting", () => {
        const random_words = [
            "Apfel", // A
            "Ball", // B
            "Clown", // C
            "Dach", // D
            "Elefant", // E
            "Fisch", // F
            "Garten", // G
            "Haus", // H
            "Insel", // I
            "Jacke", // J
            "Kugel", // K
            "Lampe", // L
            "Maus", // M
            "Nase", // N
            "Ozean", // O
            "Pferd", // P
            "Qualle", // Q
            "Rose", // R
            "Sonne", // S
            "Tiger", // T
            "Uhr", // U
            "Vogel", // V
            "Wolke", // W
            "Xylofon", // X
            "Yacht", // Y
            "Zebra", // Z
        ];

        test("tests sort default when paginated", async () => {
            const perPage = 10;
            let lastId = 1;
            const data = random_words.map((word) => ({
                id: lastId++,
                name: word,
            }));

            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", sortable: true },
                    ],
                    perPage,
                    paginated: true,
                    data,
                    defaultSort: "name",
                },
            });
            await nextTick(); // await column rendered

            const head = wrapper.find("thead");
            const ths = head.findAll("th");
            expect(ths).toHaveLength(2);
            expect(ths[1].classes("o-table__th--sortable")).toBeTruthy();
            // check arrow pointing upwards
            expect(
                ths[1].find('[data-oruga="icon"]').attributes("style"),
            ).toBeFalsy();

            const body = wrapper.find("tbody");
            const trs = body.findAll("tr");
            expect(trs).toHaveLength(perPage);

            // check default sort
            for (let idx = 0; idx < perPage; idx++) {
                const tds = trs[idx].findAll("td");
                expect(tds).toHaveLength(2);
                expect(tds[1].text()).toBe(data[idx].name);
            }
        });

        test("tests sort default desc when paginated", async () => {
            const perPage = 10;
            let lastId = 1;
            const data = random_words.map((word) => ({
                id: lastId++,
                name: word,
            }));

            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", sortable: true },
                    ],
                    perPage,
                    paginated: true,
                    data,
                    defaultSort: "name",
                    defaultSortDirection: "desc",
                },
            });
            await nextTick(); // await column rendered

            const head = wrapper.find("thead");
            const ths = head.findAll("th");
            expect(ths).toHaveLength(2);
            expect(ths[1].classes("o-table__th--sortable")).toBeTruthy();
            // check arrow pointing downwards
            expect(
                ths[1].find('[data-oruga="icon"]').attributes("style"),
            ).toBeTruthy();
            const body = wrapper.find("tbody");

            const trs = body.findAll("tr");
            expect(trs).toHaveLength(perPage);

            // check default desc sort
            for (let idx = 0; idx < perPage; idx++) {
                const tds = trs[idx].findAll("td");
                expect(tds).toHaveLength(2);
                expect(tds[1].text()).toBe(data[data.length - 1 - idx].name);
            }
        });

        test("tests sort change when paginated", async () => {
            const perPage = 10;
            let lastId = 1;
            const data = random_words.map((word) => ({
                id: lastId++,
                name: word,
            }));

            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", sortable: true },
                    ],
                    perPage,
                    paginated: true,
                    data,
                    defaultSort: "name",
                },
            });
            await nextTick(); // await column rendered

            const head = wrapper.find("thead");
            const ths = head.findAll("th");
            expect(ths).toHaveLength(2);
            expect(ths[1].classes("o-table__th--sortable")).toBeTruthy();
            // check arrow pointing upwards
            expect(
                ths[1].find('[data-oruga="icon"]').attributes("style"),
            ).toBeFalsy();
            const body = wrapper.find("tbody");

            let trs = body.findAll("tr");
            expect(trs).toHaveLength(perPage);

            // check asc sort
            for (let idx = 0; idx < perPage; idx++) {
                const tds = trs[idx].findAll("td");
                expect(tds).toHaveLength(2);
                expect(tds[1].text()).toBe(data[idx].name);
            }

            await ths[1].trigger("click");
            // check arrow pointing downwards
            expect(
                ths[1].find('[data-oruga="icon"]').attributes("style"),
            ).toBeTruthy();

            trs = body.findAll("tr");
            expect(trs).toHaveLength(perPage);

            // check desc sort
            for (let idx = 0; idx < perPage; idx++) {
                const tds = trs[idx].findAll("td");
                expect(tds).toHaveLength(2);
                expect(tds[1].text()).toBe(data[data.length - 1 - idx].name);
            }
        });

        test("tests sort correctly after data update when paginated", async () => {
            const perPage = 10;
            let lastId = 1;
            const data = random_words.map((word) => ({
                id: lastId++,
                name: word,
            }));

            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", sortable: true },
                    ],
                    perPage,
                    paginated: true,
                    data,
                    defaultSort: "name",
                },
            });
            await nextTick(); // await column rendered

            const head = wrapper.find("thead");
            const ths = head.findAll("th");
            expect(ths).toHaveLength(2);
            expect(ths[1].classes("o-table__th--sortable")).toBeTruthy();
            // check arrow pointing upwards
            expect(
                ths[1].find('[data-oruga="icon"]').attributes("style"),
            ).toBeFalsy();

            const body = wrapper.find("tbody");
            let trs = body.findAll("tr");
            // check only item count of one page is displayed
            expect(trs).toHaveLength(perPage);

            // update table data
            const newData = [
                ...random_words.slice(0, 3).map((word) => ({
                    id: lastId++,
                    name: word,
                })),
                ...data,
            ];
            await wrapper.setProps({ data: newData });

            trs = body.findAll("tr");
            // check only item count of one page is displayed
            expect(trs).toHaveLength(perPage);

            const sortedData = newData.sort((a, b) =>
                a.name.localeCompare(b.name),
            );

            // check default desc sort
            for (let idx = 0; idx < perPage; idx++) {
                const tds = trs[idx].findAll("td");
                expect(tds).toHaveLength(2);
                expect(tds[1].text()).toBe(sortedData[idx].name);
            }
        });
    });

    describe("test checkable", () => {
        test("tests header has checkAll checkbox", async () => {
            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name" },
                    ],
                    checkable: true,
                    paginated: false,
                    data: data,
                },
            });
            await nextTick();

            const head = wrapper.find("thead");
            expect(head.exists()).toBeTruthy();
            const checkboxes = head.findAll("input");
            expect(checkboxes).toHaveLength(1);
        });

        test("tests isAllUncheckable method", async () => {
            const isRowCheckable = vi.fn(() => false);

            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", filterable: true },
                    ],
                    checkable: true,
                    isRowCheckable: isRowCheckable,
                    paginated: false,
                    data: [
                        {
                            id: 1,
                            first_name: "Jesse",
                            last_name: "Simmons",
                            date: "2016-10-15 13:43:27",
                            gender: "Male",
                        },
                    ],
                },
            });
            await nextTick();

            const body = wrapper.find("tbody");
            const checkboxes = body.findAll("input");
            expect(checkboxes).toHaveLength(1);
            expect(checkboxes[0].attributes("disabled")).toBe("");
        });

        test("tests checkAll working correctly", async () => {
            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name" },
                    ],
                    checkable: true,
                    paginated: false,
                    data: data,
                },
            });
            await nextTick();

            const head = wrapper.find("thead");
            expect(head.exists()).toBeTruthy();
            const headerCheckboxes = head.findAll("input");
            expect(headerCheckboxes).toHaveLength(1);
            const checkAllCheckbox = headerCheckboxes[0];

            const body = wrapper.find("tbody");
            expect(body.exists()).toBeTruthy();
            const checkboxes = body.findAll("input");
            expect(checkboxes).toHaveLength(data.length);
            // assert no row is checked
            checkboxes.forEach((checkbox) => {
                expect(checkbox.element.checked).toBeFalsy();
            });

            // check checkAll checkbox
            await checkAllCheckbox.setValue(true);
            // assert all rows area are checked
            checkboxes.forEach((checkbox) => {
                expect(checkbox.element.checked).toBeTruthy();
            });

            // decheck checkAll checkbox again
            await checkAllCheckbox.setValue(false);
            // assert no row is checked
            checkboxes.forEach((checkbox) => {
                expect(checkbox.element.checked).toBeFalsy();
            });
        });
    });

    describe("test pageable", () => {
        test("show correct amount of rows when per page is set to 3", async () => {
            const perPage = 3;

            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", filterable: true },
                    ],
                    paginated: true,
                    data: data,
                    perPage: perPage,
                },
            });
            await nextTick(); // await child component rendering

            const trsPageOne = wrapper.findAll("tbody tr");
            expect(trsPageOne).toHaveLength(perPage);

            await wrapper.setProps({ currentPage: 2 });

            const trsPageTwo = wrapper.findAll("tbody tr");
            expect(trsPageTwo).toHaveLength(
                Math.min(data.length - perPage, perPage),
            );
        });

        test("show correct amount of rows when per page is set to 5", async () => {
            const perPage = 5;

            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", filterable: true },
                    ],
                    paginated: true,
                    data: data,
                    perPage: perPage,
                },
            });
            await nextTick(); // await child component rendering

            const trsPageOne = wrapper.findAll("tbody tr");
            expect(trsPageOne).toHaveLength(perPage);

            await wrapper.setProps({ currentPage: 2 });

            const trsPageTwo = wrapper.findAll("tbody tr");
            expect(trsPageTwo).toHaveLength(
                Math.min(data.length - perPage, perPage),
            );
        });

        test("show correct amount of rows when pageable and has filter", async () => {
            const data = [
                { id: 1, name: "Jesse" },
                { id: 2, name: "João" },
                { id: 3, name: "Tina" },
                { id: 4, name: "Marco" },
                { id: 5, name: "Hannes" },
                { id: 6, name: "Anne" },
                { id: 7, name: "Clarence" },
            ];
            const perPage = 5;

            vi.useFakeTimers(); // use fake timers for debounce
            const wrapper = mount(OTable, {
                props: {
                    columns: [
                        { label: "ID", field: "id", numeric: true },
                        { label: "Name", field: "name", filterable: true },
                    ],
                    data: data,
                    paginated: true,
                    perPage: perPage,
                },
            });
            await nextTick(); // await child component rendering

            const body = wrapper.find("tbody");
            const trs = body.findAll("tr");
            expect(trs).toHaveLength(perPage);

            const input = wrapper.find("thead input");
            expect(input.exists()).toBeTruthy();

            await input.setValue("j");
            await input.trigger("input");
            vi.runAllTimers(); // run debounce timer
            await nextTick(); // await child component rendering

            expect(wrapper.findAll("tbody tr")).toHaveLength(2); // Jesse/João

            await input.setValue("e");
            await input.trigger("input");
            vi.runAllTimers(); // run debounce timer
            await nextTick(); // await child component rendering

            expect(wrapper.findAll("tbody tr")).toHaveLength(4); // Jesse/Anne/Hannes/Clarence

            vi.useRealTimers();
        });
    });
});
