summaryrefslogtreecommitdiffstats
path: root/accessible/tests/browser/e10s/browser_caching_actions.js
blob: 8bf3542a037394869e9fbdc1a3334888d666197f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/* 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";

const gClickEvents = ["mousedown", "mouseup", "click"];

const gActionDescrMap = {
  jump: "Jump",
  press: "Press",
  check: "Check",
  uncheck: "Uncheck",
  select: "Select",
  open: "Open",
  close: "Close",
  switch: "Switch",
  click: "Click",
  collapse: "Collapse",
  expand: "Expand",
  activate: "Activate",
  cycle: "Cycle",
  "click ancestor": "Click ancestor",
};

async function testActions(browser, docAcc, id, expectedActions, domEvents) {
  const acc = findAccessibleChildByID(docAcc, id);
  is(acc.actionCount, expectedActions.length, "Correct action count");

  let actionNames = [];
  let actionDescriptions = [];
  for (let i = 0; i < acc.actionCount; i++) {
    actionNames.push(acc.getActionName(i));
    actionDescriptions.push(acc.getActionDescription(i));
  }

  is(actionNames.join(","), expectedActions.join(","), "Correct action names");
  is(
    actionDescriptions.join(","),
    expectedActions.map(a => gActionDescrMap[a]).join(","),
    "Correct action descriptions"
  );

  if (!domEvents) {
    return;
  }

  // We need to set up the listener, and wait for the promise in two separate
  // content tasks.
  await invokeContentTask(browser, [id, domEvents], (_id, _domEvents) => {
    let promises = _domEvents.map(
      evtName =>
        new Promise(resolve => {
          const listener = e => {
            if (e.target.id == _id) {
              content.removeEventListener(evtName, listener);
              content.evtPromise = null;
              resolve(42);
            }
          };
          content.addEventListener(evtName, listener);
        })
    );
    content.evtPromise = Promise.all(promises);
  });

  acc.doAction(0);

  let eventFired = await invokeContentTask(browser, [], async () => {
    await content.evtPromise;
    return true;
  });

  ok(eventFired, `DOM events fired '${domEvents}'`);
}

addAccessibleTask(
  `<ul>
    <li id="li_clickable1" onclick="">Clickable list item</li>
    <li id="li_clickable2" onmousedown="">Clickable list item</li>
    <li id="li_clickable3" onmouseup="">Clickable list item</li>
  </ul>

  <img id="onclick_img" onclick=""
        src="http://example.com/a11y/accessible/tests/mochitest/moz.png">

  <a id="link1" href="#">linkable textleaf accessible</a>
  <div id="link2" onclick="">linkable textleaf accessible</div>

  <a id="link3" href="#">
    <img id="link3img" alt="image in link"
          src="http://example.com/a11y/accessible/tests/mochitest/moz.png">
  </a>

  <div>
    <label for="TextBox_t2" id="label1">
      <span>Explicit</span>
    </label>
    <input name="in2" id="TextBox_t2" type="text" maxlength="17">
  </div>

  <div onclick=""><p id="p_in_clickable_div">p in clickable div</p></div>
  `,
  async function (browser, docAcc) {
    is(docAcc.actionCount, 0, "Doc should not have any actions");

    const _testActions = async (id, expectedActions, domEvents) => {
      await testActions(browser, docAcc, id, expectedActions, domEvents);
    };

    await _testActions("li_clickable1", ["click"], gClickEvents);
    await _testActions("li_clickable2", ["click"], gClickEvents);
    await _testActions("li_clickable3", ["click"], gClickEvents);

    await _testActions("onclick_img", ["click"], gClickEvents);
    await _testActions("link1", ["jump"], gClickEvents);
    await _testActions("link2", ["click"], gClickEvents);
    await _testActions("link3", ["jump"], gClickEvents);
    await _testActions("link3img", ["click ancestor"], gClickEvents);
    await _testActions("label1", ["click"], gClickEvents);
    await _testActions("p_in_clickable_div", ["click ancestor"], gClickEvents);

    await invokeContentTask(browser, [], () => {
      content.document
        .getElementById("li_clickable1")
        .removeAttribute("onclick");
    });

    let acc = findAccessibleChildByID(docAcc, "li_clickable1");
    await untilCacheIs(() => acc.actionCount, 0, "li has no actions");
    let thrown = false;
    try {
      acc.doAction(0);
    } catch (e) {
      thrown = true;
    }
    ok(thrown, "doAction should throw exception");

    // Remove 'for' from label
    await invokeContentTask(browser, [], () => {
      content.document.getElementById("label1").removeAttribute("for");
    });
    acc = findAccessibleChildByID(docAcc, "label1");
    await untilCacheIs(() => acc.actionCount, 0, "label has no actions");
    thrown = false;
    try {
      acc.doAction(0);
      ok(false, "doAction should throw exception");
    } catch (e) {
      thrown = true;
    }
    ok(thrown, "doAction should throw exception");

    // Add 'longdesc' to image
    await invokeContentTask(browser, [], () => {
      content.document
        .getElementById("onclick_img")
        // eslint-disable-next-line @microsoft/sdl/no-insecure-url
        .setAttribute("longdesc", "http://example.com");
    });
    acc = findAccessibleChildByID(docAcc, "onclick_img");
    await untilCacheIs(() => acc.actionCount, 2, "img has 2 actions");
    await _testActions("onclick_img", ["click", "showlongdesc"]);

    // Remove 'onclick' from image with 'longdesc'
    await invokeContentTask(browser, [], () => {
      content.document.getElementById("onclick_img").removeAttribute("onclick");
    });
    acc = findAccessibleChildByID(docAcc, "onclick_img");
    await untilCacheIs(() => acc.actionCount, 1, "img has 1 actions");
    await _testActions("onclick_img", ["showlongdesc"]);

    // Remove 'href' from link and test linkable child
    const link1Acc = findAccessibleChildByID(docAcc, "link1");
    is(
      link1Acc.firstChild.getActionName(0),
      "click ancestor",
      "linkable child has click ancestor action"
    );
    await invokeContentTask(browser, [], () => {
      let link1 = content.document.getElementById("link1");
      link1.removeAttribute("href");
    });
    await untilCacheIs(() => link1Acc.actionCount, 0, "link has no actions");
    is(link1Acc.firstChild.actionCount, 0, "linkable child's actions removed");

    // Add a click handler to the body. Ensure it propagates to descendants.
    await invokeContentTask(browser, [], () => {
      content.document.body.onclick = () => {};
    });
    await untilCacheIs(() => docAcc.actionCount, 1, "Doc has 1 action");
    await _testActions("link1", ["click ancestor"]);

    await invokeContentTask(browser, [], () => {
      content.document.body.onclick = null;
    });
    await untilCacheIs(() => docAcc.actionCount, 0, "Doc has no actions");
    is(link1Acc.actionCount, 0, "link has no actions");

    // Add a click handler to the root element. Ensure it propagates to
    // descendants.
    await invokeContentTask(browser, [], () => {
      content.document.documentElement.onclick = () => {};
    });
    await untilCacheIs(() => docAcc.actionCount, 1, "Doc has 1 action");
    await _testActions("link1", ["click ancestor"]);
  },
  {
    chrome: true,
    topLevel: true,
    iframe: true,
    remoteIframe: true,
  }
);

/**
 * Test access key.
 */
addAccessibleTask(
  `
<button id="noKey">noKey</button>
<button id="key" accesskey="a">key</button>
  `,
  async function (browser, docAcc) {
    const noKey = findAccessibleChildByID(docAcc, "noKey");
    is(noKey.accessKey, "", "noKey has no accesskey");
    const key = findAccessibleChildByID(docAcc, "key");
    is(key.accessKey, MAC ? "⌃⌥a" : "Alt+Shift+a", "key has correct accesskey");

    info("Changing accesskey");
    await invokeContentTask(browser, [], () => {
      content.document.getElementById("key").accessKey = "b";
    });
    await untilCacheIs(
      () => key.accessKey,
      MAC ? "⌃⌥b" : "Alt+Shift+b",
      "Correct accesskey after change"
    );

    info("Removing accesskey");
    await invokeContentTask(browser, [], () => {
      content.document.getElementById("key").removeAttribute("accesskey");
    });
    await untilCacheIs(
      () => key.accessKey,
      "",
      "Empty accesskey after removal"
    );

    info("Adding accesskey");
    await invokeContentTask(browser, [], () => {
      content.document.getElementById("key").accessKey = "c";
    });
    await untilCacheIs(
      () => key.accessKey,
      MAC ? "⌃⌥c" : "Alt+Shift+c",
      "Correct accesskey after addition"
    );
  },
  {
    chrome: true,
    topLevel: true,
    iframe: false, // Bug 1796846
    remoteIframe: false, // Bug 1796846
  }
);