testing-library / dom-testing-library

🐙 Simple and complete DOM testing utilities that encourage good testing practices.
https://testing-library.com/dom
MIT License
3.26k stars 467 forks source link

Leaky tests #1130

Closed SleeplessOne1917 closed 2 years ago

SleeplessOne1917 commented 2 years ago

Relevant code or config:

const clickAllRollButtons = async () => {
    for (const button of screen.getAllByText(/Roll/i)) {
        await userEvent.click(button);
    }
};

const getSelectOptions = (select: HTMLSelectElement) => {
    const options = [] as HTMLOptionElement[];
    for (let i = 0; i < select.options.length; ++i) {
        options.push(select.options.item(i) as HTMLOptionElement);
    }

    return options.map(option => option.text);
};

describe('rolls', () => {
    it('are ordered', async () => {
        render(
            <Provider store={store}>
                <RollGroup group={0} abilities={mockAbilities} />
            </Provider>
        );

        await clickAllRollButtons();

        for (const { rolls } of store.getState().rollGroups[0]) {
            for (let i = 0; i < (rolls?.length as number) - 1; ++i) {
                expect((rolls as number[])[i]).toBeGreaterThanOrEqual(
                    (rolls as number[])[i + 1]
                );
            }
        }
    });

    it('are between 1 and 6', async () => {
        render(
            <Provider store={store}>
                <RollGroup group={0} abilities={mockAbilities} />
            </Provider>
        );

        await clickAllRollButtons();

        for (const roll of store
            .getState()
            .rollGroups[0].reduce(
                (prev: number[], { rolls }) => prev.concat(rolls as number[]),
                [] as number[]
            )) {
            expect(roll).toBeGreaterThanOrEqual(1);
            expect(roll).toBeLessThanOrEqual(6);
        }
    });
});

it('removes selected options from options of other dropdowns', async () => {
    render(
        <Provider store={store}>
            <RollGroup group={0} abilities={mockAbilities} />
        </Provider>
    );

    await clickAllRollButtons();

    const dropdowns = screen.getAllByLabelText(/Select ability/i);
    const selected = [];

    await userEvent.selectOptions(dropdowns.at(0) as HTMLElement, 'CON');
    selected.push('CON');

    const secondDropdown = dropdowns.at(1) as HTMLSelectElement;
    for (const s of selected) {
        expect(getSelectOptions(secondDropdown)).not.toContain(s);
    }
    await userEvent.selectOptions(secondDropdown, 'STR');
    selected.push('STR');

    const thirdDropdown = dropdowns.at(2) as HTMLSelectElement;
    for (const s of selected) {
        expect(getSelectOptions(thirdDropdown)).not.toContain(s);
    }
    await userEvent.selectOptions(thirdDropdown, 'DEX');
    selected.push('DEX');

    const fourthDropdown = dropdowns.at(3) as HTMLSelectElement;
    for (const s of selected) {
        expect(getSelectOptions(fourthDropdown)).not.toContain(s);
    }
    await userEvent.selectOptions(fourthDropdown, 'INT');
    selected.push('INT');

    const fifthDropDown = dropdowns.at(4) as HTMLSelectElement;
    for (const s of selected) {
        expect(getSelectOptions(fifthDropDown)).not.toContain(s);
    }
    await userEvent.selectOptions(fifthDropDown, 'CHA');
    selected.push('CHA');

    const sixthDropdown = dropdowns.at(5) as HTMLSelectElement;
    for (const s of selected) {
        expect(getSelectOptions(sixthDropdown)).not.toContain(s);
    }
});

What you did:

I was trying to run the three tests listed.

What happened:

The second 2 tests failed with the following message:

TestingLibraryElementError: Unable to find an element with the text: /Roll/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

The html included with the error is the state I expect the component to be in after clickAllRollButtons is called. When I run these tests individually with it.only, they all pass. They only fail when I run the whole suite.

Reproduction:

I made a Codesandbox here.

Problem description:

It seems that the state of the screen is preserved between tests. This is a problem because tests shouldn't affect other tests. Manually adding an afterEach that calls cleanup didn't help either.

Suggested solution:

Unfortunately I don't have any suggested solutions.

SleeplessOne1917 commented 2 years ago

It turns out it was something with my redux store. Never mind.