summaryrefslogtreecommitdiffstats
path: root/accessible/tests/browser/windows/uia/browser_simplePatterns.js
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/tests/browser/windows/uia/browser_simplePatterns.js')
-rw-r--r--accessible/tests/browser/windows/uia/browser_simplePatterns.js445
1 files changed, 445 insertions, 0 deletions
diff --git a/accessible/tests/browser/windows/uia/browser_simplePatterns.js b/accessible/tests/browser/windows/uia/browser_simplePatterns.js
new file mode 100644
index 0000000000..f464db0e13
--- /dev/null
+++ b/accessible/tests/browser/windows/uia/browser_simplePatterns.js
@@ -0,0 +1,445 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/* import-globals-from ../../../mochitest/role.js */
+/* import-globals-from ../../../mochitest/states.js */
+loadScripts(
+ { name: "role.js", dir: MOCHITESTS_DIR },
+ { name: "states.js", dir: MOCHITESTS_DIR }
+);
+
+/* eslint-disable camelcase */
+const ExpandCollapseState_Collapsed = 0;
+const ExpandCollapseState_Expanded = 1;
+const ToggleState_Off = 0;
+const ToggleState_On = 1;
+const ToggleState_Indeterminate = 2;
+/* eslint-enable camelcase */
+
+/**
+ * Test the Invoke pattern.
+ */
+addUiaTask(
+ `
+<button id="button">button</button>
+<p id="p">p</p>
+<input id="checkbox" type="checkbox">
+ `,
+ async function testInvoke() {
+ await definePyVar("doc", `getDocUia()`);
+ await assignPyVarToUiaWithId("button");
+ await definePyVar("pattern", `getUiaPattern(button, "Invoke")`);
+ ok(await runPython(`bool(pattern)`), "button has Invoke pattern");
+ info("Calling Invoke on button");
+ // The button will get focus when it is clicked.
+ let focused = waitForEvent(EVENT_FOCUS, "button");
+ // The UIA -> IA2 proxy doesn't fire the Invoked event.
+ if (gIsUiaEnabled) {
+ await setUpWaitForUiaEvent("Invoke_Invoked", "button");
+ }
+ await runPython(`pattern.Invoke()`);
+ await focused;
+ ok(true, "button got focus");
+ if (gIsUiaEnabled) {
+ await waitForUiaEvent();
+ ok(true, "button got Invoked event");
+ }
+
+ await testPatternAbsent("p", "Invoke");
+ // The Microsoft IA2 -> UIA proxy doesn't follow Microsoft's own rules.
+ if (gIsUiaEnabled) {
+ // Check boxes expose the Toggle pattern, so they should not expose the
+ // Invoke pattern.
+ await testPatternAbsent("checkbox", "Invoke");
+ }
+ }
+);
+
+/**
+ * Test the Toggle pattern.
+ */
+addUiaTask(
+ `
+<input id="checkbox" type="checkbox" checked>
+<button id="toggleButton" aria-pressed="false">toggle</button>
+<button id="button">button</button>
+<p id="p">p</p>
+
+<script>
+ // When checkbox is clicked and it is not checked, make it indeterminate.
+ document.getElementById("checkbox").addEventListener("click", evt => {
+ // Within the event listener, .checked is reversed and you can't set
+ // .indeterminate. Work around this by deferring and handling the changes
+ // ourselves.
+ evt.preventDefault();
+ const target = evt.target;
+ setTimeout(() => {
+ if (target.checked) {
+ target.checked = false;
+ } else {
+ target.indeterminate = true;
+ }
+ }, 0);
+ });
+
+ // When toggleButton is clicked, set aria-pressed to true.
+ document.getElementById("toggleButton").addEventListener("click", evt => {
+ evt.target.ariaPressed = "true";
+ });
+</script>
+ `,
+ async function testToggle() {
+ await definePyVar("doc", `getDocUia()`);
+ await assignPyVarToUiaWithId("checkbox");
+ await definePyVar("pattern", `getUiaPattern(checkbox, "Toggle")`);
+ ok(await runPython(`bool(pattern)`), "checkbox has Toggle pattern");
+ is(
+ await runPython(`pattern.CurrentToggleState`),
+ ToggleState_On,
+ "checkbox has ToggleState_On"
+ );
+ // The IA2 -> UIA proxy doesn't fire ToggleState prop change events.
+ if (gIsUiaEnabled) {
+ info("Calling Toggle on checkbox");
+ await setUpWaitForUiaPropEvent("ToggleToggleState", "checkbox");
+ await runPython(`pattern.Toggle()`);
+ await waitForUiaEvent();
+ ok(true, "Got ToggleState prop change event on checkbox");
+ is(
+ await runPython(`pattern.CurrentToggleState`),
+ ToggleState_Off,
+ "checkbox has ToggleState_Off"
+ );
+ info("Calling Toggle on checkbox");
+ await setUpWaitForUiaPropEvent("ToggleToggleState", "checkbox");
+ await runPython(`pattern.Toggle()`);
+ await waitForUiaEvent();
+ ok(true, "Got ToggleState prop change event on checkbox");
+ is(
+ await runPython(`pattern.CurrentToggleState`),
+ ToggleState_Indeterminate,
+ "checkbox has ToggleState_Indeterminate"
+ );
+ }
+
+ await assignPyVarToUiaWithId("toggleButton");
+ await definePyVar("pattern", `getUiaPattern(toggleButton, "Toggle")`);
+ ok(await runPython(`bool(pattern)`), "toggleButton has Toggle pattern");
+ is(
+ await runPython(`pattern.CurrentToggleState`),
+ ToggleState_Off,
+ "toggleButton has ToggleState_Off"
+ );
+ if (gIsUiaEnabled) {
+ info("Calling Toggle on toggleButton");
+ await setUpWaitForUiaPropEvent("ToggleToggleState", "toggleButton");
+ await runPython(`pattern.Toggle()`);
+ await waitForUiaEvent();
+ ok(true, "Got ToggleState prop change event on toggleButton");
+ is(
+ await runPython(`pattern.CurrentToggleState`),
+ ToggleState_On,
+ "toggleButton has ToggleState_Off"
+ );
+ }
+
+ await testPatternAbsent("button", "Toggle");
+ await testPatternAbsent("p", "Toggle");
+ }
+);
+
+/**
+ * Test the ExpandCollapse pattern.
+ */
+addUiaTask(
+ `
+<details>
+ <summary id="summary">summary</summary>
+ details
+</details>
+<button id="popup" aria-haspopup="true">popup</button>
+<button id="button">button</button>
+<script>
+ // When popup is clicked, set aria-expanded to true.
+ document.getElementById("popup").addEventListener("click", evt => {
+ evt.target.ariaExpanded = "true";
+ });
+</script>
+ `,
+ async function testExpandCollapse() {
+ await definePyVar("doc", `getDocUia()`);
+ await assignPyVarToUiaWithId("summary");
+ await definePyVar("pattern", `getUiaPattern(summary, "ExpandCollapse")`);
+ ok(await runPython(`bool(pattern)`), "summary has ExpandCollapse pattern");
+ is(
+ await runPython(`pattern.CurrentExpandCollapseState`),
+ ExpandCollapseState_Collapsed,
+ "summary has ExpandCollapseState_Collapsed"
+ );
+ // The IA2 -> UIA proxy doesn't fire ToggleState prop change events, nor
+ // does it fail when Expand/Collapse is called on a control which is
+ // already in the desired state.
+ if (gIsUiaEnabled) {
+ info("Calling Expand on summary");
+ await setUpWaitForUiaPropEvent(
+ "ExpandCollapseExpandCollapseState",
+ "summary"
+ );
+ await runPython(`pattern.Expand()`);
+ await waitForUiaEvent();
+ ok(
+ true,
+ "Got ExpandCollapseExpandCollapseState prop change event on summary"
+ );
+ is(
+ await runPython(`pattern.CurrentExpandCollapseState`),
+ ExpandCollapseState_Expanded,
+ "summary has ExpandCollapseState_Expanded"
+ );
+ info("Calling Expand on summary");
+ await testPythonRaises(`pattern.Expand()`, "Expand on summary failed");
+ info("Calling Collapse on summary");
+ await setUpWaitForUiaPropEvent(
+ "ExpandCollapseExpandCollapseState",
+ "summary"
+ );
+ await runPython(`pattern.Collapse()`);
+ await waitForUiaEvent();
+ ok(
+ true,
+ "Got ExpandCollapseExpandCollapseState prop change event on summary"
+ );
+ is(
+ await runPython(`pattern.CurrentExpandCollapseState`),
+ ExpandCollapseState_Collapsed,
+ "summary has ExpandCollapseState_Collapsed"
+ );
+ info("Calling Collapse on summary");
+ await testPythonRaises(
+ `pattern.Collapse()`,
+ "Collapse on summary failed"
+ );
+ }
+
+ await assignPyVarToUiaWithId("popup");
+ // Initially, popup has aria-haspopup but not aria-expanded. That should
+ // be exposed as collapsed.
+ await definePyVar("pattern", `getUiaPattern(popup, "ExpandCollapse")`);
+ ok(await runPython(`bool(pattern)`), "popup has ExpandCollapse pattern");
+ // The IA2 -> UIA proxy doesn't expose ExpandCollapseState_Collapsed for
+ // aria-haspopup without aria-expanded.
+ if (gIsUiaEnabled) {
+ is(
+ await runPython(`pattern.CurrentExpandCollapseState`),
+ ExpandCollapseState_Collapsed,
+ "popup has ExpandCollapseState_Collapsed"
+ );
+ info("Calling Expand on popup");
+ await setUpWaitForUiaPropEvent(
+ "ExpandCollapseExpandCollapseState",
+ "popup"
+ );
+ await runPython(`pattern.Expand()`);
+ await waitForUiaEvent();
+ ok(
+ true,
+ "Got ExpandCollapseExpandCollapseState prop change event on popup"
+ );
+ is(
+ await runPython(`pattern.CurrentExpandCollapseState`),
+ ExpandCollapseState_Expanded,
+ "popup has ExpandCollapseState_Expanded"
+ );
+ }
+
+ await testPatternAbsent("button", "ExpandCollapse");
+ }
+);
+
+/**
+ * Test the ScrollItem pattern.
+ */
+addUiaTask(
+ `
+<hr style="height: 100vh;">
+<button id="button">button</button>
+ `,
+ async function testScrollItem(browser, docAcc) {
+ await definePyVar("doc", `getDocUia()`);
+ await assignPyVarToUiaWithId("button");
+ await definePyVar("pattern", `getUiaPattern(button, "ScrollItem")`);
+ ok(await runPython(`bool(pattern)`), "button has ScrollItem pattern");
+ const button = findAccessibleChildByID(docAcc, "button");
+ testStates(button, STATE_OFFSCREEN);
+ info("Calling ScrollIntoView on button");
+ // UIA doesn't have an event for this.
+ let scrolled = waitForEvent(EVENT_SCROLLING_END, docAcc);
+ await runPython(`pattern.ScrollIntoView()`);
+ await scrolled;
+ ok(true, "Document scrolled");
+ testStates(button, 0, 0, STATE_OFFSCREEN);
+ }
+);
+
+/**
+ * Test the Value pattern.
+ */
+addUiaTask(
+ `
+<input id="text" value="before">
+<input id="textRo" readonly value="textRo">
+<input id="textDis" disabled value="textDis">
+<select id="select"><option selected>a</option><option>b</option></select>
+<progress id="progress" value="0.5"></progress>
+<input id="range" type="range" aria-valuetext="02:00:00">
+<a id="link" href="https://example.com/">Link</a>
+<div id="ariaTextbox" contenteditable role="textbox">before</div>
+<button id="button">button</button>
+ `,
+ async function testValue() {
+ await definePyVar("doc", `getDocUia()`);
+ await assignPyVarToUiaWithId("text");
+ await definePyVar("pattern", `getUiaPattern(text, "Value")`);
+ ok(await runPython(`bool(pattern)`), "text has Value pattern");
+ ok(
+ !(await runPython(`pattern.CurrentIsReadOnly`)),
+ "text has IsReadOnly false"
+ );
+ is(
+ await runPython(`pattern.CurrentValue`),
+ "before",
+ "text has correct Value"
+ );
+ info("SetValue on text");
+ await setUpWaitForUiaPropEvent("ValueValue", "text");
+ await runPython(`pattern.SetValue("after")`);
+ await waitForUiaEvent();
+ is(
+ await runPython(`pattern.CurrentValue`),
+ "after",
+ "text has correct Value"
+ );
+
+ await assignPyVarToUiaWithId("textRo");
+ await definePyVar("pattern", `getUiaPattern(textRo, "Value")`);
+ ok(await runPython(`bool(pattern)`), "textRo has Value pattern");
+ ok(
+ await runPython(`pattern.CurrentIsReadOnly`),
+ "textRo has IsReadOnly true"
+ );
+ is(
+ await runPython(`pattern.CurrentValue`),
+ "textRo",
+ "textRo has correct Value"
+ );
+ info("SetValue on textRo");
+ await testPythonRaises(
+ `pattern.SetValue("after")`,
+ "SetValue on textRo failed"
+ );
+
+ await assignPyVarToUiaWithId("textDis");
+ await definePyVar("pattern", `getUiaPattern(textDis, "Value")`);
+ ok(await runPython(`bool(pattern)`), "textDis has Value pattern");
+ ok(
+ !(await runPython(`pattern.CurrentIsReadOnly`)),
+ "textDis has IsReadOnly false"
+ );
+ is(
+ await runPython(`pattern.CurrentValue`),
+ "textDis",
+ "textDis has correct Value"
+ );
+ // The IA2 -> UIA proxy doesn't fail SetValue for a disabled element.
+ if (gIsUiaEnabled) {
+ info("SetValue on textDis");
+ await testPythonRaises(
+ `pattern.SetValue("after")`,
+ "SetValue on textDis failed"
+ );
+ }
+
+ await assignPyVarToUiaWithId("select");
+ await definePyVar("pattern", `getUiaPattern(select, "Value")`);
+ ok(await runPython(`bool(pattern)`), "select has Value pattern");
+ ok(
+ !(await runPython(`pattern.CurrentIsReadOnly`)),
+ "select has IsReadOnly false"
+ );
+ is(
+ await runPython(`pattern.CurrentValue`),
+ "a",
+ "select has correct Value"
+ );
+ info("SetValue on select");
+ await testPythonRaises(
+ `pattern.SetValue("b")`,
+ "SetValue on select failed"
+ );
+
+ await assignPyVarToUiaWithId("progress");
+ await definePyVar("pattern", `getUiaPattern(progress, "Value")`);
+ ok(await runPython(`bool(pattern)`), "progress has Value pattern");
+ // Gecko a11y doesn't treat progress bars as read only, but it probably
+ // should.
+ todo(
+ await runPython(`pattern.CurrentIsReadOnly`),
+ "progress has IsReadOnly true"
+ );
+ is(
+ await runPython(`pattern.CurrentValue`),
+ "50%",
+ "progress has correct Value"
+ );
+ info("SetValue on progress");
+ await testPythonRaises(
+ `pattern.SetValue("60%")`,
+ "SetValue on progress failed"
+ );
+
+ await assignPyVarToUiaWithId("range");
+ await definePyVar("pattern", `getUiaPattern(range, "Value")`);
+ ok(await runPython(`bool(pattern)`), "range has Value pattern");
+ is(
+ await runPython(`pattern.CurrentValue`),
+ "02:00:00",
+ "range has correct Value"
+ );
+
+ await assignPyVarToUiaWithId("link");
+ await definePyVar("pattern", `getUiaPattern(link, "Value")`);
+ ok(await runPython(`bool(pattern)`), "link has Value pattern");
+ is(
+ await runPython(`pattern.CurrentValue`),
+ "https://example.com/",
+ "link has correct Value"
+ );
+
+ await assignPyVarToUiaWithId("ariaTextbox");
+ await definePyVar("pattern", `getUiaPattern(ariaTextbox, "Value")`);
+ ok(await runPython(`bool(pattern)`), "ariaTextbox has Value pattern");
+ ok(
+ !(await runPython(`pattern.CurrentIsReadOnly`)),
+ "ariaTextbox has IsReadOnly false"
+ );
+ is(
+ await runPython(`pattern.CurrentValue`),
+ "before",
+ "ariaTextbox has correct Value"
+ );
+ info("SetValue on ariaTextbox");
+ await setUpWaitForUiaPropEvent("ValueValue", "ariaTextbox");
+ await runPython(`pattern.SetValue("after")`);
+ await waitForUiaEvent();
+ is(
+ await runPython(`pattern.CurrentValue`),
+ "after",
+ "ariaTextbox has correct Value"
+ );
+
+ await testPatternAbsent("button", "Value");
+ }
+);