Skip to content

chore: Fixed the intermittent failing browser tests#3410

Merged
chrisolsen merged 1 commit intodevfrom
dustin/fix-tests
Feb 17, 2026
Merged

chore: Fixed the intermittent failing browser tests#3410
chrisolsen merged 1 commit intodevfrom
dustin/fix-tests

Conversation

@ArakTaiRoth
Copy link
Copy Markdown
Collaborator

Before (the change)

We had multiple intermittent failures in various browser tests.

Components affected:

  • Date Picker
  • Menu Button
  • Radio
  • Text Area
  • Temporary Notification

After (the change)

Hopefully no more intermittent failures. Though I did notice some failures in datagrid.browser.spec.tsx, but I never modified that file as it's quite different from the rest of our browser tests.

Make sure that you've checked the boxes below before you submit the PR

  • I have read and followed the setup steps
  • I have created necessary unit tests
  • I have tested the functionality in both React and Angular.

Steps needed to test

Explanation of changes made:

  • datepicker.browser.spec.tsx

    • Modified renders with props to use an exact date as a value instead of basing it off today's date (which cares about timezone)
    • Modified waitFor under browser event to detect the element only, and then click it outside of the waitFor, this should avoid a race condition
    • Removed checking the month dropdown in the disabled test, I couldn't get that to work reliable, if you have a better way let me know. Also changed it to check the actual elements, this seemed to work better.
  • textarea.browser.spec.tsx

    • Modified a couple waitFor statements to only check if an object exits, clicking the object is done outside. Same reason as above
  • radio.browser.spec.tsx

    • Modified it to always re-get the elements involved in the enable/disable test. This should help due to React re-rendering wonkiness
  • menu-button.browser.spec.tsx

    • Same waitFor changes as above made in this file as well
  • temporary-notification-ctrl.browser.spec.tsx

    • Same waitFor changes as above

Also updated the vitest package to 4.0.13 (actually 4.0.18 I think), so it's the same as the other vitest packages (@vitest/browser for example). We were getting version mismatch errors, and that could potentially cause other issues with tests.

Comment on lines +407 to +411
const datePickerDay = result.getByTestId("input-day").element() as HTMLInputElement;
const datePickerYear = result.getByTestId("input-year").element() as HTMLInputElement;

expect(datePickerDay.disabled).toBe(true);
expect(datePickerYear.disabled).toBe(true);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How to move the locators out of the waitFor and use the locators to perform the test on. The month test is also included.

    const result = render(<Component />);

    const datePickerDayLoc = result.getByTestId("input-day");
    const datePickerMonthLoc = result.getByTestId("input-month").getByTestId("input");
    const datePickerYearLoc = result.getByTestId("input-year");

    await vi.waitFor(() => {
      expect(datePickerDayLoc).toBeDisabled();
      expect(datePickerMonthLoc).toBeDisabled();
      expect(datePickerYearLoc).toBeDisabled();
    });


Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chrisolsen The actual fix for this it seems was for the month input, getting "input-month", and then getting "input" based off of that.


const menuAction = result.getByTestId(`menu-action-${i}`);
expect(menuAction).toBeDefined();
const menuAction = result.getByTestId(`menu-action-${i}`);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This locator can also be moved out of the loop with

    const result = render(<Component />);
    const menuButton = result.getByTestId("menu-button");
    const menuActions = result.getByTestId(/menu-action-*/);

    for (let i = 1; i <= 3; i++) {
      await menuButton.click();
      await menuActions.nth(i-1).click();

      await vi.waitFor(() => {
        expect(onAction).toHaveBeenNthCalledWith(i, {
          action: `action${i}`,
        });
        expect(onAction).toHaveBeenCalledTimes(i);
      });
    }

    expect(onAction).toHaveBeenCalledTimes(3);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't know about the nth() function, thanks.

await menuAction.click();
await menuAction.click();

// Verify the correct action was triggered
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason the comments were removed? They do make things easier to follow

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed a lot of things, just assumed this wasn't necessary anymore either. I've re-added this specific comment.

expect(actionIcon).toBeDefined();
await vi.waitFor(() => {
expect(actionIcon.elements().length).toBe(1);
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't needed, as the element() method in the next block will confirm that
the actionIcon exists.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair. I was just trying to keep it doing as many of the same things as it was previously doing as I could.

const firstAction = result.getByTestId("first-action");
const secondAction = result.getByTestId("second-action");

// Click first action
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments also make things much easier to understand why things are being done the way they are.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed firstAction.click() kind of implied "Click first action".

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comments were initially added for clarity and removing them makes things hard to follow, so I think they all need to be added back

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chrisolsen All comments have been restored, some remain gone though because their functionality is now gone

@@ -40,62 +40,51 @@ describe("Radio", () => {
};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After looking a little more into some of the Locator methods I think we could improve this even more.

  it("should enable and disable radio group programmatically", async () => {
    const Component = () => {
      const [isDisabled, setIsDisabled] = useState(true);
      const [selectedValue, setSelectedValue] = useState("");

      return (
        <div>
          <GoabRadioGroup
            name="fruits"
            disabled={isDisabled}
            value={selectedValue}
            onChange={(e) => {
              setSelectedValue(e.value);
            }}
          >
            <GoabRadioItem name="fruits" value="apple" label="Apple" />
            <GoabRadioItem name="fruits" value="banana" label="Banana" />
          </GoabRadioGroup>

          <GoabButton
            testId="toggle-button"
            onClick={() => {
              setIsDisabled((value) => !value);
            }}
          >
            {isDisabled ? "Enable" : "Disable"}
          </GoabButton>

          <div data-testid="selected-value">{selectedValue}</div>
        </div>
      );
    };

    const result = render(<Component />);
    const radioItemsLoc = result.getByTestId(/^radio-option-.*/);
    const selectedValue = result.getByTestId("selected-value");

    // check that the radio items are all disabled
    await vi.waitFor(() => {
      const els = radioItemsLoc.all();
      expect(els.length).toBe(2);
      for (const item of els) {
        expect(item).toBeDisabled();
      }
    });

    // validate the selected value is blank
    await vi.waitFor(() => {
      expect(selectedValue.element().textContent).toBe("");
    });

    // enable the radio group
    const toggleButton = result.getByTestId("toggle-button");
    await toggleButton.click();

    // validate they are now enabled
    await vi.waitFor(() => {
      for (const item of radioItemsLoc.all()) {
        expect(item).toBeEnabled();
      }
    });

    // click the first radio item
    const appleRadioItem = radioItemsLoc.nth(0);
    appleRadioItem.element().click();

    // indicator of the selected value is changed
    await vi.waitFor(() => {
      expect(selectedValue.element().textContent).toBe("apple");
    });

    // revert to disabled state
    await toggleButton.click();

    // verify they are disabled
    await vi.waitFor(() => {
      for (const item of radioItemsLoc.all()) {
        expect(item).toBeDisabled();
      }
    });

    // click on a disabled item
    const bananaRadioItem = radioItemsLoc.nth(1);
    bananaRadioItem.element().click();

    // old value is retained
    await vi.waitFor(() => {
      expect(selectedValue.element().textContent).toBe("apple");
    });

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put this in place. I made a small change to your code, because the click() used on the radio items near the end didn't work in your version.


await userEvent.type(textareaEL, "s");
await vi.waitFor(() => {
expect(textarea.elements().length).toBe(1);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be shortened to textarea.length


const textareaEL = textarea.element() as HTMLTextAreaElement;
textareaEL.focus();
await userEvent.type(textareaEL, "s");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to focus if typing, so could be shortened to

    await userEvent.type(textarea.element(), "s");

Comment on lines +96 to +97
const inputEL = input.element() as HTMLInputElement;
inputEL.focus();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be shortened to input.element().focus()

Comment on lines 93 to 95
await vi.waitFor(() => {
const inputEL = input.element() as HTMLInputElement;
expect(inputEL).toBeTruthy();
inputEL.focus();
expect(input.elements().length).toBe(1);
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are focusing on the first element below, this isn't really necessary. Since it is a selector with a testid, it should only be 0 or 1. But if you want to keep it, it can be shortened to expect(input.length).toBe(1);

Copy link
Copy Markdown
Collaborator

@vanessatran-ddi vanessatran-ddi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested on my local, it passed all the test after I clean nx and dist and re-build.

@chrisolsen chrisolsen merged commit ad5c0bc into dev Feb 17, 2026
4 checks passed
@chrisolsen chrisolsen deleted the dustin/fix-tests branch February 17, 2026 20:26
@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.41.0-dev.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 4.11.0-dev.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 6.11.0-dev.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.11.0-dev.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.41.0-next.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.11.0-next.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 6.11.0-next.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 4.11.0-next.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.41.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen chrisolsen added the released Released into production. label Feb 27, 2026
@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 1.11.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 6.11.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 4.11.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.0.0-next.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.0.0-next.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 7.0.0-next.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 5.0.0-next.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 7.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 5.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants