From 4996400100a90886389b847f82d33c667a85bd3d Mon Sep 17 00:00:00 2001 From: Valerie Young Date: Mon, 18 Jan 2021 06:52:54 -0800 Subject: [PATCH] Toolbar Example: Add missing keyboard tests (pull #1691) --- examples/toolbar/toolbar.html | 4 +- test/tests/toolbar_toolbar.js | 960 ++++++++++++++++++++++++++++++++-- 2 files changed, 910 insertions(+), 54 deletions(-) diff --git a/examples/toolbar/toolbar.html b/examples/toolbar/toolbar.html index 3e96d0d33..353c1b5d5 100644 --- a/examples/toolbar/toolbar.html +++ b/examples/toolbar/toolbar.html @@ -489,7 +489,7 @@

Checkbox (Night Mode)

- + Space Toggles the state of the checkbox. @@ -511,7 +511,7 @@

Link (Help)

- + Enter
Space Activate the link. diff --git a/test/tests/toolbar_toolbar.js b/test/tests/toolbar_toolbar.js index 500129a27..4cce7a5b2 100644 --- a/test/tests/toolbar_toolbar.js +++ b/test/tests/toolbar_toolbar.js @@ -7,7 +7,6 @@ const assertAttributeDNE = require('../util/assertAttributeDNE'); const assertAttributeValues = require('../util/assertAttributeValues'); const assertRovingTabindex = require('../util/assertRovingTabindex'); const assertHasFocus = require('../util/assertHasFocus'); -const assertNoElements = require('../util/assertNoElements'); const assertAttributeCanBeToggled = require('../util/assertAttributeCanBeToggled'); const exampleFile = 'toolbar/toolbar.html'; @@ -37,18 +36,86 @@ const ex = { last: '#ex1 [role="radio"]:nth-child(3)', }, tabbableItemAfterToolbarSelector: '#textarea1', - tabbableItemBeforeToolbarSelector: 'body > main > p > a:last-of-type', + tabbableItemBeforeToolbarSelector: '[href="../../#kbd_roving_tabindex"]', toolbarSelector: '#ex1 [role="toolbar"]', + checkboxSelector: '#ex1 #checkbox', + linkSelector: '#ex1 #link', + textArea: '#ex1 textarea', + itemsWithPopups: [0, 1, 2, 3, 4, 5], + toggleItems: [0, 1, 2], + toggleItemStyle: { + 0: { + style: 'fontWeight', + active: 'bold', + inactive: 'normal', + }, + 1: { + style: 'fontStyle', + active: 'italic', + inactive: 'normal', + }, + 2: { + style: 'textDecoration', + active: 'underline', + inactive: 'none', + }, + }, + radioItems: [3, 4, 5], + radioItemStyle: { 3: 'left', 4: 'center', 5: 'right' }, +}; + +const clickAndWait = async function (t, selector, index) { + index = index || 0; + let elements = await t.context.queryElements(t, selector); + await elements[index].click(); + + return await t.context.session + .wait( + async function () { + let tabindex = await elements[index].getAttribute('tabindex'); + return tabindex === '0'; + }, + t.context.waitTime, + `Timeout waiting for click to set tabindex="0" on '${selector}' at index ${index}` + ) + .catch((err) => { + return err; + }); +}; + +const waitAndCheckFocus = async function (t, selector, index) { + index = index || 0; + return t.context.session + .wait( + async function () { + return t.context.session.executeScript( + function () { + const [selector, index] = arguments; + let items = document.querySelectorAll(selector); + return items[index] === document.activeElement; + }, + selector, + index + ); + }, + t.context.waitTime, + `Timeout waiting for activeElement to become '${selector}' at index ${index}` + ) + .catch((err) => { + return err; + }); }; -const sendKeyAndAssertSelectorIsHidden = async function ( - t, - key, - selector, - selectorToBeHidden -) { - await t.context.session.findElement(By.css(selector)).sendKeys(key); - await assertNoElements(t, selectorToBeHidden); +const checkStyle = async function (t, style, value) { + return t.context.session.executeScript( + function () { + const [style, value] = arguments; + let textArea = document.querySelector('#ex1 textarea'); + return textArea.style[style] === value; + }, + style, + value + ); }; /** @@ -288,9 +355,18 @@ ariaTest( t, ex.fontFamilyMenuitemSelector ); + let menu = await t.context.session.findElement(By.css(ex.menuSelector)); for (let i = 0; i < menuItems.length; i++) { await menuButton.click(); + await t.context.session.wait( + async function () { + return await menu.isDisplayed(); + }, + t.context.waitTime, + `Waiting for menu to open` + ); + await menuItems[i].click(); await menuButton.click(); for (let j = 0; j < menuItems.length; j++) { @@ -427,9 +503,16 @@ ariaTest( } ); -ariaTest('', exampleFile, 'toolbar-spinbutton-aria-valuemax', async (t) => { - await assertAttributeValues(t, ex.spinSelector, 'aria-valuemax', '40'); -}); +ariaTest( + 'Spin button has aria-valuemax', + exampleFile, + 'toolbar-spinbutton-aria-valuemax', + async (t) => { + await assertAttributeValues(t, ex.spinSelector, 'aria-valuemax', '40'); + } +); + +// Keys ariaTest( 'ARROW_LEFT: If the first control has focus, focus moves to the last control.', @@ -490,45 +573,6 @@ ariaTest( } ); -ariaTest( - 'END: Moves focus to the last control.', - exampleFile, - 'toolbar-home', - async (t) => { - await t.context.session - .findElement(By.css(ex.itemSelectors.first)) - .sendKeys(Key.END); - await assertHasFocus(t, ex.itemSelectors.last); - } -); - -ariaTest( - 'ESCAPE: Escape key hides any .popup-label', - exampleFile, - 'toolbar-toggle-esc', - async (t) => { - await t.context.session.findElement(By.css(ex.itemSelectors.first)).click(); - await sendKeyAndAssertSelectorIsHidden( - t, - Key.ESCAPE, - ex.itemSelectors.first, - '.popup-label.show' - ); - } -); - -ariaTest( - 'HOME: Moves focus to the first control.', - exampleFile, - 'toolbar-home', - async (t) => { - await t.context.session - .findElement(By.css(ex.itemSelectors.last)) - .sendKeys(Key.HOME); - await assertHasFocus(t, ex.itemSelectors.first); - } -); - ariaTest( 'TAB: Moves focus into the toolbar, to the first menu item', exampleFile, @@ -772,3 +816,815 @@ ariaTest( await assertHasFocus(t, ex.radioButtons.last); } ); + +ariaTest('key HOME moves focus', exampleFile, 'toolbar-home', async (t) => { + let items = await t.context.queryElements(t, ex.itemSelector); + + for (let i = 0; i < items.length; i++) { + await items[i].sendKeys(Key.HOME); + + t.true( + await waitAndCheckFocus(t, ex.itemSelector, 0), + `Sending HOME to item at index ${i} should move focus to the first item` + ); + + t.is( + await items[0].getAttribute('tabindex'), + '0', + `Sending HOME to item at index ${i} should set tabindex=0 on the first item` + ); + } +}); + +ariaTest('key END moves focus', exampleFile, 'toolbar-end', async (t) => { + let items = await t.context.queryElements(t, ex.itemSelector); + + for (let i = 0; i < items.length; i++) { + await items[i].sendKeys(Key.END); + + t.true( + await waitAndCheckFocus(t, ex.itemSelector, items.length - 1), + `Sending END to item at index ${i} should move focus to the last item` + ); + + t.is( + await items[items.length - 1].getAttribute('tabindex'), + '0', + `Sending END to item at index ${i} should set tabindex=0 on the last item` + ); + } +}); + +ariaTest( + 'key ESCAPE closes popup', + exampleFile, + 'toolbar-toggle-esc', + async (t) => { + let items = await t.context.queryElements(t, ex.itemSelector); + + for (let i of ex.itemsWithPopups) { + await clickAndWait(t, ex.itemSelector, i); + + let popup = await items[i].findElement(By.css('span.popup-label')); + t.true( + await popup.isDisplayed(), + `Clicking menu item ${i} should display a popup and if not, this test might be broken.` + ); + + await items[i].sendKeys(Key.ESCAPE); + + t.false( + await popup.isDisplayed(), + `Sending key ESCAPE to menuitem ${i} should close popup label.` + ); + } + } +); + +ariaTest( + 'Key ENTER toggles toggle buttons', + exampleFile, + 'toolbar-toggle-enter-or-space', + async (t) => { + let items = await t.context.queryElements(t, ex.itemSelector); + + for (let i of ex.toggleItems) { + await items[i].sendKeys(Key.ENTER); + + t.is( + await items[i].getAttribute('aria-pressed'), + 'true', + `Sending enter to inactive toggle at index ${i} should set aria-pressed to true` + ); + t.true( + await checkStyle( + t, + ex.toggleItemStyle[i].style, + ex.toggleItemStyle[i].active + ), + `Sending enter to inactive toggle at index ${i} should set the style ${ex.toggleItemStyle[i].style} in the text area to ${ex.toggleItemStyle[i].active}` + ); + + await items[i].sendKeys(Key.ENTER); + + t.is( + await items[i].getAttribute('aria-pressed'), + 'false', + `Sending enter to active toggle at index ${i} should set aria-pressed to false` + ); + t.true( + await checkStyle( + t, + ex.toggleItemStyle[i].style, + ex.toggleItemStyle[i].inactive + ), + `Sending enter to active toggle at index ${i} should set the style ${ex.toggleItemStyle[i].style} in the text area to ${ex.toggleItemStyle[i].inactive}.` + ); + } + } +); + +ariaTest( + 'Key SPACE toggles toggle buttons', + exampleFile, + 'toolbar-toggle-enter-or-space', + async (t) => { + let items = await t.context.queryElements(t, ex.itemSelector); + + for (let i of ex.toggleItems) { + await items[i].sendKeys(Key.SPACE); + + t.is( + await items[i].getAttribute('aria-pressed'), + 'true', + `Sending space to inactive toggle at index ${i} should set aria-pressed to true` + ); + t.true( + await checkStyle( + t, + ex.toggleItemStyle[i].style, + ex.toggleItemStyle[i].active + ), + `Sending space to inactive toggle at index ${i} should set the style ${ex.toggleItemStyle[i].style} in the text area to ${ex.toggleItemStyle[i].active}` + ); + + await items[i].sendKeys(Key.SPACE); + + t.is( + await items[i].getAttribute('aria-pressed'), + 'false', + `Sending space to active toggle at index ${i} should set aria-pressed to false` + ); + t.true( + await checkStyle( + t, + ex.toggleItemStyle[i].style, + ex.toggleItemStyle[i].inactive + ), + `Sending space to active toggle at index ${i} should set the style ${ex.toggleItemStyle[i].style} in the text area to ${ex.toggleItemStyle[i].inactive}.` + ); + } + } +); + +ariaTest( + 'Key ENTER selects radio option for alignment', + exampleFile, + 'toolbar-radio-enter-or-space', + async (t) => { + let items = await t.context.queryElements(t, ex.itemSelector); + + for (let i of ex.radioItems) { + await items[i].sendKeys(Key.ENTER); + + t.is( + await items[i].getAttribute('aria-checked'), + 'true', + `Sending enter to item at index ${i} should set aria-checked to true` + ); + t.true( + await checkStyle(t, 'textAlign', ex.radioItemStyle[i]), + `Sending enter to radio index ${i} should set the style 'textAlign' style in the text area to ${ex.radioItemStyle[i]}}` + ); + } + } +); + +ariaTest( + 'Key SPACE selects radio option for alignment', + exampleFile, + 'toolbar-radio-enter-or-space', + async (t) => { + let items = await t.context.queryElements(t, ex.itemSelector); + + for (let i of ex.radioItems) { + await items[i].sendKeys(Key.SPACE); + + t.is( + await items[i].getAttribute('aria-checked'), + 'true', + `Sending SPACE to item at index ${i} should set aria-checked to true` + ); + t.true( + await checkStyle(t, 'textAlign', ex.radioItemStyle[i]), + `Sending SPACE to radio index ${i} should set the style 'textAlign' style in the text area to ${ex.radioItemStyle[i]}}` + ); + } + } +); + +ariaTest( + 'Key DOWN ARROW moves focus between radio text align items', + exampleFile, + 'toolbar-radio-down-arrow', + async (t) => { + let items = await t.context.queryElements(t, ex.itemSelector); + + await items[ex.radioItems[0]].sendKeys(Key.ARROW_DOWN); + + for (let i of ex.radioItems.slice(1)) { + t.true( + await waitAndCheckFocus(t, ex.itemSelector, i), + `Sending ARROW DOWN to radio item at index ${ + i - 1 + } should move focus to item at index ${i}` + ); + + t.is( + await items[i].getAttribute('tabindex'), + '0', + `Sending ARROW DOWN to radio item at index ${ + i - 1 + } should set tabindex=0 for item at index ${i}` + ); + + await items[i].sendKeys(Key.ARROW_DOWN); + } + + // Focus should now be on first + t.true( + await waitAndCheckFocus(t, ex.itemSelector, ex.radioItems[0]), + `Sending ARROW DOWN to the last radio item should move focus to the first radio item` + ); + + t.is( + await items[ex.radioItems[0]].getAttribute('tabindex'), + '0', + `Sending ARROW DOWN to the last radio item should set tabindex=0 for the first radio item` + ); + } +); + +ariaTest( + 'Key UP ARROW moves focus between radio text align items', + exampleFile, + 'toolbar-radio-up-arrow', + async (t) => { + let items = await t.context.queryElements(t, ex.itemSelector); + + await items[ex.radioItems[0]].sendKeys(Key.ARROW_UP); + + let lastItemIndex = ex.radioItems.length - 1; + + t.true( + await waitAndCheckFocus(t, ex.itemSelector, ex.radioItems[lastItemIndex]), + `Sending ARROW UP to the first radio item should move focus to the last radio item` + ); + + t.is( + await items[ex.radioItems[lastItemIndex]].getAttribute('tabindex'), + '0', + `Sending ARROW UP to the first radio item should set tabindex=0 for the last radio item` + ); + + for (let i of ex.radioItems.slice(1).reverse()) { + await items[i].sendKeys(Key.ARROW_UP); + + t.true( + await waitAndCheckFocus(t, ex.itemSelector, i - 1), + `Sending ARROW UP to radio item at index ${i} should move focus to item at index ${ + i - 1 + }` + ); + + t.is( + await items[i - 1].getAttribute('tabindex'), + '0', + `Sending ARROW UP to radio item at index ${i} should set tabindex=0 for item at index ${ + i - 1 + }` + ); + } + } +); + +ariaTest( + 'Test key enter on copy/paste/cut keys', + exampleFile, + 'toolbar-button-enter-or-space', + async (t) => { + let textarea = await t.context.session.findElement(By.css('textarea')); + await textarea.sendKeys(Key.chord(Key.CONTROL, 'a')); + let originalText = await textarea.getAttribute('value'); + + const buttons = await t.context.queryElements( + t, + ex.textEditButtonsSelector + ); + const copy = 0; + const paste = 1; + const cut = 2; + + t.is( + await buttons[copy].getAttribute('aria-disabled'), + 'false', + 'After selecting text' + ); + t.is( + await buttons[paste].getAttribute('aria-disabled'), + 'true', + 'After selecting text' + ); + t.is( + await buttons[cut].getAttribute('aria-disabled'), + 'false', + 'After selecting text' + ); + + // SEND ENTER TO COPY + await buttons[copy].sendKeys(Key.ENTER); + + t.is( + await buttons[copy].getAttribute('aria-disabled'), + 'false', + 'After copy' + ); + t.is( + await buttons[paste].getAttribute('aria-disabled'), + 'false', + 'After copy' + ); + t.is( + await buttons[cut].getAttribute('aria-disabled'), + 'false', + 'After copy' + ); + + // SEND ENTER TO CUT + await buttons[cut].sendKeys(Key.ENTER); + + t.is( + await buttons[copy].getAttribute('aria-disabled'), + 'true', + 'After cut' + ); + t.is( + await buttons[paste].getAttribute('aria-disabled'), + 'false', + 'After cut' + ); + t.is(await buttons[cut].getAttribute('aria-disabled'), 'true', 'After cut'); + + await t.context.session.wait( + async function () { + return (await textarea.getAttribute('value')) === ''; + }, + t.context.waitTime, + `Timeout waiting for sending ENTER to cut to clear text in textarea` + ); + t.is( + await textarea.getAttribute('value'), + '', + 'After cut, there should be no text in the text area' + ); + + // SEND ENTER TO PASTE + await buttons[paste].sendKeys(Key.ENTER); + + t.is( + await textarea.getAttribute('value'), + originalText, + 'After paste, there should be the previous text in the text area' + ); + } +); + +ariaTest( + 'Test key space on copy/paste/cut keys', + exampleFile, + 'toolbar-button-enter-or-space', + async (t) => { + let textarea = await t.context.session.findElement(By.css('textarea')); + await textarea.sendKeys(Key.chord(Key.CONTROL, 'a')); + let originalText = await textarea.getAttribute('value'); + + const buttons = await t.context.queryElements( + t, + ex.textEditButtonsSelector + ); + const copy = 0; + const paste = 1; + const cut = 2; + + t.is( + await buttons[copy].getAttribute('aria-disabled'), + 'false', + 'After selecting text' + ); + t.is( + await buttons[paste].getAttribute('aria-disabled'), + 'true', + 'After selecting text' + ); + t.is( + await buttons[cut].getAttribute('aria-disabled'), + 'false', + 'After selecting text' + ); + + // SEND SPACE TO COPY + await buttons[copy].sendKeys(Key.SPACE); + + t.is( + await buttons[copy].getAttribute('aria-disabled'), + 'false', + 'After copy' + ); + t.is( + await buttons[paste].getAttribute('aria-disabled'), + 'false', + 'After copy' + ); + t.is( + await buttons[cut].getAttribute('aria-disabled'), + 'false', + 'After copy' + ); + + // SEND SPACE TO CUT + await buttons[cut].sendKeys(Key.SPACE); + + t.is( + await buttons[copy].getAttribute('aria-disabled'), + 'true', + 'After cut' + ); + t.is( + await buttons[paste].getAttribute('aria-disabled'), + 'false', + 'After cut' + ); + t.is(await buttons[cut].getAttribute('aria-disabled'), 'true', 'After cut'); + + await t.context.session.wait( + async function () { + return (await textarea.getAttribute('value')) === ''; + }, + t.context.waitTime, + `Timeout waiting for sending SPACE to cut to clear text in textarea` + ); + t.is( + await textarea.getAttribute('value'), + '', + 'After cut, there should be no text in the text area' + ); + + // SEND SPACE TO PASTE + await buttons[paste].sendKeys(Key.SPACE); + + t.is( + await textarea.getAttribute('value'), + originalText, + 'After paste, there should be the previous text in the text area' + ); + } +); + +ariaTest( + 'Keys for opening menubutton', + exampleFile, + 'toolbar-menubutton-enter-or-space-or-down-or-up', + async (t) => { + const menubutton = await t.context.session.findElement( + By.css(ex.fontFamilyButtonSelector) + ); + const menu = await t.context.session.findElement(By.css(ex.menuSelector)); + + for (let key of [Key.ENTER, Key.SPACE, Key.ARROW_UP, Key.ARROW_DOWN]) { + await menubutton.sendKeys(key); + + t.true( + await menu.isDisplayed(), + `After sending key ${key} the menu should be displayed` + ); + + t.true( + await waitAndCheckFocus(t, ex.fontFamilyMenuitemSelector, 0), + `After sending key ${key} the focus should be on the first item` + ); + + // Close the menu by clicking outside of it + await (await t.context.session.findElement(By.css('#ex_label'))).click(); + } + + // Select a different font family, and confirm that opening the submenu puts focus on selected font family + + await menubutton.sendKeys(Key.ENTER); + await ( + await t.context.queryElements(t, ex.fontFamilyMenuitemSelector) + )[1].click(); + + for (let key of [Key.ENTER, Key.SPACE, Key.ARROW_UP, Key.ARROW_DOWN]) { + await menubutton.sendKeys(key); + + t.true( + await menu.isDisplayed(), + `After sending key ${key} the menu should be displayed` + ); + + t.true( + await waitAndCheckFocus(t, ex.fontFamilyMenuitemSelector, 1), + `Now that the second font has been selected, after sending key ${key} the focus should be on the second item` + ); + + // Close the menu by clicking outside of it + await (await t.context.session.findElement(By.css('#ex_label'))).click(); + } + } +); + +ariaTest( + 'Key ENTER changes font', + exampleFile, + 'toolbar-menu-enter-or-space', + async (t) => { + const menubutton = await t.context.session.findElement( + By.css(ex.fontFamilyButtonSelector) + ); + const menuitems = await t.context.queryElements( + t, + ex.fontFamilyMenuitemSelector + ); + + for (let i = 0; i < menuitems.length; i++) { + await menubutton.sendKeys(Key.ENTER); + let font = await menuitems[i].getText(); + await menuitems[i].sendKeys(Key.ENTER); + t.true( + await checkStyle(t, 'fontFamily', font.toLowerCase()), + `The font family in the text area should be updated to reflect the menuitem ('${font}') when sending key 'ENTER'` + ); + } + } +); + +ariaTest( + 'Key SPACE changes font', + exampleFile, + 'toolbar-menu-enter-or-space', + async (t) => { + const menubutton = await t.context.session.findElement( + By.css(ex.fontFamilyButtonSelector) + ); + const menuitems = await t.context.queryElements( + t, + ex.fontFamilyMenuitemSelector + ); + + for (let i = 0; i < menuitems.length; i++) { + await menubutton.sendKeys(Key.SPACE); + let font = await menuitems[i].getText(); + await menuitems[i].sendKeys(Key.SPACE); + t.true( + await checkStyle(t, 'fontFamily', font.toLowerCase()), + `The font family in the text area should be updated to reflect the menuitem ('${font}') when sending key 'SPACE'` + ); + } + } +); + +ariaTest( + 'DOWN ARROW moves focus between menuitems', + exampleFile, + 'toolbar-menu-down-arrow', + async (t) => { + const menubutton = await t.context.session.findElement( + By.css(ex.fontFamilyButtonSelector) + ); + const menuitems = await t.context.queryElements( + t, + ex.fontFamilyMenuitemSelector + ); + + await menubutton.sendKeys(Key.ENTER); + + for (let i = 0; i < menuitems.length - 1; i++) { + await menuitems[i].sendKeys(Key.ARROW_DOWN); + + t.true( + await waitAndCheckFocus(t, ex.fontFamilyMenuitemSelector, i + 1), + `Sending ARROW DOWN to menuitem at index ${i} should move focus to item at index ${ + i + 1 + }` + ); + } + + await menuitems[menuitems.length - 1].sendKeys(Key.ARROW_DOWN); + + t.true( + await waitAndCheckFocus(t, ex.fontFamilyMenuitemSelector, 0), + `Sending ARROW DOWN to the last menu item should move focus to the first menu item` + ); + } +); + +ariaTest( + 'UP ARROW moves focus', + exampleFile, + 'toolbar-menu-up-arrow', + async (t) => { + const menubutton = await t.context.session.findElement( + By.css(ex.fontFamilyButtonSelector) + ); + const menuitems = await t.context.queryElements( + t, + ex.fontFamilyMenuitemSelector + ); + + await menubutton.sendKeys(Key.ENTER); + await menuitems[0].sendKeys(Key.ARROW_UP); + + t.true( + await waitAndCheckFocus( + t, + ex.fontFamilyMenuitemSelector, + menuitems.length - 1 + ), + `Sending ARROW UP to the first menu item should move focus to the last menu item` + ); + + for (let i = menuitems.length - 1; i > 0; i--) { + await menuitems[i].sendKeys(Key.ARROW_UP); + + t.true( + await waitAndCheckFocus(t, ex.fontFamilyMenuitemSelector, i - 1), + `Sending ARROW UP to menuitem at index ${i} should move focus to item at index ${ + i - 1 + }` + ); + } + } +); + +ariaTest( + 'ESC sent to menuitem closes menu', + exampleFile, + 'toolbar-menu-escape', + async (t) => { + const menubutton = await t.context.session.findElement( + By.css(ex.fontFamilyButtonSelector) + ); + const menu = await t.context.session.findElement(By.css(ex.menuSelector)); + const menuitems = await t.context.queryElements( + t, + ex.fontFamilyMenuitemSelector + ); + + for (let i = menuitems.length - 1; i > 0; i--) { + await menubutton.sendKeys(Key.ENTER); + await menuitems[i].sendKeys(Key.ESCAPE); + + t.true( + await waitAndCheckFocus(t, ex.fontFamilyButtonSelector), + `Sending ESCAPE to menuitem at index ${i} should move focus to the font family menubutton` + ); + t.false( + await menu.isDisplayed(), + `Sending ESCAPE to menuitem at index ${i} should close the menu` + ); + } + } +); + +ariaTest( + 'DOWN ARROW to spinbutton decrease value by 1', + exampleFile, + 'toolbar-spinbutton-down-arrow', + async (t) => { + const spinbutton = await t.context.session.findElement( + By.css(ex.spinSelector) + ); + + const originalValue = await spinbutton.getAttribute('aria-valuenow'); + spinbutton.sendKeys(Key.ARROW_DOWN); + + await t.context.session.wait( + async function () { + return ( + (await spinbutton.getAttribute('aria-valuenow')) !== originalValue + ); + }, + t.context.waitTime, + 'Timeout waiting for aria-valuenow value to change' + ); + + let valuenow = await spinbutton.getAttribute('aria-valuenow'); + + t.is( + parseInt(valuenow), + parseInt(originalValue) - 1, + `Sending down arrow to the spin button should switch the value to one less than the original value` + ); + t.true( + await checkStyle(t, 'fontSize', `${valuenow}pt`), + `Sending down arrow to the spin button should switch the textarea's fontSize styling` + ); + } +); + +ariaTest( + 'UP ARROW to spin button increase value by 1', + exampleFile, + 'toolbar-spinbutton-up-arrow', + async (t) => { + const spinbutton = await t.context.session.findElement( + By.css(ex.spinSelector) + ); + + const originalValue = await spinbutton.getAttribute('aria-valuenow'); + spinbutton.sendKeys(Key.ARROW_UP); + + await t.context.session.wait( + async function () { + return ( + (await spinbutton.getAttribute('aria-valuenow')) !== originalValue + ); + }, + t.context.waitTime, + 'Timeout waiting for aria-valuenow value to change' + ); + + let valuenow = await spinbutton.getAttribute('aria-valuenow'); + t.is( + parseInt(valuenow), + parseInt(originalValue) + 1, + `Sending up arrow to the spin button should switch the value to one more than the original value` + ); + + t.true( + await checkStyle(t, 'fontSize', `${valuenow}pt`), + `Sending up arrow to the spin button should switch the textarea's fontSize styling` + ); + } +); + +ariaTest( + 'PAGE DOWN to spin button decrease value by 5', + exampleFile, + 'toolbar-spinbutton-page-down', + async (t) => { + const spinbutton = await t.context.session.findElement( + By.css(ex.spinSelector) + ); + + const originalValue = await spinbutton.getAttribute('aria-valuenow'); + spinbutton.sendKeys(Key.PAGE_DOWN); + + await t.context.session.wait( + async function () { + return ( + (await spinbutton.getAttribute('aria-valuenow')) !== originalValue + ); + }, + t.context.waitTime, + 'Timeout waiting for aria-valuenow value to change' + ); + + let valuenow = await spinbutton.getAttribute('aria-valuenow'); + t.is( + parseInt(valuenow), + parseInt(originalValue) - 5, + `Sending up arrow to the spin button should switch the value to five less than the original value` + ); + + t.true( + await checkStyle(t, 'fontSize', `${valuenow}pt`), + `Sending up arrow to the spin button should switch the textarea's fontSize styling` + ); + } +); +ariaTest( + 'PAGE UP to spin button increase by 5', + exampleFile, + 'toolbar-spinbutton-page-up', + async (t) => { + const spinbutton = await t.context.session.findElement( + By.css(ex.spinSelector) + ); + + const originalValue = await spinbutton.getAttribute('aria-valuenow'); + spinbutton.sendKeys(Key.PAGE_UP); + + await t.context.session.wait( + async function () { + return ( + (await spinbutton.getAttribute('aria-valuenow')) !== originalValue + ); + }, + t.context.waitTime, + 'Timeout waiting for aria-valuenow value to change' + ); + + let valuenow = await spinbutton.getAttribute('aria-valuenow'); + t.is( + parseInt(valuenow), + parseInt(originalValue) + 5, + `Sending up arrow to the spin button should switch the value to one more than the original value` + ); + + t.true( + await checkStyle(t, 'fontSize', `${valuenow}pt`), + `Sending up arrow to the spin button should switch the textarea's fontSize styling` + ); + } +);