CAUTION! Before considering use of the ARIA menubar pattern for site navigation, it is important to understand:
+
+
The menubar pattern requires complex functionality that is unnecessary for typical site navigation that is styled to look like a menubar with expandable sections or fly outs.
The following implementation of the
design pattern for menubar
- demonstrates a menubar that provides site navigation menus.
- Each item in the menubar represents a section of a web site for a mythical university and opens a submenu containing menu items that link to pages within that section.
+ demonstrates how a menubar can provide navigation menus.
+ The parent menu items in the menubar represent a section of a mythical university web site and open a submenu containing menu items that link to pages within that section.
+ The navigation system it illustrates is comparable to the navigation illustrated in the Example of Disclosure for Navigation Menus.
+ As noted above, the disclosure pattern is better suited for most web sites because few sites need the additional keyboard functionality required to support the ARIA menubar and menu roles.
Similar examples include:
@@ -47,153 +59,192 @@
Example
-
+
+
+
+
Mythical University
+
Using a Menubar for navigation links
+
+
+
+
+
+
Mythical University
+
+
+
+
+
+
+
+
@@ -201,11 +252,41 @@
Example
Accessibility Features
+
Menu items that trigger navigation move focus to the target page title:
+
+
+ An important aspect of designing a navigation menu experience is where keyboard focus moves when an item that triggers navigation is activated and the menu closes.
+ If activating a menubar item changes content on the page without triggering a browser page load, i.e., works like typical single-page apps, the focus position after the content load significantly effects efficiency for keyboard and assistive technology users.
+
+
+ This example behaves like a single page app and activating a menu item that loads new content moves focus to the beginning of the new content, which is a level one heading with content that matches the name of the activated menu item.
+ Focusing on the heading informs screen reader users that navigation is complete and confirms the destination.
+
+
+ To view other pages, keyboard users need to navigate back to the menubar .
+ To optimize keyboard efficiency, this example locates the menubar immediately before the content display area in the Tab sequence.
+
+
To help communicate that the arrow keys are available for directional navigation within the menubar and its submenus, a border is added to the menubar container when focus is within the menubar.
+
To help users locate the menu item that loads the currently displayed page, visual indicators of the path to that menu item are added to the menu items in the path:
+
+
The parent menubar item in the path has an extra thick (4 pixel) bottom border.
+
If the current page menu item is nested within a deeper submenu, the parent submenu item in the path has an extra thick (4 pixel) left side border.
+
Finally, the menu item that loads the currently displayed page is styled with an extra thick border on the left side.
+
+
To support operating system high contrast settings:
-
Focus is highlighted by adding and removing a border around the menu item with focus.
-
The arrow icons indicating the current state of expandable menus use in-line SVG images with the CSS currentColor value for the fill and stroke properties of the polygon element.
+
+ Because transparent borders are visible on some systems with operating system high contrast settings enabled, transparency cannot be used to create a visual difference between the element that is focused an other elements.
+ Instead of using transparency, the focused element has a thicker border and less padding.
+ When an element receives focus, its border changes from 0 to 2 pixels and padding is reduced by 2 pixels.
+ When an element loses focus, its border changes from 2 to 0 pixels and padding is increased by 2 pixels.
+
+
+ To ensure the arrow icons used to indicate the expanded or collapsed state have sufficient contrast with the background when high contrast settings invert colors, the CSS currentColor value for the fill and stroke properties of the SVG polygon element is used to synchronize the color with text content.
+ If specific colors are used to specify the fill and stroke properties, these colors will remain the same in high contrast mode, which could lead to insufficient contrast between the icon and the background or even make the icon invisible if its color matches the high contrast mode background.
+
Since the menubar presents a site navigation system, it is wrapped in a navigation region implemented with a nav element that has an aria-label that matches the label on the menubar.
@@ -227,7 +308,12 @@
Menubar
Space Enter
-
Opens submenu and moves focus to first item in the submenu.
+
+
+
If the item is a parent menu item, opens submenu and moves focus to first item in the submenu.
+
Otherwise, activates the menu item, which loads new content and places focus on the heading that titles the content.
+
+
@@ -305,7 +391,7 @@
Submenu
If the item is a parent menu item, opens submenu and moves focus to first item in the submenu.
-
Otherwise, activates menu item, which navigates to a dummy page. NOTE: use browser go-back function to return to this page.
+
Otherwise, activates the menu item, which loads new content and places focus on the heading that titles the content.
@@ -404,6 +490,85 @@
Submenu
Role, Property, State, and Tabindex Attributes
+
+
Landmarks
+
+
+
+
Role
+
Attribute
+
Element
+
Usage
+
+
+
+
+
banner
+
+
header
+
+
+
Identifies the top section of content that is common across pages in a web site.
+
+ NOTE: The banner role declaration is only necessary because it is an example that is nested inside the content of this page.
+ In an actual website, the header element would be a top level element, i.e., it would have the body as its scope.
+ If the scope of the header element were body, browsers would automatically treat the header as an ARIA banner, so the header would not need role="banner".
+
+
+
+
+
navigation
+
+
nav
+
+ Identifies the region containing the mythical university navigation.
+
+
+
+
+
aria-label="Mythical University"
+
nav
+
+ Provides an accessible name for the navigation landmark that describes the purpose of the navigation.
+
+
+
+
region
+
+
section
+
+
+
Identifies the main content region of the page depicted in the example.
+
In an actual web page, this region would be identified by a main landmark, but since this page already has a main landmark, role region is used to avoid having two main landmarks on the page.
+
+
+
+
+
+
aria-labelledby="idref"
+
section
+
+ Provides the region with an accessible name from the h1 element.
+
+
+
+
contentinfo
+
+
footer
+
+
+
Identifies the bottom section of content common across pages in a web site.
+
+ NOTE: The contentinfo role declaration is only necessary because the footer is an example that is nested inside the content of this page.
+ In an actual website, the footer element would be a top level element, i.e., it would have the body as its scope.
+ If the scope of the footer element were body, browsers would automatically treat the footer as a ARIA contentinfo landmark, so the footer would not need role="contentinfo".
+
+
+
+
+
+
+
Menubar
@@ -433,8 +598,7 @@
Menubar
- aria-label="string"
-
+ aria-label="string"
ul
@@ -464,10 +628,10 @@
Menubar
-
+
- tabindex="-1"
+ tabindex="-1"
a
@@ -476,10 +640,10 @@
Menubar
Makes the a element keyboard focusable, but not part of the tab sequence.
-
+
- tabindex="0"
+ tabindex="0"
a
@@ -487,43 +651,59 @@
Menubar
Includes the element in the Tab sequence.
-
Only one menubar item has tabindex="0".
-
On page load, the first menubar item has tabindex="0".
+
Only one menubar item has tabindex="0".
+
On page load, the first menubar item has tabindex="0".
Identifies the link URL has the same URL as the web page.
+
If the link URL is not the same, the attribute is removed.
+
Menu items in the menubar in the path to the current page menu item are given a title attribute with the text content of "Contains current page link" to indicate to screen reader users the menu item contains the current page link.
+
+
+
+
- aria-haspopup="true"
+ aria-haspopup="true"
a
Indicates the menuitem has a submenu.
-
+
- aria-expanded="true"
+ aria-expanded="true"
a
Indicates the submenu is open.
-
+
- aria-expanded="false"
+ aria-expanded="false"
a
Indicates the submenu is closed.
-
+
none
@@ -564,8 +744,7 @@
Submenu
- aria-label="string"
-
+ aria-label="string"
ul
@@ -580,7 +759,7 @@
Submenu
-
+
menuitem
@@ -595,47 +774,47 @@
Submenu
-
+
- tabindex="-1"
+ tabindex="-1"
a
Keeps the a element focusable but removes it from the Tab sequence.
-
+
- aria-haspopup="true"
+ aria-haspopup="true"
a
Indicates the menu item has a submenu.
-
+
- aria-expanded="true"
+ aria-expanded="true"
a
Indicates the submenu is open.
-
+
- aria-expanded="false"
+ aria-expanded="false"
a
Indicates the submenu is closed.
-
+
none
diff --git a/test/tests/menubar_menubar-navigation.js b/test/tests/menubar_menubar-navigation.js
index 68565d40f7..0df4348d8b 100644
--- a/test/tests/menubar_menubar-navigation.js
+++ b/test/tests/menubar_menubar-navigation.js
@@ -1,88 +1,130 @@
const { ariaTest } = require('..');
-const { By, Key } = require('selenium-webdriver');
+const { Key } = require('selenium-webdriver');
+const assert = require('assert');
const assertAttributeValues = require('../util/assertAttributeValues');
const assertAriaLabelExists = require('../util/assertAriaLabelExists');
+const assertAriaLabelledby = require('../util/assertAriaLabelledby');
const assertAriaRoles = require('../util/assertAriaRoles');
const assertRovingTabindex = require('../util/assertRovingTabindex');
const exampleFile = 'menubar/menubar-navigation.html';
const ex = {
+ // landmark selectors
+ bannerSelector: '#ex1 header',
+ navigationSelector: '#ex1 nav',
+ regionSelector: '#ex1 section',
+ contentinfoSelector: '#ex1 footer',
+
+ // Title selector
+ titleSelector: '#id-page-title',
+
// menubar selector
menubarSelector: '#ex1 [role="menubar"]',
+ numberOfMenuitems: 31,
// menu selectors
anyMenuSelector: '#ex1 [role="menu"]',
- menuSelector: '#ex1 [role="menubar"]>li>[role="menu"]',
- // menuitem selectors
+ // Menubar menuitem selectors
menubarMenuitemSelector: '#ex1 [role="menubar"]>li>[role="menuitem"]',
+ menubarMenuitemWithPopupSelector: '#ex1 [role="menubar"]>li>[aria-haspopup]',
+
+ // Menuitem selectors
+ anyMenuitemSelector: '#ex1 [role="menubar"] [role="menuitem"]',
+ anyExpandableMenuitemSelector:
+ '#ex1 [role="menubar"] [role="menuitem"][aria-expanded]',
+
+ // Menu menuitem selectors
anyMenuMenuitemSelector: '#ex1 [role="menu"]>li>[role="menuitem"]',
- menuMenuitemSelectors: [
- '#ex1 [role="menubar"]>li:nth-of-type(1)>[role="menu"]>li>[role="menuitem"]',
- '#ex1 [role="menubar"]>li:nth-of-type(2)>[role="menu"]>li>[role="menuitem"]',
- '#ex1 [role="menubar"]>li:nth-of-type(3)>[role="menu"]>li>[role="menuitem"]',
- ],
- groupSelector: '#ex1 [role="group"]',
- numMenus: 3,
- numSubmenus: 3,
+
+ // Submenu item selectors
+ submenuMenuitemsWithHasPopup:
+ '#ex1 [role="menu"] [role="menuitem"][aria-haspopup]',
+ submenuMenuitemsWithExpanded:
+ '#ex1 [role="menu"] [role="menuitem"][aria-expanded]',
+
+ // Selectors for testing expandable menus in submenus
numTotalMenus: 6,
- submenuLocations: [
- // [, ]
- [0, 2],
- [0, 3],
- [1, 1],
+ menuLocations: [1, 2, 3],
+ // Numbers are the index of the menuitems in the entire menubar
+ // including submenus
+ keyboardTestMenuitems: [
+ [1, -1, [2, 3, 4, 8]],
+ [1, 4, [5, 6, 7]],
+ [1, 8, [9, 10, 11]],
+ [12, -1, [13, 14, 18, 19, 20, 21]],
+ [12, 14, [15, 16, 17]],
+ [22, -1, [23, 24, 25, 26, 27, 28, 29, 30]],
],
- numMenuMenuitems: [4, 6, 8],
};
-// Returns specified submenu
-const getSubmenuSelector = function (menuIndex, menuitemIndex) {
- return (
- '#ex1 [role="menubar"]>li:nth-of-type(' +
- (menuIndex + 1) +
- ')>[role="menu"]>li:nth-of-type(' +
- (menuitemIndex + 1) +
- ')>[role="menu"]'
- );
-};
+// await new Promise(resolve => setTimeout(resolve, 3000));
-// Returns the menuitems of a specified submenu
-const getSubmenuMenuitemSelector = function (menuIndex, menuitemIndex) {
- return (
- '#ex1 [role="menubar"]>li:nth-of-type(' +
- (menuIndex + 1) +
- ')>[role="menu"]>li:nth-of-type(' +
- (menuitemIndex + 1) +
- ')>[role="menu"] [role="menuitem"]'
- );
+const openMenus = async function (t, menuIndex, menuitemIndex) {
+ // Send ENTER to open menu
+ if (menuIndex >= 0) {
+ if (await isExpandable(t, ex.anyMenuitemSelector, menuIndex)) {
+ const menubarItems = await t.context.queryElements(
+ t,
+ ex.anyMenuitemSelector
+ );
+ await menubarItems[menuIndex].sendKeys(Key.ENTER);
+ }
+ }
+
+ // Get the menuitems for that menu and send ENTER to open the submenu
+ if (menuitemIndex >= 0) {
+ if (await isExpandable(t, ex.anyMenuitemSelector, menuitemIndex)) {
+ const menuitems = await t.context.queryElements(
+ t,
+ ex.anyMenuitemSelector
+ );
+ await menuitems[menuitemIndex].sendKeys(Key.ENTER);
+ }
+ }
+ return;
};
-const openSubmenu = async function (t, menuIndex, menuitemIndex) {
- // Send ARROW_DOWN to open menu
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
+const compareText = async function (t, selector, text) {
+ return t.context.session.executeScript(
+ function () {
+ const [selector, text] = arguments;
+ const item = document.querySelector(selector);
+ return (
+ item.textContent.trim().toLowerCase() === text.trim().toLowerCase()
+ );
+ },
+ selector,
+ text
);
- await menubarItems[menuIndex].sendKeys(Key.ARROW_DOWN);
+};
- // Get the menuitems for that menu and send ARROW_RIGHT to open the submenu
- const menuitems = await t.context.queryElements(
- t,
- ex.menuMenuitemSelectors[menuIndex]
+const isExpandable = async function (t, selector, index) {
+ return t.context.session.executeScript(
+ function () {
+ const [selector, index] = arguments;
+ const items = document.querySelectorAll(selector);
+ return items[index].hasAttribute('aria-expanded');
+ },
+ selector,
+ index
);
- await menuitems[menuitemIndex].sendKeys(Key.ARROW_RIGHT);
- return;
};
-const waitForUrlChange = async function (t) {
- return t.context.session
- .wait(() => {
- return t.context.session.getCurrentUrl().then((url) => {
- return url != t.context.url;
- });
- }, t.context.waitTime)
- .catch(() => {});
+const checkOpen = async function (t, selector, index) {
+ return t.context.session.executeScript(
+ function () {
+ const [selector, index] = arguments;
+ const items = document.querySelectorAll(selector);
+ const test1 = items[index].getAttribute('aria-expanded') === 'true';
+ const menu = items[index].nextElementSibling;
+ const test2 = menu.style.display != 'none';
+ return test1 && test2;
+ },
+ selector,
+ index
+ );
};
const exampleInitialized = async function (t) {
@@ -110,362 +152,311 @@ const checkFocus = async function (t, selector, index) {
);
};
-const doesMenuitemHaveSubmenu = function (menuIndex, menuitemIndex) {
- for (let submenuLocation of ex.submenuLocations) {
- if (
- submenuLocation[0] === menuIndex &&
- submenuLocation[1] === menuitemIndex
- ) {
- return true;
- }
- }
- return false;
-};
-
-// Attributes
+// Tests for landmark roles in example
ariaTest(
- 'Test for role="menubar" on ul',
+ 'role="banner" on header element',
exampleFile,
- 'menubar-role',
+ 'banner-role',
async (t) => {
- await assertAriaRoles(t, 'ex1', 'menubar', 1, 'ul');
+ const banners = await t.context.queryElements(t, ex.bannerSelector);
+
+ t.is(
+ banners.length,
+ 1,
+ 'One "role=banner" element should be found by selector: ' +
+ ex.bannerSelector
+ );
+
+ t.is(
+ await banners[0].getTagName(),
+ 'header',
+ 'role="banner" should be found on a "header"'
+ );
}
);
ariaTest(
- 'Test aria-label on menubar',
+ 'nav element identifies navigation landmark',
exampleFile,
- 'menubar-aria-label',
+ 'navigation-role',
async (t) => {
- await assertAriaLabelExists(t, ex.menubarSelector);
+ const navs = await t.context.queryElements(t, ex.navigationSelector);
+
+ t.is(
+ navs.length,
+ 1,
+ 'One nav element should be found by selector: ' + ex.navigationSelector
+ );
}
);
ariaTest(
- 'Test for role="menuitem" in menubar',
+ 'aria-label on nav element',
exampleFile,
- 'menuitem-role',
+ 'navigation-aria-label',
async (t) => {
- const menuitems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
-
- t.is(
- menuitems.length,
- ex.numMenus,
- '"role=menuitem" elements should be found by selector: ' +
- ex.menubarMenuitemSelector
- );
-
- for (let menuitem of menuitems) {
- t.truthy(
- await menuitem.getText(),
- '"role=menuitem" elements should all have accessible text content: ' +
- ex.menubarMenuitemSelector
- );
- }
+ await assertAriaLabelExists(t, ex.navigationSelector);
}
);
ariaTest(
- 'Test roving tabindex',
+ 'section element identifies region landmark',
exampleFile,
- 'menuitem-tabindex',
+ 'region-role',
async (t) => {
- // Wait for roving tabindex to be initialized by the javascript
- await exampleInitialized(t);
+ const regions = await t.context.queryElements(t, ex.regionSelector);
- await assertRovingTabindex(t, ex.menubarMenuitemSelector, Key.ARROW_RIGHT);
+ t.is(
+ regions.length,
+ 1,
+ 'One section element should be found by selector: ' + ex.regionSelector
+ );
}
);
ariaTest(
- 'Test aria-haspopup set to true on menuitems',
+ 'aria-labelledby on section element',
exampleFile,
- 'menuitem-aria-haspopup',
+ 'region-aria-labelledby',
async (t) => {
- await assertAttributeValues(
- t,
- ex.menubarMenuitemSelector,
- 'aria-haspopup',
- 'true'
- );
+ await assertAriaLabelledby(t, ex.regionSelector);
}
);
ariaTest(
- '"aria-expanded" attribute on menubar menuitem',
+ 'role="contentinfo" on footer element',
exampleFile,
- 'menuitem-aria-expanded',
+ 'contentinfo-role',
async (t) => {
- // Before interacting with page, make sure aria-expanded is set to false
- await assertAttributeValues(
+ const contentinfos = await t.context.queryElements(
t,
- ex.menubarMenuitemSelector,
- 'aria-expanded',
- 'false'
+ ex.contentinfoSelector
);
- // AND make sure no submenus are visible
- const submenus = await t.context.queryElements(t, ex.menuSelector);
- for (let submenu of submenus) {
- t.false(
- await submenu.isDisplayed(),
- 'No submenus (found by selector: ' +
- ex.menuSelector +
- ') should be displayed on load'
- );
- }
+ t.is(
+ contentinfos.length,
+ 1,
+ 'One "role=contentinfo" element should be found by selector: ' +
+ ex.contentinfoSelector
+ );
- const menuitems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
+ t.is(
+ await contentinfos[0].getTagName(),
+ 'footer',
+ 'role="contentinfo" should be found on a "footer"'
);
+ }
+);
- for (let menuIndex = 0; menuIndex < menuitems.length; menuIndex++) {
- // Send ARROW_DOWN to open submenu
- await menuitems[menuIndex].sendKeys(Key.ARROW_DOWN);
+// Attributes
- for (let item = 0; item < menuitems.length; item++) {
- // Test attribute "aria-expanded" is only set for the opened submenu
- const displayed = menuIndex === item ? true : false;
- t.is(
- await menuitems[item].getAttribute('aria-expanded'),
- displayed.toString(),
- 'focus is on element ' +
- menuIndex +
- ' of elements "' +
- ex.menubarMenuitemSelector +
- '", therefore "aria-expanded" on menuitem ' +
- item +
- ' should be ' +
- displayed
- );
+ariaTest(
+ 'Test for role="menubar" on ul',
+ exampleFile,
+ 'menubar-role',
+ async (t) => {
+ await assertAriaRoles(t, 'ex1', 'menubar', 1, 'ul');
+ }
+);
- // Test the submenu is indeed displayed
- t.is(
- await submenus[item].isDisplayed(),
- displayed,
- 'focus is on element ' +
- menuIndex +
- ' of elements "' +
- ex.menubarMenuitemSelector +
- '", therefore isDisplay of submenu ' +
- item +
- ' should return ' +
- displayed
- );
- }
+ariaTest(
+ 'Test for role="menuitem" on a elements',
+ exampleFile,
+ 'menuitem-role',
+ async (t) => {
+ await assertAriaRoles(t, 'ex1', 'menuitem', ex.numberOfMenuitems, 'a');
+ }
+);
- // Send the ESCAPE to close submenu
- await menuitems[menuIndex].sendKeys(Key.ESCAPE);
- }
+ariaTest(
+ 'Test aria-label on menubar',
+ exampleFile,
+ 'menubar-aria-label',
+ async (t) => {
+ await assertAriaLabelExists(t, ex.menubarSelector);
}
);
ariaTest(
- 'Test for role="none" on menubar li',
+ 'Test roving tabindex',
exampleFile,
- 'none-role',
+ 'menubar-menuitem-tabindex',
async (t) => {
- const liElements = await t.context.queryElements(
+ // Wait for roving tabindex to be initialized by the javascript
+ await exampleInitialized(t);
+ await assertRovingTabindex(t, ex.menubarMenuitemSelector, Key.ARROW_RIGHT);
+ }
+);
+
+ariaTest(
+ 'Test aria-haspopup set to true on menuitems with popup menus',
+ exampleFile,
+ 'menubar-menuitem-aria-haspopup',
+ async (t) => {
+ const menuitems = await t.context.queryElements(
t,
- ex.menubarSelector + '>li'
+ ex.menubarMenuitemSelector
);
-
- for (let liElement of liElements) {
- t.is(
- await liElement.getAttribute('role'),
- 'none',
- '"role=none" should be found on all list elements that are immediate descendants of: ' +
- ex.menubarSelector
+ for (let i = 0; i < ex.menuLocations.length; i++) {
+ let menuitem = menuitems[ex.menuLocations[i]];
+ assert.strictEqual(
+ await menuitem.getAttribute('aria-haspopup'),
+ 'true',
+ 'Attribute "aria-haspopup" with value "true" should be found on element with label "' +
+ menuitem.getText() +
+ '"'
);
}
+ t.pass();
}
);
-ariaTest('Test for role="menu" on ul', exampleFile, 'menu-role', async (t) => {
- await assertAriaRoles(t, 'ex1', 'menu', ex.numTotalMenus, 'ul');
-});
-
ariaTest(
- 'Test for aria-label on role="menu"',
+ 'Test aria-expanded on menubar menuitems set to false when popup is closed',
exampleFile,
- 'menu-aria-label',
+ 'menubar-menuitem-aria-expanded-false',
async (t) => {
- await assertAriaLabelExists(t, ex.anyMenuSelector);
+ const menuitems = await t.context.queryElements(
+ t,
+ ex.menubarMenuitemSelector
+ );
+ for (let i = 0; i < ex.menuLocations.length; i++) {
+ let menuitem = menuitems[ex.menuLocations[i]];
+ assert.strictEqual(
+ await menuitem.getAttribute('aria-expanded'),
+ 'false',
+ 'Attribute "aria-expanded" with value "false" should be found on element with label "' +
+ menuitem.getText() +
+ '"'
+ );
+ }
+ t.pass();
}
);
ariaTest(
- 'Test for submenu role="menuitem"s with accessible names',
+ 'Test aria-expanded on menubar menuitems set to true when popup is open',
exampleFile,
- 'sub-menuitem-role',
+ 'menubar-menuitem-aria-expanded-true',
async (t) => {
- const menuitems = await t.context.queryElements(
+ const menubarItems = await t.context.queryElements(
t,
- ex.anyMenuMenuitemSelector
- );
-
- t.truthy(
- menuitems.length,
- '"role=menuitem" elements should be found by selector: ' +
- ex.anyMenuMenuitemSelector
+ ex.menubarMenuitemWithPopupSelector
);
- // Test the accessible name of each menuitem
-
- for (let menuitem of menuitems) {
- // The menuitem is not visible, so we cannot use selenium's "getText" function
- const menuText = await t.context.session.executeScript(function () {
- const el = arguments[0];
- return el.innerHTML;
- }, menuitem);
+ for (let i = 0; i < menubarItems.length; i++) {
+ const menubarItem = menubarItems[i];
+ await menubarItem.sendKeys(Key.ARROW_DOWN);
- t.truthy(
- menuText,
- '"role=menuitem" elements should all have accessible text content: ' +
- ex.anyMenuMenuitemSelector
+ assert.strictEqual(
+ await menubarItem.getAttribute('aria-expanded'),
+ 'true',
+ 'aria-expanded should be "true" for "' + menubarItem.getText() + '"'
);
+ t.pass();
}
}
);
ariaTest(
- 'Test tabindex="-1" on submenu role="menuitem"s',
+ 'Test aria-haspopup set to true on sub menu menuitems with popups',
exampleFile,
- 'sub-menuitem-tabindex',
+ 'menu-menuitem-aria-haspopup',
async (t) => {
await assertAttributeValues(
t,
- ex.anyMenuMenuitemSelector,
- 'tabindex',
- '-1'
+ ex.submenuMenuitemsWithHasPopup,
+ 'aria-haspopup',
+ 'true'
);
}
);
ariaTest(
- 'Test aria-haspopup on menuitems with submenus',
+ '"aria-expanded" attribute on sub-menu menuitem',
exampleFile,
- 'sub-menuitem-aria-haspopup',
+ 'menu-menuitem-aria-expanded-false',
async (t) => {
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- const menuItems = await t.context.queryElements(
- t,
- ex.menuMenuitemSelectors[menuIndex]
- );
-
- for (
- let menuitemIndex = 0;
- menuitemIndex < menuItems.length;
- menuitemIndex++
- ) {
- const menuitemHasSubmenu = doesMenuitemHaveSubmenu(
- menuIndex,
- menuitemIndex
- );
+ // Before interacting with page, make sure aria-expanded is set to false
+ await assertAttributeValues(
+ t,
+ ex.submenuMenuitemsWithExpanded,
+ 'aria-expanded',
+ 'false'
+ );
- const ariaPopup = menuitemHasSubmenu ? 'true' : null;
- const hasAriaPopupMsg = menuitemHasSubmenu
- ? 'aria-haspopup set to "true".'
- : 'no aria-haspopup attribute.';
+ // AND make sure no submenus are visible
+ const submenus = await t.context.queryElements(
+ t,
+ ex.submenuMenuitemsWithExpanded
+ );
- t.is(
- await menuItems[menuitemIndex].getAttribute('aria-haspopup'),
- ariaPopup,
- 'menuitem at index ' +
- menuitemIndex +
- ' in menu at index ' +
- menuIndex +
- ' is expected ' +
- 'to have ' +
- hasAriaPopupMsg
- );
- }
+ for (let i = 0; i < submenus.length; i++) {
+ t.false(
+ await checkOpen(t, ex.submenuMenuitemsWithExpanded, i),
+ 'Submenu with index "' + i + ' should NOT be displayed on load'
+ );
}
}
);
ariaTest(
- 'Test aria-expanded on menuitems with submenus',
+ '"aria-expanded" attribute on sub-menu menuitem',
exampleFile,
- 'sub-menuitem-aria-expanded',
+ 'menu-menuitem-aria-expanded-true',
async (t) => {
- const menubarMenuitems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
-
- for (let submenuLocation of ex.submenuLocations) {
- const [menuIndex, menuitemIndex] = submenuLocation;
+ const testMenuitems = [
+ [1, 4],
+ [1, 8],
+ [12, 14],
+ ];
- // Send ARROW_DOWN to open menu
- await menubarMenuitems[menuIndex].sendKeys(Key.ARROW_DOWN);
+ for (let i = 0; i < testMenuitems.length; i++) {
+ const testMenuitem = testMenuitems[i];
+ const indexMenubarOpen = testMenuitem[0];
+ const indexSendArrowRight = testMenuitem[1];
- // Get the menuitems for that menu
- const menuitems = await t.context.queryElements(
- t,
- ex.menuMenuitemSelectors[menuIndex]
- );
+ await openMenus(t, indexMenubarOpen, -1);
- // Get the submenu associate with the menuitem we are testing
- const submenuSelector = getSubmenuSelector(menuIndex, menuitemIndex);
+ let menuitems = await t.context.queryElements(t, ex.anyMenuitemSelector);
+ await menuitems[indexSendArrowRight].sendKeys(Key.ARROW_RIGHT);
- t.is(
- await menuitems[menuitemIndex].getAttribute('aria-expanded'),
- 'false',
- 'menuitem at index ' +
- menuitemIndex +
- ' in menu at index ' +
- menuIndex +
- ' is expected ' +
- 'to have aria-expanded="false" after opening the menu that contains it'
- );
- t.false(
- await t.context.session
- .findElement(By.css(submenuSelector))
- .isDisplayed(),
- 'submenu attached to menuitem at index ' +
- menuitemIndex +
- ' in menu at index ' +
- menuIndex +
- ' is expected to not be displayed after opening the menu that contains the menuitem'
+ t.true(
+ await checkOpen(t, ex.anyMenuitemSelector, indexSendArrowRight),
+ 'Menuitem with index "' +
+ indexSendArrowRight +
+ '"" should be displayed on load'
);
+ }
+ }
+);
- // Send ARROW_RIGHT to the menuitem we are testing
- await menuitems[menuitemIndex].sendKeys(Key.ARROW_RIGHT);
+ariaTest(
+ 'Test for role="none" on menubar li',
+ exampleFile,
+ 'menubar-role-none',
+ async (t) => {
+ const liElements = await t.context.queryElements(
+ t,
+ ex.menubarSelector + '>li'
+ );
+ for (let liElement of liElements) {
t.is(
- await menuitems[menuitemIndex].getAttribute('aria-expanded'),
- 'true',
- 'menuitem at index ' +
- menuitemIndex +
- ' in menu at index ' +
- menuIndex +
- ' is expected ' +
- 'to have aria-expanded="true" after sending right arrow to it'
- );
- t.true(
- await t.context.session
- .findElement(By.css(submenuSelector))
- .isDisplayed(),
- 'submenu attached to menuitem at index ' +
- menuitemIndex +
- ' in menu at index ' +
- menuIndex +
- ' is expected to be displayed after sending left arrow to associated menuitem'
+ await liElement.getAttribute('role'),
+ 'none',
+ '"role=none" should be found on all list elements that are immediate descendants of: ' +
+ ex.menubarSelector
);
}
}
);
ariaTest(
- 'Test for role="none" on menu lis',
+ 'Test for role="none" on menu li',
exampleFile,
- 'sub-none-role',
+ 'menu-role-none',
async (t) => {
const liElements = await t.context.queryElements(
t,
@@ -473,7 +464,9 @@ ariaTest(
);
for (let liElement of liElements) {
- if ((await liElement.getAttribute('role')) !== 'separator') {
+ var isSeparator =
+ (await await liElement.getAttribute('role')) == 'separator';
+ if (!isSeparator) {
t.is(
await liElement.getAttribute('role'),
'none',
@@ -485,68 +478,115 @@ ariaTest(
}
);
-// KEYS
+ariaTest('Test for role="menu" on ul', exampleFile, 'menu-role', async (t) => {
+ await assertAriaRoles(t, 'ex1', 'menu', ex.numTotalMenus, 'ul');
+});
ariaTest(
- 'Key ENTER open submenu',
+ 'Test for aria-label on role="menu"',
exampleFile,
- 'menubar-space-or-enter',
+ 'menu-aria-label',
+ async (t) => {
+ await assertAriaLabelExists(t, ex.anyMenuSelector);
+ }
+);
+
+ariaTest(
+ 'Test for submenu role="menuitem"s with accessible names',
+ exampleFile,
+ 'menu-menuitem-role',
async (t) => {
const menuitems = await t.context.queryElements(
t,
- ex.menubarMenuitemSelector
+ ex.anyMenuMenuitemSelector
);
- const menus = await t.context.queryElements(t, ex.menuSelector);
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- // Send the ENTER key
- await menuitems[menuIndex].sendKeys(Key.ENTER);
- // Test that the submenu is displayed
- t.true(
- await menus[menuIndex].isDisplayed(),
- 'Sending key "ENTER" to menuitem ' +
- menuIndex +
- ' in menubar should display submenu'
- );
+ let count = 0;
- t.true(
- await checkFocus(t, ex.menuMenuitemSelectors[menuIndex], 0),
- 'Sending key "ENTER" to menuitem ' +
- menuIndex +
- ' in menubar should send focus to the first element in the submenu'
- );
+ for (let item of menuitems) {
+ const isSeparator =
+ (await await item.getAttribute('role')) === 'separator';
+ if (!isSeparator) {
+ count += 1;
+ }
}
+
+ t.truthy(
+ count,
+ '"role=menuitem" elements should be found by selector: ' +
+ ex.anyMenuMenuitemSelector
+ );
}
);
ariaTest(
- 'Key SPACE open submenu',
+ 'Test tabindex="-1" on submenu role="menuitem"s',
exampleFile,
- 'menubar-space-or-enter',
+ 'menu-menuitem-tabindex',
async (t) => {
- const menubarItems = await t.context.queryElements(
+ await assertAttributeValues(
t,
- ex.menubarMenuitemSelector
+ ex.anyMenuMenuitemSelector,
+ 'tabindex',
+ '-1'
);
- const menus = await t.context.queryElements(t, ex.menuSelector);
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- // Send the SPACE key
- await menubarItems[menuIndex].sendKeys(' ');
+ }
+);
- // Test that the submenu is displayed
- t.true(
- await menus[menuIndex].isDisplayed(),
- 'Sending key "SPACE" to menuitem ' +
- menuIndex +
- ' in menubar should display submenu'
- );
+// await new Promise(resolve => setTimeout(resolve, 3000));
+// KEYS
+ariaTest(
+ 'Key ENTER open submenu',
+ exampleFile,
+ 'menubar-space-or-enter',
+ async (t) => {
+ const keys = ['ENTER', 'SPACE'];
+ const keyCodes = { ENTER: Key.ENTER, SPACE: ' ' };
+ // Indexes are to menuitems in the menubar
+ const menubarIndexes = [1, 2, 3];
+ const menuIndexes = [0, 3, 5];
+ const menuitemIndexes = [2, 13, 23];
+
+ for (let j = 0; j < keys.length; j++) {
+ const key = keys[j];
+ const keyCode = keyCodes[key];
+
+ for (let i = 0; i < menubarIndexes.length; i++) {
+ const menubarItems = await t.context.queryElements(
+ t,
+ ex.menubarMenuitemSelector
+ );
- t.true(
- await checkFocus(t, ex.menuMenuitemSelectors[menuIndex], 0),
- 'Sending key "SPACE" to menuitem ' +
- menuIndex +
- ' in menubar should send focus to the first element in the submenu'
- );
+ const menubarIndex = menubarIndexes[i];
+ await menubarItems[menubarIndex].sendKeys(keyCode);
+
+ const menus = await t.context.queryElements(t, ex.anyMenuSelector);
+
+ const menuElement = menus[menuIndexes[i]];
+ const menuId = await menuElement.getAttribute('id');
+
+ // Test that the submenu is displayed
+ t.true(
+ await menuElement.isDisplayed(),
+ 'Sending key "' +
+ key +
+ '" to menuitem referencing menu with id="' +
+ menuId +
+ '" in menubar should display submenu'
+ );
+
+ // Check that focus is on first menuitem
+ t.true(
+ await checkFocus(t, ex.anyMenuitemSelector, menuitemIndexes[i]),
+ 'Sending key "' +
+ key +
+ '" to menubar item index "' +
+ menubarIndexes[i] +
+ '" in menubar should send focus to menuitem index "' +
+ menuitemIndexes[i] +
+ '" the first element in the submenu'
+ );
+ }
}
}
);
@@ -561,9 +601,11 @@ ariaTest(
ex.menubarMenuitemSelector
);
- for (let menuIndex = 0; menuIndex < ex.numMenus + 1; menuIndex++) {
- const currentIndex = menuIndex % ex.numMenus;
- const nextIndex = (menuIndex + 1) % ex.numMenus;
+ const numMenuitems = menubarItems.length;
+
+ for (let menuIndex = 0; menuIndex < numMenuitems + 1; menuIndex++) {
+ const currentIndex = menuIndex % numMenuitems;
+ const nextIndex = (menuIndex + 1) % numMenuitems;
// Send the ARROW_RIGHT key
await menubarItems[currentIndex].sendKeys(Key.ARROW_RIGHT);
@@ -581,7 +623,7 @@ ariaTest(
);
ariaTest(
- 'Key ARROW_RIGHT moves focus to next menubar item',
+ 'Key ARROW_LEFT moves focus to next menubar item',
exampleFile,
'menubar-left-arrow',
async (t) => {
@@ -590,16 +632,18 @@ ariaTest(
ex.menubarMenuitemSelector
);
+ const numMenuitems = menubarItems.length;
+
// Send the ARROW_LEFT key to the first menuitem
await menubarItems[0].sendKeys(Key.ARROW_LEFT);
// Test the focus is on the last menu item
t.true(
- await checkFocus(t, ex.menubarMenuitemSelector, ex.numMenus - 1),
+ await checkFocus(t, ex.menubarMenuitemSelector, numMenuitems - 1),
'Sending key "ARROW_LEFT" to menuitem 0 will change focus to menu item 3'
);
- for (let menuIndex = ex.numMenus - 1; menuIndex > 0; menuIndex--) {
+ for (let menuIndex = numMenuitems - 1; menuIndex > 0; menuIndex--) {
// Send the ARROW_LEFT key
await menubarItems[menuIndex].sendKeys(Key.ARROW_LEFT);
@@ -620,37 +664,40 @@ ariaTest(
exampleFile,
'menubar-up-arrow',
async (t) => {
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
- const menus = await t.context.queryElements(t, ex.menuSelector);
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- // Send the ENTER key
- await menubarItems[menuIndex].sendKeys(Key.UP);
+ const menubarIndexes = [1, 2, 3];
+ const menuIndexes = [0, 3, 5];
+ const menuitemIndexes = [8, 21, 30];
+
+ for (let i = 0; i < menubarIndexes.length; i++) {
+ const menubarItems = await t.context.queryElements(
+ t,
+ ex.menubarMenuitemSelector
+ );
+
+ const menubarIndex = menubarIndexes[i];
+ await menubarItems[menubarIndex].sendKeys(Key.UP);
+
+ const menus = await t.context.queryElements(t, ex.anyMenuSelector);
+
+ const menuElement = menus[menuIndexes[i]];
+ const menuId = await menuElement.getAttribute('id');
// Test that the submenu is displayed
t.true(
- await menus[menuIndex].isDisplayed(),
- 'Sending key "ENTER" to menuitem ' +
- menuIndex +
- ' in menubar should display submenu'
+ await menuElement.isDisplayed(),
+ 'Sending key ARROW_UP to menuitem referencing menu with id="' +
+ menuId +
+ '" in menubar should display submenu.'
);
- const numSubItems = (
- await t.context.queryElements(t, ex.menuMenuitemSelectors[menuIndex])
- ).length;
-
- // Test that the focus is on the last item in the list
+ // Check that focus is on first menuitem
t.true(
- await checkFocus(
- t,
- ex.menuMenuitemSelectors[menuIndex],
- numSubItems - 1
- ),
- 'Sending key "ENTER" to menuitem ' +
- menuIndex +
- ' in menubar should send focus to the first element in the submenu'
+ await checkFocus(t, ex.anyMenuitemSelector, menuitemIndexes[i]),
+ 'Sending key ARROW_UP to menubar item index "' +
+ menubarIndexes[i] +
+ '" in menubar should send focus to menuitem index "' +
+ menuitemIndexes[i] +
+ '" the last element in the submenu.'
);
}
}
@@ -661,29 +708,40 @@ ariaTest(
exampleFile,
'menubar-down-arrow',
async (t) => {
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
- const menus = await t.context.queryElements(t, ex.menuSelector);
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- // Send the ENTER key
- await menubarItems[menuIndex].sendKeys(Key.DOWN);
+ const menubarIndexes = [1, 2, 3];
+ const menuIndexes = [0, 3, 5];
+ const menuitemIndexes = [2, 13, 23];
+
+ for (let i = 0; i < menubarIndexes.length; i++) {
+ const menubarItems = await t.context.queryElements(
+ t,
+ ex.menubarMenuitemSelector
+ );
+
+ const menubarIndex = menubarIndexes[i];
+ await menubarItems[menubarIndex].sendKeys(Key.DOWN);
+
+ const menus = await t.context.queryElements(t, ex.anyMenuSelector);
+
+ const menuElement = menus[menuIndexes[i]];
+ const menuId = await menuElement.getAttribute('id');
// Test that the submenu is displayed
t.true(
- await menus[menuIndex].isDisplayed(),
- 'Sending key "ENTER" to menuitem ' +
- menuIndex +
- ' in menubar should display submenu'
+ await menuElement.isDisplayed(),
+ 'Sending key ARROW_DOWN to menuitem referencing menu with id="' +
+ menuId +
+ '" in menubar should display submenu'
);
- // Test that the focus is on the first item in the list
+ // Check that focus is on first menuitem
t.true(
- await checkFocus(t, ex.menuMenuitemSelectors[menuIndex], 0),
- 'Sending key "ENTER" to menuitem ' +
- menuIndex +
- ' in menubar should send focus to the first element in the submenu'
+ await checkFocus(t, ex.anyMenuitemSelector, menuitemIndexes[i]),
+ 'Sending key ARROW_DOWN to menubar item index "' +
+ menubarIndexes[i] +
+ '" in menubar should send focus to menuitem index "' +
+ menuitemIndexes[i] +
+ '" the last element in the submenu'
);
}
}
@@ -698,7 +756,10 @@ ariaTest(
t,
ex.menubarMenuitemSelector
);
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
+
+ const numMenubarItems = menubarItems.length;
+
+ for (let menuIndex = 0; menuIndex < numMenubarItems; menuIndex++) {
// Send the ARROW_RIGHT key to move the focus to later menu item for every test
for (let i = 0; i < menuIndex; i++) {
await menubarItems[i].sendKeys(Key.ARROW_RIGHT);
@@ -727,7 +788,10 @@ ariaTest(
t,
ex.menubarMenuitemSelector
);
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
+
+ const numMenubarItems = menubarItems.length;
+
+ for (let menuIndex = 0; menuIndex < numMenubarItems; menuIndex++) {
// Send the ARROW_RIGHT key to move the focus to later menu item for every test
for (let i = 0; i < menuIndex; i++) {
await menubarItems[i].sendKeys(Key.ARROW_RIGHT);
@@ -738,7 +802,7 @@ ariaTest(
// Test that the focus is on the last item in the list
t.true(
- await checkFocus(t, ex.menubarMenuitemSelector, ex.numMenus - 1),
+ await checkFocus(t, ex.menubarMenuitemSelector, numMenubarItems - 1),
'Sending key "END" to menuitem ' +
menuIndex +
' in menubar should move the focus to the last menuitem'
@@ -756,7 +820,8 @@ ariaTest(
{ sendChar: 'z', sendIndex: 0, endIndex: 0 },
{ sendChar: 'a', sendIndex: 0, endIndex: 1 },
{ sendChar: 'a', sendIndex: 1, endIndex: 2 },
- { sendChar: 'a', sendIndex: 2, endIndex: 0 },
+ { sendChar: 'a', sendIndex: 2, endIndex: 3 },
+ { sendChar: 'a', sendIndex: 3, endIndex: 1 },
];
const menubarItems = await t.context.queryElements(
@@ -781,257 +846,232 @@ ariaTest(
}
);
-// This test is failing due to a bug reported in issue: https://github.com/w3c/aria-practices/issues/907
-ariaTest.failing(
+ariaTest(
'ENTER in submenu selects item',
exampleFile,
'submenu-space-or-enter',
async (t) => {
- // Test all the level one menuitems
+ // Indexes to menuitems in the test
+ // first number is the menuitem to test
+ // second number is the index to ta menuitem that must be open
+ // if number is -1 that means that menu does not need
+ // to be open
+ // third number is the index to a menuitem that must be open
+ // if number is -1 that means that menu does not need
+ // to be open
+ // The text string is what the title should be when the link is
+ // activated
+ const testMenuitems = [
+ [0, -1, -1, 'Home'],
+ [2, 1, -1, 'Overview'],
+ [3, 1, -1, 'Administration'],
+ [5, 1, 4, 'History'],
+ [6, 1, 4, 'Current Statistics'],
+ [7, 1, 4, 'Awards'],
+ [9, 1, 8, 'For prospective students'],
+ [10, 1, 8, 'For alumni'],
+ [11, 1, 8, 'For visitors'],
+ [13, 12, -1, 'Apply'],
+ [15, 12, 14, 'Undergraduate'],
+ [16, 12, 14, 'Graduate'],
+ [17, 12, 14, 'Professional Schools'],
+ [18, 12, -1, 'Sign Up'],
+ [19, 12, -1, 'Visit'],
+ [20, 12, -1, 'Photo Tour'],
+ [21, 12, -1, 'Connect'],
+ [23, 22, -1, 'Colleges & Schools'],
+ [24, 22, -1, 'Programs of Study'],
+ [25, 22, -1, 'Honors Programs'],
+ [26, 22, -1, 'Online Courses'],
+ [27, 22, -1, 'Course Explorer'],
+ [28, 22, -1, 'Register for Class'],
+ [29, 22, -1, 'Academic Calendar'],
+ [30, 22, -1, 'Transcripts'],
+ ];
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- for (
- let itemIndex = 0;
- itemIndex < ex.numMenuMenuitems[menuIndex];
- itemIndex++
- ) {
- await t.context.session.get(t.context.url);
+ const numTests = testMenuitems.length;
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
+ const keys = ['ENTER', 'SPACE'];
+ const keyCodes = { ENTER: Key.ENTER, SPACE: ' ' };
+
+ for (let j = 0; j < keys.length; j++) {
+ const key = keys[j];
+ const keyCode = keyCodes[key];
- // Open the submenu
- await menubarItems[menuIndex].sendKeys(Key.ENTER);
- const items = await t.context.queryElements(
+ for (let i = 0; i < numTests; i++) {
+ const testMenuitem = testMenuitems[i];
+ const indexMenuitem = testMenuitem[0];
+ const indexMenubarOpen = testMenuitem[1];
+ const indexSubmenuOpen = testMenuitem[2];
+ const menuitemText = testMenuitem[3];
+
+ await openMenus(t, indexMenubarOpen, indexSubmenuOpen);
+
+ let menuitems = await t.context.queryElements(
t,
- ex.menuMenuitemSelectors[menuIndex]
+ ex.anyMenuitemSelector
);
- const itemText = await items[itemIndex].getText();
-
- // send ENTER to the item
- await items[itemIndex].sendKeys(Key.ENTER);
- await waitForUrlChange(t);
-
- t.not(
- await t.context.session.getCurrentUrl(),
- t.context.url,
- 'Sending key "ENTER" to menuitem "' +
- itemText +
- '" should navigate to a new webpage.'
- );
- }
- }
-
- // Test all the submenu menuitems
- for (let submenuLocation of ex.submenuLocations) {
- const [menuIndex, menuitemIndex] = submenuLocation;
+ await menuitems[indexMenuitem].sendKeys(keyCode);
- // Get the submenu associate with the menuitem we are testing
- const submenuMenuitemSelector = getSubmenuMenuitemSelector(
- menuIndex,
- menuitemIndex
- );
- const numItems = (
- await t.context.queryElements(t, submenuMenuitemSelector)
- ).length;
-
- // Test all the items in the submenu
- for (let itemIndex = 0; itemIndex < numItems; itemIndex++) {
- await openSubmenu(t, ...submenuLocation);
-
- // send ENTER to the item we are testing
- const items = await t.context.queryElements(t, submenuMenuitemSelector);
- const itemText = await items[itemIndex].getText();
- await items[itemIndex].sendKeys(Key.ENTER);
- await waitForUrlChange(t);
-
- t.not(
- await t.context.session.getCurrentUrl(),
- t.context.url,
- 'Sending key "ENTER" to menuitem ' +
- itemText +
- '" should navigate to a new webpage.'
+ t.true(
+ await compareText(t, ex.titleSelector, menuitemText),
+ 'Sending key "' +
+ key +
+ '" to menuitem "' +
+ menuitemText +
+ '" should update the content title.'
);
- await t.context.session.get(t.context.url);
+ // Test that the focus switches to the title
+ t.true(
+ await checkFocus(t, ex.titleSelector, 0),
+ 'Sending key "' +
+ key +
+ '" to menuitem "' +
+ '" the focus should be on the content title.'
+ );
}
}
}
);
-// This test is failing due to a bug reported in issue: https://github.com/w3c/aria-practices/issues/907
-ariaTest.failing(
- 'SPACE in submenu selects item',
+ariaTest(
+ 'ESCAPE to submenu closes submenu',
exampleFile,
- 'submenu-space-or-enter',
+ 'submenu-escape',
async (t) => {
- // Test all the level one menuitems
-
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- for (
- let itemIndex = 0;
- itemIndex < ex.numMenuMenuitems[menuIndex];
- itemIndex++
- ) {
- await t.context.session.get(t.context.url);
+ const testMenuitems = [
+ [1, -1, 2],
+ [1, -1, 3],
+ [1, -1, 4],
+ [1, -1, 8],
+ [1, 4, 5],
+ [1, 4, 6],
+ [1, 4, 7],
+ [1, 8, 9],
+ [1, 8, 10],
+ [1, 8, 11],
+ [12, -1, 13],
+ [12, -1, 14],
+ [12, -1, 18],
+ [12, -1, 19],
+ [12, -1, 20],
+ [12, -1, 21],
+ [12, 14, 15],
+ [12, 14, 16],
+ [12, 14, 17],
+ [22, -1, 23],
+ [22, -1, 24],
+ [22, -1, 25],
+ [22, -1, 26],
+ [22, -1, 27],
+ [22, -1, 28],
+ [22, -1, 29],
+ [22, -1, 30],
+ ];
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
+ for (let i = 0; i < testMenuitems.length; i++) {
+ const testMenuitem = testMenuitems[i];
+ const indexMenubarOpen = testMenuitem[0];
+ const indexSubmenuOpen = testMenuitem[1];
+ const menuitemSendEscape = testMenuitem[2];
- // Open the submenu
- await menubarItems[menuIndex].sendKeys(Key.ENTER);
- const items = await t.context.queryElements(
- t,
- ex.menuMenuitemSelectors[menuIndex]
- );
- const itemText = await items[itemIndex].getText();
-
- // send SPACE to the item
- await items[itemIndex].sendKeys(' ');
- await waitForUrlChange(t);
-
- t.not(
- await t.context.session.getCurrentUrl(),
- t.context.url,
- 'Sending key "SPACE" to menuitem "' +
- itemText +
- '" should navigate to a new webpage.'
- );
+ let indexMenuitem = indexMenubarOpen;
+ if (indexSubmenuOpen >= 0) {
+ indexMenuitem = indexSubmenuOpen;
}
- }
- // Test all the submenu menuitems
+ await openMenus(t, indexMenubarOpen, indexSubmenuOpen);
- for (let submenuLocation of ex.submenuLocations) {
- const [menuIndex, menuitemIndex] = submenuLocation;
+ let menuitems = await t.context.queryElements(t, ex.anyMenuitemSelector);
+ await menuitems[menuitemSendEscape].sendKeys(Key.ESCAPE);
- // Get the submenu associate with the menuitem we are testing
- const submenuMenuitemSelector = getSubmenuMenuitemSelector(
- menuIndex,
- menuitemIndex
+ t.true(
+ await checkFocus(t, ex.anyMenuitemSelector, indexMenuitem),
+ 'Sending key ESCAPE to menuitem "' +
+ menuitemSendEscape +
+ '" the focus should be on menuitem "' +
+ indexMenuitem +
+ '".'
);
- const numItems = (
- await t.context.queryElements(t, submenuMenuitemSelector)
- ).length;
-
- // Test all the items in the submenu
- for (let itemIndex = 0; itemIndex < numItems; itemIndex++) {
- await openSubmenu(t, ...submenuLocation);
-
- // send SPACE to the item we are testing
- const items = await t.context.queryElements(t, submenuMenuitemSelector);
- const itemText = await items[itemIndex].getText();
- await items[itemIndex].sendKeys(' ');
- await waitForUrlChange(t);
-
- t.not(
- await t.context.session.getCurrentUrl(),
- t.context.url,
- 'Sending key "SPACE" to menuitem ' +
- itemText +
- '" should navigate to a new webpage.'
- );
-
- await t.context.session.get(t.context.url);
- }
}
}
);
ariaTest(
- 'ESCAPE to submenu closes submenu',
+ 'ARROW_RIGHT to submenu closes submenu and opens next',
exampleFile,
- 'submenu-escape',
+ 'submenu-right-arrow',
async (t) => {
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
- const menus = await t.context.queryElements(t, ex.menuSelector);
-
- // Test all the level one menuitems
-
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- for (
- let itemIndex = 0;
- itemIndex < ex.numMenuMenuitems[menuIndex];
- itemIndex++
- ) {
- // Open the submenu
- await menubarItems[menuIndex].sendKeys(Key.ENTER);
-
- const items = await t.context.queryElements(
- t,
- ex.menuMenuitemSelectors[menuIndex]
- );
- const itemText = await items[itemIndex].getText();
-
- // send ARROW_RIGHT to the item
- await items[itemIndex].sendKeys(Key.ESCAPE);
+ const testMenuitems = [
+ [1, -1, 2, 12],
+ [1, -1, 3, 12],
+ [1, 4, 5, 12],
+ [1, 4, 6, 12],
+ [1, 4, 7, 12],
+ [1, 8, 9, 12],
+ [1, 8, 10, 12],
+ [1, 8, 11, 12],
+ [12, -1, 13, 22],
+ [12, -1, 18, 22],
+ [12, -1, 19, 22],
+ [12, -1, 20, 22],
+ [12, -1, 21, 22],
+ [12, 14, 15, 22],
+ [12, 14, 16, 22],
+ [12, 14, 17, 22],
+ [22, -1, 23, 0],
+ [22, -1, 24, 0],
+ [22, -1, 25, 0],
+ [22, -1, 26, 0],
+ [22, -1, 27, 0],
+ [22, -1, 28, 0],
+ [22, -1, 29, 0],
+ [22, -1, 30, 0],
+ [0, -1, 0, 1],
+ ];
- t.false(
- await menus[menuIndex].isDisplayed(),
- 'Sending key "ESCAPE" to submenu item "' +
- itemText +
- '" should close the menu'
- );
- t.true(
- await checkFocus(t, ex.menubarMenuitemSelector, menuIndex),
- 'Sending key "ESCAPE" to submenu item "' +
- itemText +
- '" should change the focus to menuitem ' +
- menuIndex +
- ' in the menubar'
- );
- }
- }
+ for (let i = 0; i < testMenuitems.length; i++) {
+ const testMenuitem = testMenuitems[i];
+ const indexMenubarOpen = testMenuitem[0];
+ const indexSubmenuOpen = testMenuitem[1];
+ const indexSendArrowRight = testMenuitem[2];
+ const indexIsOpen = testMenuitem[3];
- // Test all the submenu menuitems
+ await openMenus(t, indexMenubarOpen, indexSubmenuOpen);
- for (let submenuLocation of ex.submenuLocations) {
- const [menuIndex, menuitemIndex] = submenuLocation;
+ let menuitems = await t.context.queryElements(t, ex.anyMenuitemSelector);
+ await menuitems[indexSendArrowRight].sendKeys(Key.ARROW_RIGHT);
- // Get the submenu items we are testing
- let submenuMenuitemSelector = getSubmenuMenuitemSelector(
- menuIndex,
- menuitemIndex
+ t.true(
+ await checkFocus(t, ex.anyMenuitemSelector, indexIsOpen),
+ 'Sending key ARROW_RIGHT to menuitem "' +
+ indexSendArrowRight +
+ '" the focus should be on menuitem "' +
+ indexIsOpen +
+ '".'
);
- const items = await t.context.queryElements(t, submenuMenuitemSelector);
- const numItems = items.length;
-
- for (let itemIndex = 0; itemIndex < numItems; itemIndex++) {
- await openSubmenu(t, ...submenuLocation);
-
- // send ESCAPE to the item
- const itemText = await items[itemIndex].getText();
- await items[itemIndex].sendKeys(Key.ESCAPE);
- const submenuSelector = getSubmenuSelector(...submenuLocation);
+ if (await isExpandable(t, ex.anyMenuitemSelector, indexMenubarOpen)) {
t.false(
- await t.context.session
- .findElement(By.css(submenuSelector))
- .isDisplayed(),
- 'Sending key "ESCAPE" to submenu item "' +
- itemText +
- '" should close the menu'
+ await checkOpen(t, ex.anyMenuitemSelector, indexMenubarOpen),
+ 'Sending key ARROW_RIGHT to menuitem "' +
+ indexSendArrowRight +
+ '" the menubar menuitem "' +
+ indexMenubarOpen +
+ '" should be closed.'
);
+ }
+ if (await isExpandable(t, ex.anyMenuitemSelector, indexIsOpen)) {
t.true(
- await checkFocus(
- t,
- ex.menuMenuitemSelectors[menuIndex],
- menuitemIndex
- ),
- 'Sending key "ESCAPE" to submenu item "' +
- itemText +
- '" should send focus to menuitem ' +
- menuitemIndex +
- ' in the parent menu'
+ await checkOpen(t, ex.anyMenuitemSelector, indexIsOpen),
+ 'Sending key ARROW_RIGHT to menuitem "' +
+ indexSendArrowRight +
+ '" the menubar menuitem "' +
+ indexIsOpen +
+ '" should be open.'
);
}
}
@@ -1039,125 +1079,42 @@ ariaTest(
);
ariaTest(
- 'ARROW_RIGHT to submenu closes submenu and opens next',
+ 'ARROW_RIGHT to expandable submenu opens popout menu',
exampleFile,
'submenu-right-arrow',
async (t) => {
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
- const menus = await t.context.queryElements(t, ex.menuSelector);
-
- // Test all the level one menuitems
+ const testMenuitems = [
+ [1, 4, 5],
+ [1, 8, 9],
+ [12, 14, 15],
+ ];
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- for (
- let itemIndex = 0;
- itemIndex < ex.numMenuMenuitems[menuIndex];
- itemIndex++
- ) {
- // Open the submenu
- await menubarItems[menuIndex].sendKeys(Key.ENTER);
+ for (let i = 0; i < testMenuitems.length; i++) {
+ const testMenuitem = testMenuitems[i];
+ const indexMenubarOpen = testMenuitem[0];
+ const indexSendArrowRight = testMenuitem[1];
+ const indexHasFocus = testMenuitem[2];
- const items = await t.context.queryElements(
- t,
- ex.menuMenuitemSelectors[menuIndex]
- );
- const itemText = await items[itemIndex].getText();
- const hasSubmenu = await items[itemIndex].getAttribute('aria-haspopup');
-
- // send ARROW_RIGHT to the item
- await items[itemIndex].sendKeys(Key.ARROW_RIGHT);
-
- if (hasSubmenu) {
- const submenuSelector = getSubmenuSelector(menuIndex, itemIndex);
- const submenuMenuitemSelector = getSubmenuMenuitemSelector(
- menuIndex,
- itemIndex
- );
-
- t.true(
- await t.context.session
- .findElement(By.css(submenuSelector))
- .isDisplayed(),
- 'Sending key "ARROW_RIGHT" to menuitem "' +
- itemText +
- '" should open the submenu: ' +
- submenuSelector
- );
- t.true(
- await checkFocus(t, submenuMenuitemSelector, 0),
- 'Sending key "ARROW_RIGHT" to menuitem "' +
- itemIndex +
- '" should put focus on first item in submenu: ' +
- submenuSelector
- );
- } else {
- // Account for wrapping (index 0 should go to 3)
- const nextMenuIndex = menuIndex === 2 ? 0 : menuIndex + 1;
-
- // Test that the submenu is closed
- t.false(
- await menus[menuIndex].isDisplayed(),
- 'Sending key "ARROW_RIGHT" to submenu item "' +
- itemText +
- '" should close list'
- );
-
- // Test that the focus is on the menuitem in the menubar
- t.true(
- await checkFocus(t, ex.menubarMenuitemSelector, nextMenuIndex),
- 'Sending key "ARROW_RIGHT" to submenu item "' +
- itemText +
- '" should send focus to menuitem' +
- nextMenuIndex +
- ' in the menubar'
- );
- }
- }
- }
+ await openMenus(t, indexMenubarOpen, -1);
- // Test all the submenu menuitems
- for (let submenuLocation of ex.submenuLocations) {
- const [menuIndex, menuitemIndex] = submenuLocation;
+ let menuitems = await t.context.queryElements(t, ex.anyMenuitemSelector);
+ await menuitems[indexSendArrowRight].sendKeys(Key.ARROW_RIGHT);
- // Get the submenu items we are testing
- const submenuMenuitemSelector = getSubmenuMenuitemSelector(
- menuIndex,
- menuitemIndex
+ t.true(
+ await checkFocus(t, ex.anyMenuitemSelector, indexHasFocus),
+ 'Sending key ARROW_RIGHT to menuitem "' +
+ indexSendArrowRight +
+ '" the focus should be on menuitem "' +
+ indexHasFocus +
+ '".'
);
- const items = await t.context.queryElements(t, submenuMenuitemSelector);
- const numItems = items.length;
-
- for (let itemIndex = 0; itemIndex < numItems; itemIndex++) {
- await openSubmenu(t, ...submenuLocation);
-
- // send ARROW_RIGHT to the item
- const itemText = await items[itemIndex].getText();
- await items[itemIndex].sendKeys(Key.ARROW_RIGHT);
-
- // Account for wrapping (index 0 should go to 3)
- const nextMenuIndex = menuIndex === 2 ? 0 : menuIndex + 1;
- // Test that the submenu is closed
- t.false(
- await menus[menuIndex].isDisplayed(),
- 'Sending key "ARROW_RIGHT" to submenu item "' +
- itemText +
- '" should close list'
- );
-
- // Test that the focus is on the menuitem in the menubar
- t.true(
- await checkFocus(t, ex.menubarMenuitemSelector, nextMenuIndex),
- 'Sending key "ARROW_RIGHT" to submenu item "' +
- itemText +
- '" should send focus to menuitem' +
- nextMenuIndex +
- ' in the menubar'
- );
- }
+ t.true(
+ await checkOpen(t, ex.anyMenuitemSelector, indexSendArrowRight),
+ 'Sending key ARROW_RIGHT to menuitem "' +
+ indexSendArrowRight +
+ '" should open the popout menu.'
+ );
}
}
);
@@ -1167,97 +1124,113 @@ ariaTest(
exampleFile,
'submenu-left-arrow',
async (t) => {
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
- const menus = await t.context.queryElements(t, ex.menuSelector);
-
- // Test all the level one menuitems
+ const testMenuitems = [
+ [22, -1, 23, 12],
+ [22, -1, 24, 12],
+ [22, -1, 25, 12],
+ [22, -1, 26, 12],
+ [22, -1, 27, 12],
+ [22, -1, 28, 12],
+ [22, -1, 29, 12],
+ [22, -1, 30, 12],
+ [12, -1, 13, 1],
+ [12, -1, 14, 1],
+ [12, -1, 18, 1],
+ [12, -1, 19, 1],
+ [12, -1, 20, 1],
+ [12, -1, 21, 1],
+ [1, -1, 2, 0],
+ [1, -1, 3, 0],
+ [1, -1, 4, 0],
+ [1, -1, 8, 0],
+ [0, -1, 0, 22],
+ ];
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- for (
- let itemIndex = 0;
- itemIndex < ex.numMenuMenuitems[menuIndex];
- itemIndex++
- ) {
- // Open the submenu
- await menubarItems[menuIndex].sendKeys(Key.ENTER);
+ for (let i = 0; i < testMenuitems.length; i++) {
+ const testMenuitem = testMenuitems[i];
+ const indexMenubarOpen = testMenuitem[0];
+ const indexSubmenuOpen = testMenuitem[1];
+ const indexSendArrowLeft = testMenuitem[2];
+ const indexIsOpen = testMenuitem[3];
- const items = await t.context.queryElements(
- t,
- ex.menuMenuitemSelectors[menuIndex]
- );
- const itemText = await items[itemIndex].getText();
+ await openMenus(t, indexMenubarOpen, indexSubmenuOpen);
- // send ARROW_LEFT to the item
- await items[itemIndex].sendKeys(Key.ARROW_LEFT);
+ let menuitems = await t.context.queryElements(t, ex.anyMenuitemSelector);
+ await menuitems[indexSendArrowLeft].sendKeys(Key.ARROW_LEFT);
- // Account for wrapping (index 0 should go to 3)
- const nextMenuIndex = menuIndex === 0 ? 2 : menuIndex - 1;
+ t.true(
+ await checkFocus(t, ex.anyMenuitemSelector, indexIsOpen),
+ 'Sending key ARROW_LEFT to menuitem "' +
+ indexSendArrowLeft +
+ '" the focus should be on menuitem "' +
+ indexIsOpen +
+ '".'
+ );
- // Test that the submenu is closed
+ if (await isExpandable(t, ex.anyMenuitemSelector, indexMenubarOpen)) {
t.false(
- await menus[menuIndex].isDisplayed(),
- 'Sending key "ARROW_LEFT" to submenu item "' +
- itemText +
- '" should close list'
+ await checkOpen(t, ex.anyMenuitemSelector, indexMenubarOpen),
+ 'Sending key ARROW_LEFT to menuitem "' +
+ indexSendArrowLeft +
+ '" the menubar menuitem "' +
+ indexMenubarOpen +
+ '" should be closed.'
);
+ }
- // Test that the focus is on the menuitem in the menubar
+ if (await isExpandable(t, ex.anyMenuitemSelector, indexIsOpen)) {
t.true(
- await checkFocus(t, ex.menubarMenuitemSelector, nextMenuIndex),
- 'Sending key "ARROW_LEFT" to submenu item "' +
- itemText +
- '" should send focus to menuitem' +
- nextMenuIndex +
- ' in the menubar'
+ await checkOpen(t, ex.anyMenuitemSelector, indexIsOpen),
+ 'Sending key ARROW_LEFT to menuitem "' +
+ indexSendArrowLeft +
+ '" the menubar menuitem "' +
+ indexIsOpen +
+ '" should be open.'
);
}
}
+ }
+);
- // Test all the submenu menuitems
- for (let submenuLocation of ex.submenuLocations) {
- const [menuIndex, menuitemIndex] = submenuLocation;
+ariaTest(
+ 'ARROW_LEFT to popout menu closes popout and moves focus to submenu',
+ exampleFile,
+ 'submenu-left-arrow',
+ async (t) => {
+ const testMenuitems = [
+ [12, 14, 15],
+ [12, 14, 16],
+ [12, 14, 17],
+ ];
- // Get the submenu items we are testing
- const submenuMenuitemSelector = getSubmenuMenuitemSelector(
- menuIndex,
- menuitemIndex
- );
- const items = await t.context.queryElements(t, submenuMenuitemSelector);
- const numItems = items.length;
+ for (let i = 0; i < testMenuitems.length; i++) {
+ const testMenuitem = testMenuitems[i];
+ const indexMenubarOpen = testMenuitem[0];
+ const indexSubmenuOpen = testMenuitem[1];
+ const indexSendArrowLeft = testMenuitem[2];
- for (let itemIndex = 0; itemIndex < numItems; itemIndex++) {
- await openSubmenu(t, ...submenuLocation);
+ await openMenus(t, indexMenubarOpen, indexSubmenuOpen);
- // send ARROW_LEFT to the item
- const itemText = await items[itemIndex].getText();
- await items[itemIndex].sendKeys(Key.ARROW_LEFT);
+ let menuitems = await t.context.queryElements(t, ex.anyMenuitemSelector);
+ await menuitems[indexSendArrowLeft].sendKeys(Key.ARROW_LEFT);
- const submenuSelector = getSubmenuSelector(...submenuLocation);
- t.false(
- await t.context.session
- .findElement(By.css(submenuSelector))
- .isDisplayed(),
- 'Sending key "ARROW_LEFT" to submenu item "' +
- itemText +
- '" should close the menu'
- );
+ t.true(
+ await checkFocus(t, ex.anyMenuitemSelector, indexSubmenuOpen),
+ 'Sending key ARROW_LEFT to menuitem "' +
+ indexSendArrowLeft +
+ '" the focus should be on menuitem "' +
+ indexSubmenuOpen +
+ '".'
+ );
- t.true(
- await checkFocus(
- t,
- ex.menuMenuitemSelectors[menuIndex],
- menuitemIndex
- ),
- 'Sending key "ARROW_LEFT" to submenu item "' +
- itemText +
- '" should send focus to menuitem ' +
- menuitemIndex +
- ' in the parent menu'
- );
- }
+ t.false(
+ await checkOpen(t, ex.anyMenuitemSelector, indexSubmenuOpen),
+ 'Sending key ARROW_LEFT to menuitem "' +
+ indexSendArrowLeft +
+ '" the menubar menuitem "' +
+ indexSubmenuOpen +
+ '" should be closed.'
+ );
}
}
);
@@ -1267,81 +1240,33 @@ ariaTest(
exampleFile,
'submenu-down-arrow',
async (t) => {
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
+ for (let i = 0; i < ex.keyboardTestMenuitems.length; i++) {
+ const testMenuitem = ex.keyboardTestMenuitems[i];
+ const indexMenubarOpen = testMenuitem[0];
+ const indexSubmenuOpen = testMenuitem[1];
+ const menuitemIndexes = testMenuitem[2];
- // Test all the level one menuitems
-
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- for (
- let itemIndex = 0;
- itemIndex < ex.numMenuMenuitems[menuIndex];
- itemIndex++
- ) {
- // Open the submenu
- await menubarItems[menuIndex].sendKeys(Key.ENTER);
-
- const items = await t.context.queryElements(
- t,
- ex.menuMenuitemSelectors[menuIndex]
- );
- const itemText = await items[itemIndex].getText();
+ await openMenus(t, indexMenubarOpen, indexSubmenuOpen);
- // send ARROW_DOWN to the item
- await items[itemIndex].sendKeys(Key.ARROW_DOWN);
+ const len = menuitemIndexes.length;
- // Account for wrapping (last item should move to first item)
- const nextItemIndex =
- itemIndex === ex.numMenuMenuitems[menuIndex] - 1 ? 0 : itemIndex + 1;
+ for (let i = 0; i < len; i++) {
+ const indexSendArrowDown = menuitemIndexes[i];
+ const indexHasFocus = menuitemIndexes[(i + 1) % len];
- // Test that the focus is on the menuitem in the menubar
- t.true(
- await checkFocus(
- t,
- ex.menuMenuitemSelectors[menuIndex],
- nextItemIndex
- ),
- 'Sending key "ARROW_DOWN" to submenu item "' +
- itemText +
- '" should send focus to menuitem' +
- nextItemIndex +
- ' in the same menu'
+ let menuitems = await t.context.queryElements(
+ t,
+ ex.anyMenuitemSelector
);
- }
- }
-
- // Test all the submenu menuitems
- for (let submenuLocation of ex.submenuLocations) {
- const [menuIndex, menuitemIndex] = submenuLocation;
-
- // Get the submenu items we are testing
- const submenuMenuitemSelector = getSubmenuMenuitemSelector(
- menuIndex,
- menuitemIndex
- );
- const items = await t.context.queryElements(t, submenuMenuitemSelector);
- const numItems = items.length;
-
- for (let itemIndex = 0; itemIndex < numItems; itemIndex++) {
- await openSubmenu(t, ...submenuLocation);
+ await menuitems[indexSendArrowDown].sendKeys(Key.ARROW_DOWN);
- // send ARROW_DOWN to the item
- const itemText = await items[itemIndex].getText();
- await items[itemIndex].sendKeys(Key.ARROW_DOWN);
-
- // Account for wrapping (last item should move to first item)
- const nextItemIndex = itemIndex === numItems - 1 ? 0 : itemIndex + 1;
-
- // Test that the focus is on the menuitem in the menubar
t.true(
- await checkFocus(t, submenuMenuitemSelector, nextItemIndex),
- 'Sending key "ARROW_DOWN" to submenu item "' +
- itemText +
- '" should send focus to menuitem' +
- nextItemIndex +
- ' in the same menu'
+ await checkFocus(t, ex.anyMenuitemSelector, indexHasFocus),
+ 'Sending key ARROW_DOWN to menuitem "' +
+ indexSendArrowDown +
+ '" the focus should be on menuitem "' +
+ indexHasFocus +
+ '".'
);
}
}
@@ -1353,332 +1278,177 @@ ariaTest(
exampleFile,
'submenu-up-arrow',
async (t) => {
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
+ for (let i = 0; i < ex.keyboardTestMenuitems.length; i++) {
+ const testMenuitem = ex.keyboardTestMenuitems[i];
+ const indexMenubarOpen = testMenuitem[0];
+ const indexSubmenuOpen = testMenuitem[1];
+ const menuitemIndexes = testMenuitem[2];
- // Test all the level one menuitems
+ await openMenus(t, indexMenubarOpen, indexSubmenuOpen);
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- for (
- let itemIndex = 0;
- itemIndex < ex.numMenuMenuitems[menuIndex];
- itemIndex++
- ) {
- // Open the submenu
- await menubarItems[menuIndex].sendKeys(Key.ENTER);
+ const len = menuitemIndexes.length;
- const items = await t.context.queryElements(
+ for (let i = 1; i <= len; i++) {
+ const indexSendArrowUp = menuitemIndexes[i % len];
+ const indexHasFocus = menuitemIndexes[i - 1];
+
+ let menuitems = await t.context.queryElements(
t,
- ex.menuMenuitemSelectors[menuIndex]
+ ex.anyMenuitemSelector
);
- const itemText = await items[itemIndex].getText();
-
- // send ARROW_UP to the item
- await items[itemIndex].sendKeys(Key.ARROW_UP);
+ await menuitems[indexSendArrowUp].sendKeys(Key.ARROW_UP);
- // Account for wrapping (last item should move to first item)
- const nextItemIndex =
- itemIndex === 0 ? ex.numMenuMenuitems[menuIndex] - 1 : itemIndex - 1;
-
- // Test that the focus is on the menuitem in the menubar
t.true(
- await checkFocus(
- t,
- ex.menuMenuitemSelectors[menuIndex],
- nextItemIndex
- ),
- 'Sending key "ARROW_UP" to submenu item "' +
- itemText +
- '" should send focus to menuitem' +
- nextItemIndex +
- ' in the same menu'
+ await checkFocus(t, ex.anyMenuitemSelector, indexHasFocus),
+ 'Sending key ARROW_UP to menuitem "' +
+ indexSendArrowUp +
+ '" the focus should be on menuitem "' +
+ indexHasFocus +
+ '".'
);
}
}
+ }
+);
- // Test all the submenu menuitems
- for (let submenuLocation of ex.submenuLocations) {
- const [menuIndex, menuitemIndex] = submenuLocation;
+ariaTest(
+ 'HOME moves focus to first item in menu',
+ exampleFile,
+ 'submenu-home',
+ async (t) => {
+ for (let i = 0; i < ex.keyboardTestMenuitems.length; i++) {
+ const testMenuitem = ex.keyboardTestMenuitems[i];
+ const indexMenubarOpen = testMenuitem[0];
+ const indexSubmenuOpen = testMenuitem[1];
+ const menuitemIndexes = testMenuitem[2];
- // Get the submenu items we are testing
- const submenuMenuitemSelector = getSubmenuMenuitemSelector(
- menuIndex,
- menuitemIndex
- );
- const items = await t.context.queryElements(t, submenuMenuitemSelector);
- const numItems = items.length;
+ await openMenus(t, indexMenubarOpen, indexSubmenuOpen);
- for (let itemIndex = 0; itemIndex < numItems; itemIndex++) {
- await openSubmenu(t, ...submenuLocation);
+ const len = menuitemIndexes.length;
- // send ARROW_UP to the item
- const itemText = await items[itemIndex].getText();
- await items[itemIndex].sendKeys(Key.ARROW_UP);
+ for (let i = 0; i < len; i++) {
+ const indexSendHome = menuitemIndexes[i];
+ const indexHasFocus = menuitemIndexes[0];
- // Account for wrapping (last item should move to first item)
- const nextItemIndex = itemIndex === 0 ? numItems - 1 : itemIndex - 1;
+ let menuitems = await t.context.queryElements(
+ t,
+ ex.anyMenuitemSelector
+ );
+ await menuitems[indexSendHome].sendKeys(Key.HOME);
- // Test that the focus is on the menuitem in the menubar
t.true(
- await checkFocus(t, submenuMenuitemSelector, nextItemIndex),
- 'Sending key "ARROW_UP" to submenu item "' +
- itemText +
- '" should send focus to menuitem' +
- nextItemIndex +
- ' in the same menu'
+ await checkFocus(t, ex.anyMenuitemSelector, indexHasFocus),
+ 'Sending key HOME to menuitem "' +
+ indexSendHome +
+ '" the focus should be on menuitem "' +
+ indexHasFocus +
+ '".'
);
}
}
}
);
-ariaTest('HOME moves focus in menu', exampleFile, 'submenu-home', async (t) => {
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
-
- // Test all the level one menuitems
-
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- for (
- let itemIndex = 0;
- itemIndex < ex.numMenuMenuitems[menuIndex];
- itemIndex++
- ) {
- // Open the submenu
- await menubarItems[menuIndex].sendKeys(Key.ENTER);
-
- const items = await t.context.queryElements(
- t,
- ex.menuMenuitemSelectors[menuIndex]
- );
- const itemText = await items[itemIndex].getText();
-
- // send HOME to the item
- await items[itemIndex].sendKeys(Key.HOME);
-
- // Test that the focus is on the menuitem in the menubar
- t.true(
- await checkFocus(t, ex.menuMenuitemSelectors[menuIndex], 0),
- 'Sending key "HOME" to submenu item "' +
- itemText +
- '" should send focus to first menuitem in the same menu'
- );
- }
- }
-
- // Test all the submenu menuitems
- for (let submenuLocation of ex.submenuLocations) {
- const [menuIndex, menuitemIndex] = submenuLocation;
-
- // Get the submenu items we are testing
- const submenuMenuitemSelector = getSubmenuMenuitemSelector(
- menuIndex,
- menuitemIndex
- );
- const items = await t.context.queryElements(t, submenuMenuitemSelector);
- const numItems = items.length;
-
- for (let itemIndex = 0; itemIndex < numItems; itemIndex++) {
- await openSubmenu(t, ...submenuLocation);
-
- // send HOME to the item
- const itemText = await items[itemIndex].getText();
- await items[itemIndex].sendKeys(Key.HOME);
-
- t.true(
- await checkFocus(t, submenuMenuitemSelector, 0),
- 'Sending key "HOME" to submenu item "' +
- itemText +
- '" should send focus to the first menuitem in the same menu'
- );
- }
- }
-});
-
-ariaTest('END moves focus in menu', exampleFile, 'submenu-end', async (t) => {
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
-
- // Test all the level one menuitems
+ariaTest(
+ 'END moves focus to lest menuitem in the menu',
+ exampleFile,
+ 'submenu-end',
+ async (t) => {
+ for (let i = 0; i < ex.keyboardTestMenuitems.length; i++) {
+ const testMenuitem = ex.keyboardTestMenuitems[i];
+ const indexMenubarOpen = testMenuitem[0];
+ const indexSubmenuOpen = testMenuitem[1];
+ const menuitemIndexes = testMenuitem[2];
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- for (
- let itemIndex = 0;
- itemIndex < ex.numMenuMenuitems[menuIndex];
- itemIndex++
- ) {
- // Open the submenu
- await menubarItems[menuIndex].sendKeys(Key.ENTER);
+ await openMenus(t, indexMenubarOpen, indexSubmenuOpen);
- const items = await t.context.queryElements(
- t,
- ex.menuMenuitemSelectors[menuIndex]
- );
- const itemText = await items[itemIndex].getText();
+ const len = menuitemIndexes.length;
- // send END to the item
- await items[itemIndex].sendKeys(Key.END);
+ for (let i = 0; i < len; i++) {
+ const indexSendEnd = menuitemIndexes[i];
+ const indexHasFocus = menuitemIndexes[len - 1];
- // Test that the focus is on the menuitem in the menubar
- t.true(
- await checkFocus(
+ let menuitems = await t.context.queryElements(
t,
- ex.menuMenuitemSelectors[menuIndex],
- ex.numMenuMenuitems[menuIndex] - 1
- ),
- 'Sending key "END" to submenu item "' +
- itemText +
- '" should send focus to last menuitem in the same menu'
- );
- }
- }
-
- // Test all the submenu menuitems
- for (let submenuLocation of ex.submenuLocations) {
- const [menuIndex, menuitemIndex] = submenuLocation;
-
- // Get the submenu items we are testing
- const submenuMenuitemSelector = getSubmenuMenuitemSelector(
- menuIndex,
- menuitemIndex
- );
- const items = await t.context.queryElements(t, submenuMenuitemSelector);
- const numItems = items.length;
-
- for (let itemIndex = 0; itemIndex < numItems; itemIndex++) {
- await openSubmenu(t, ...submenuLocation);
-
- // send END to the item
- const itemText = await items[itemIndex].getText();
- await items[itemIndex].sendKeys(Key.END);
+ ex.anyMenuitemSelector
+ );
+ await menuitems[indexSendEnd].sendKeys(Key.END);
- t.true(
- await checkFocus(t, submenuMenuitemSelector, numItems - 1),
- 'Sending key "END" to submenu item "' +
- itemText +
- '" should send focus to the last menuitem in the same menu'
- );
+ t.true(
+ await checkFocus(t, ex.anyMenuitemSelector, indexHasFocus),
+ 'Sending key END to menuitem "' +
+ indexSendEnd +
+ '" the focus should be on menuitem "' +
+ indexHasFocus +
+ '".'
+ );
+ }
}
}
-});
+);
ariaTest(
'Character sends to menubar changes focus in menubar',
exampleFile,
'submenu-character',
async (t) => {
- const charIndexTest = [
- [
- // Tests for menu dropdown 0
- { sendChar: 'a', sendIndex: 0, endIndex: 1 },
- { sendChar: 'x', sendIndex: 1, endIndex: 1 },
- { sendChar: 'o', sendIndex: 1, endIndex: 0 },
- ],
- [
- // Tests for menu dropdown 1
- { sendChar: 'c', sendIndex: 0, endIndex: 5 },
- { sendChar: 'y', sendIndex: 5, endIndex: 5 },
- ],
- [
- // Tests for menu dropdown 2
- { sendChar: 'c', sendIndex: 0, endIndex: 4 },
- { sendChar: 'r', sendIndex: 4, endIndex: 5 },
- { sendChar: 'z', sendIndex: 5, endIndex: 5 },
- ],
+ const testMenuitems = [
+ [2, 1, -1, 'z', 2],
+ [2, 1, -1, 'a', 3],
+ [3, 1, -1, 'c', 8],
+ [4, 1, -1, 'o', 2],
+ [5, 1, 4, 'a', 7],
+ [6, 1, 4, 'h', 5],
+ [7, 1, 4, 'z', 7],
+ [8, 1, -1, 'z', 8],
+ [9, 1, 8, 'f', 10],
+ [10, 1, 8, 'f', 11],
+ [11, 1, 8, 'f', 9],
+ [13, 12, -1, 'a', 13],
+ [13, 12, -1, 'v', 19],
+ [14, 12, -1, 'a', 13],
+ [15, 12, 14, 'p', 17],
+ [16, 12, 14, 'u', 15],
+ [17, 12, 14, 'g', 16],
+ [18, 12, -1, 't', 14],
+ [19, 12, -1, 'v', 19],
+ [20, 12, -1, 'v', 19],
+ [21, 12, -1, 'p', 20],
+ [23, 22, -1, 'h', 25],
+ [24, 22, -1, 'r', 28],
+ [25, 22, -1, '1', 25],
+ [26, 22, -1, 'c', 27],
+ [27, 22, -1, 'c', 23],
+ [28, 22, -1, 'p', 24],
+ [29, 22, -1, 'o', 26],
+ [30, 22, -1, 't', 30],
];
- const menubarItems = await t.context.queryElements(
- t,
- ex.menubarMenuitemSelector
- );
- for (let menuIndex = 0; menuIndex < ex.numMenus; menuIndex++) {
- // Open the dropdown
- await menubarItems[menuIndex].sendKeys(Key.ARROW_DOWN);
- const items = await t.context.queryElements(
- t,
- ex.menuMenuitemSelectors[menuIndex]
- );
-
- for (let test of charIndexTest[menuIndex]) {
- // Send character to menuitem
- const itemText = await items[test.sendIndex].getText();
- await items[test.sendIndex].sendKeys(test.sendChar);
-
- // Test that the focus switches to the appropriate menuitem
- t.true(
- await checkFocus(
- t,
- ex.menuMenuitemSelectors[menuIndex],
- test.endIndex
- ),
- 'Sending character ' +
- test.sendChar +
- ' to menuitem ' +
- itemText +
- ' should move the focus to menuitem ' +
- test.endIndex
- );
- }
- }
+ for (let i = 0; i < testMenuitems.length; i++) {
+ const testMenuitem = testMenuitems[i];
- const subCharIndexTest = [
- [
- // Tests for menu dropdown 0
- { sendChar: 'c', sendIndex: 0, endIndex: 1 },
- { sendChar: 'h', sendIndex: 1, endIndex: 0 },
- { sendChar: 'x', sendIndex: 0, endIndex: 0 },
- ],
- [
- // Tests for menu dropdown 1
- { sendChar: 'f', sendIndex: 0, endIndex: 1 },
- { sendChar: 'f', sendIndex: 1, endIndex: 2 },
- ],
- [
- // Tests for menu dropdown 2
- { sendChar: 'p', sendIndex: 0, endIndex: 2 },
- { sendChar: 'z', sendIndex: 2, endIndex: 2 },
- ],
- ];
+ const indexMenuitem = testMenuitem[0];
+ const indexMenubarOpen = testMenuitem[1];
+ const indexSubmenuOpen = testMenuitem[2];
+ const testChar = testMenuitem[3];
+ const indexHasFocus = testMenuitem[4];
- let testIndex = 0;
+ await openMenus(t, indexMenubarOpen, indexSubmenuOpen);
- // Test all the submenu menuitems
- for (let submenuLocation of ex.submenuLocations) {
- const [menuIndex, menuitemIndex] = submenuLocation;
+ let menuitems = await t.context.queryElements(t, ex.anyMenuitemSelector);
- await openSubmenu(t, ...submenuLocation);
+ await menuitems[indexMenuitem].sendKeys(testChar);
- // Get the submenu items we are testing
- const submenuMenuitemSelector = getSubmenuMenuitemSelector(
- menuIndex,
- menuitemIndex
+ t.true(
+ await checkFocus(t, ex.anyMenuitemSelector, indexHasFocus),
+ 'Sending "' +
+ testChar +
+ '" to menuitem "' +
+ indexMenuitem +
+ '" the focus should be on menuitem "' +
+ indexHasFocus +
+ '".'
);
- const items = await t.context.queryElements(t, submenuMenuitemSelector);
-
- for (let test of subCharIndexTest[testIndex]) {
- // Send character to menuitem
- const itemText = await items[test.sendIndex].getText();
- await items[test.sendIndex].sendKeys(test.sendChar);
-
- // Test that the focus switches to the appropriate menuitem
- t.true(
- await checkFocus(t, submenuMenuitemSelector, test.endIndex),
- 'Sending character ' +
- test.sendChar +
- ' to menuitem ' +
- itemText +
- ' should move the focus to menuitem ' +
- test.endIndex
- );
- }
-
- testIndex++;
}
}
);