Skip to content

[UEPR-445] Topbar unit and integration tests#438

Open
kbangelov wants to merge 10 commits intoscratchfoundation:release/UEPR-297-accessibility-improvementsfrom
kbangelov:task/uepr-445-topbar-tests
Open

[UEPR-445] Topbar unit and integration tests#438
kbangelov wants to merge 10 commits intoscratchfoundation:release/UEPR-297-accessibility-improvementsfrom
kbangelov:task/uepr-445-topbar-tests

Conversation

@kbangelov
Copy link
Contributor

@kbangelov kbangelov commented Feb 9, 2026

Resolves

Part of https://scratchfoundation.atlassian.net/browse/UEPR-445

Proposed Changes

Unit and integration tests to ensure safety.

Reason for Changes

Part of accessibility initiative for Scratch.

Test coverage

13 Unit tests for the hook and 17 Integration tests for the Settings, File and Edit menus

@kbangelov kbangelov requested a review from a team as a code owner February 9, 2026 13:03
@kbangelov kbangelov marked this pull request as draft February 9, 2026 13:06
@kbangelov kbangelov changed the base branch from develop to release/accessibility-improvements February 10, 2026 10:56
@kbangelov kbangelov force-pushed the release/accessibility-improvements branch 2 times, most recently from e37597f to f2902ce Compare February 13, 2026 09:00
@scratchfoundation scratchfoundation deleted a comment from github-actions bot Feb 13, 2026
@kbangelov kbangelov changed the base branch from release/accessibility-improvements to release/UEPR-297-accessibility-improvements February 13, 2026 10:09
@kbangelov kbangelov force-pushed the task/uepr-445-topbar-tests branch from 98bf7f1 to 10cec89 Compare February 13, 2026 10:40
@kbangelov kbangelov marked this pull request as ready for review February 13, 2026 10:41
@kbangelov kbangelov force-pushed the task/uepr-445-topbar-tests branch from 10cec89 to 814f74d Compare February 13, 2026 10:52
@kbangelov kbangelov requested a review from Copilot February 13, 2026 10:55
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds comprehensive test coverage for keyboard navigation in the Scratch GUI menu bar, supporting the accessibility initiative (UEPR-445). It introduces 13 unit tests for a menu navigation hook and 17 integration tests covering File, Edit, and Settings menus.

Changes:

  • Added unit tests for the useMenuNavigation hook covering keyboard interactions, RTL support, and focus management
  • Added integration tests for menu bar keyboard navigation including submenu interactions and RTL language switching
  • Added @reduxjs/toolkit dependency for test infrastructure

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
packages/scratch-gui/test/unit/hooks/use-menu-navigation.test.jsx Unit tests for the menu navigation hook covering keyboard shortcuts, focus management, and RTL behavior
packages/scratch-gui/test/integration/menu-bar-keyboard-nav.test.js Integration tests for keyboard navigation across File, Edit, and Settings menus including submenu interactions
packages/scratch-gui/package.json Adds @reduxjs/toolkit dependency for test store configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +23
store = configureStore({
reducer: {
locales: (state = {isRtl: false}) => state
}
});
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

This test file uses configureStore from @reduxjs/toolkit to create a Redux store, which is inconsistent with the rest of the codebase. All other unit tests in the project use redux-mock-store (e.g., test/unit/components/menu-bar.test.jsx, test/unit/components/error-boundary-hoc.test.jsx). Consider using redux-mock-store for consistency with established testing patterns.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a a deprecated method and this new one is the official recommendation. Yes, it's inconsistent and it seems to work the other way too, but I don't think it's a big deal. Perhaps I could change the others too?

@kbangelov kbangelov force-pushed the task/uepr-445-topbar-tests branch 2 times, most recently from 1b2b384 to a7b81d7 Compare February 13, 2026 12:00
@kbangelov kbangelov force-pushed the task/uepr-445-topbar-tests branch from a7b81d7 to b0af01a Compare February 13, 2026 12:24
});

afterAll(async () => {
await driver.executeScript('document.activeElement.blur()');
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need to blur after all tests have completed?

Comment on lines +54 to +60
test('Tab focuses file menu on 3 clicks', async () => {
// Pressing tab 3 times should focus the file menu button
await clickKeys([Key.TAB, Key.TAB, Key.TAB]);

const activeElement = await driver.switchTo().activeElement();
expect(await activeElement.getAttribute('aria-label')).toBe('File menu');
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, this is a bit specific. I wonder if it would make more sense to check the "order of navigation", instead of the File Menu in particular. E.g. first we focus the logo, then Settings, then File menu, etc.? Or something like "Pressing Tab navigates through the menu items"?

Comment on lines +42 to +52
const clickKey = async key => {
await driver.actions().sendKeys(key)
.perform();
await driver.sleep(SLEEP_TIME);
};

const clickKeys = async keys => {
for (const key of keys) {
await clickKey(key);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we could add these to the SeleniumHelper? If we want to write other tests for keyboard navigation, it'd be helpful to reuse them. It'd also be nice to add some error handling in case something goes wrong.

const clickKey = async key => {
await driver.actions().sendKeys(key)
.perform();
await driver.sleep(SLEEP_TIME);
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to sleep after each key press? Or is there a particular issue you were avoiding that way? I wonder if we could avoid "sleeping" unless we definitely need to, to not increase the tests run time too much?

Comment on lines +67 to +69
// Explicit keyboard focus
await driver.executeScript('arguments[0].focus()', fileMenuButton);
await clickKey(Key.ENTER);
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we could achieve the same by using fileMenuButton.sendKeys(Key.ENTER), instead of manually focusing and then sending the key to the active element. Although for more complex test cases we'd still need to switch to the currently active element, and use sendKeys on it (e.g. for ArrowUp/Down navigation).

test('Space opens the Language submenu', async () => {
const settingsMenuButton = await findByXpath(SETTINGS_MENU_XPATH);
await driver.executeScript('arguments[0].focus()', settingsMenuButton);
await clickKeys([Key.ENTER, Key.SPACE]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should test pressing Space twice? Because we've already tested the Enter interaction?

Comment on lines +235 to +251
test('Tab closes File menu', async () => {
const fileMenuButton = await findByXpath(FILE_MENU_XPATH);
await driver.executeScript('arguments[0].focus()', fileMenuButton);
await clickKeys([Key.ENTER, Key.TAB]);

const activeElement = await driver.switchTo().activeElement();
expect(await activeElement.getAttribute('aria-label')).toBe('Edit menu');

await loadUri(uri);

const fileMenuButton2 = await findByXpath(FILE_MENU_XPATH);
await driver.executeScript('arguments[0].focus()', fileMenuButton2);
await clickKeys([Key.ENTER, Key.ARROW_DOWN, Key.TAB]);

const activeElement2 = await driver.switchTo().activeElement();
expect(await activeElement2.getAttribute('aria-label')).toBe('Edit menu');
});
Copy link
Contributor

Choose a reason for hiding this comment

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

The fact that we need to reload in the middle of the test makes me think that it'd make more sense to either divide these into two different test cases, e.g. Tab closes File Menu and Tab closes Tab menu after navigation, or test only the second one, since it also test whether "Tab closes the menu".

.sendKeys(Key.TAB)
.keyUp(Key.SHIFT)
.perform();
await driver.sleep(SLEEP_TIME);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need the sleep here?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments