Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / bindings / test / test_async_iterable.html
blobed47ef4c9db0cfb4089504d65ab91e1a4fb7cee6
1 <!-- Any copyright is dedicated to the Public Domain.
2 - http://creativecommons.org/publicdomain/zero/1.0/ -->
3 <!DOCTYPE HTML>
4 <html>
5 <head>
6 <title>Test Async Iterable Interface</title>
7 <script src="/tests/SimpleTest/SimpleTest.js"></script>
8 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
9 </head>
10 <body>
11 <script class="testbody" type="application/javascript">
13 add_task(async function init() {
14 await SpecialPowers.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]});
15 });
17 const singleValues = Array(10).fill(0).map((_, i) => i * 9 % 7);
19 async function check_single_result_values(values, multiplier = 1) {
20 is(values.length, 10, `AsyncIterableSingle: should return 10 elements`);
21 for (let i = 0; i < 10; i++) {
22 let expected = singleValues[i] * multiplier;
23 is(values[i], expected,
24 `AsyncIterableSingle: should be ${expected}, get ${values[i]}`);
28 async function check_single_result(itr, multiplier = 1) {
29 let values = [];
30 for await (let v of itr) {
31 values.push(v);
33 check_single_result_values(values, multiplier);
36 async function test_data_single() {
37 info(`AsyncIterableSingle: Testing simple iterable creation and functionality`);
39 // eslint-disable-next-line no-undef
40 let itr = new TestInterfaceAsyncIterableSingle({ failToInit: true });
41 let initFailed = false;
42 try {
43 itr.values();
44 } catch (e) {
45 initFailed = true;
47 ok(initFailed,
48 "AsyncIterableSingle: A failure in asynchronous iterator initialization " +
49 "steps should propagate to the caller of the asynchronous iterator's " +
50 "constructor.");
52 // eslint-disable-next-line no-undef
53 itr = new TestInterfaceAsyncIterableSingle();
54 is(itr.values, itr[Symbol.asyncIterator],
55 `AsyncIterableSingle: Should be using @@asyncIterator for 'values'`);
57 await check_single_result(itr);
58 await check_single_result(itr.values());
60 // eslint-disable-next-line no-undef
61 itr = new TestInterfaceAsyncIterableSingleWithArgs();
62 is(itr.values, itr[Symbol.asyncIterator],
63 `AsyncIterableSingleWithArgs: Should be using @@asyncIterator for 'values'`);
65 await check_single_result(itr, 1);
66 await check_single_result(itr.values({ multiplier: 2 }), 2);
68 // eslint-disable-next-line no-undef
69 itr = new TestInterfaceAsyncIterableSingle();
70 let itrValues = itr.values();
71 let values = [];
72 for (let i = 0; i < 10; ++i) {
73 values.push(itrValues.next());
75 check_single_result_values(await Promise.all(values).then(v => v.map(w => w.value)));
77 // Test that there is only one ongoing promise at a time.
78 // Async iterables return a promise that is then resolved with the iterator
79 // value. We create an array of unresolved promises here, one promise for
80 // every result that we expect from the iterator. We pass that array of
81 // promises to the .value() method of the
82 // TestInterfaceAsyncIterableSingleWithArgs, and it will chain the resolving
83 // of each resulting iterator value on the corresponding promise from this
84 // array. We then resolve the promises in the array one by one in reverse
85 // order. This tries to make sure that the iterator always resolves the
86 // promises in the order of iteration.
87 let unblockers = [];
88 let blockingPromises = [];
89 for (let i = 0; i < 10; ++i) {
90 let unblocker;
91 let promise = new Promise((resolve) => {
92 unblocker = resolve;
93 });
94 unblockers.push(unblocker);
95 blockingPromises.push(promise);
98 // eslint-disable-next-line no-undef
99 itr = new TestInterfaceAsyncIterableSingleWithArgs();
100 itrValues = itr.values({ blockingPromises });
101 values = [];
102 for (let i = 0; i < 10; ++i) {
103 values.push(itrValues.next());
105 unblockers.reverse();
106 for (let unblocker of unblockers) {
107 unblocker();
110 check_single_result_values(await Promise.all(values).then(v => v.map(w => w.value)));
112 // eslint-disable-next-line no-undef
113 itr = new TestInterfaceAsyncIterableSingleWithArgs();
115 let callCount = itr.returnCallCount;
117 let i = 0;
118 for await (let v of itr) {
119 if (++i > 1) {
120 break;
122 values.push(v);
125 is(itr.returnCallCount, callCount + 1,
126 `AsyncIterableSingle: breaking out of for-await-of loop should call "return"`);
127 is(itr.returnLastCalledWith, undefined,
128 `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm should be called with the argument that was passed in.`);
130 // eslint-disable-next-line no-undef
131 itr = new TestInterfaceAsyncIterableSingleWithArgs();
133 async function * yieldFromIterator () {
134 yield * itr
137 let yieldingIterator = yieldFromIterator();
139 let result = await yieldingIterator.next();
140 is(result.value, singleValues[0],
141 `AsyncIterableSingle: should be ${singleValues[0]}, get ${result.value}`);
142 result = await yieldingIterator.next();
143 is(result.value, singleValues[1],
144 `AsyncIterableSingle: should be ${singleValues[1]}, get ${result.value}`);
146 result = await yieldingIterator.return("abcd");
147 is(typeof result, "object",
148 `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
149 is(result.done, true,
150 `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
151 is(result.value, "abcd",
152 `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`);
153 is(itr.returnLastCalledWith, "abcd",
154 `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm should be called with the argument that was passed in.`);
156 result = await yieldingIterator.return("efgh");
157 is(typeof result, "object",
158 `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
159 is(result.done, true,
160 `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
161 is(result.value, "efgh",
162 `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`);
163 is(itr.returnLastCalledWith, "abcd",
164 `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm shouldn't be called if the iterator's 'is finished' flag is true already.`);
166 // eslint-disable-next-line no-undef
167 itr = new TestInterfaceAsyncIterableSingleWithArgs();
168 itrValues = itr.values({ failNextAfter: 1 });
169 await itrValues.next().then(({ value, done }) => {
170 is(value, singleValues[0], "First value is correct");
171 ok(!done, "Expecting more values");
172 return itrValues.next();
173 }).then(() => {
174 ok(false, "Second call to next() should convert failure to a rejected promise.");
175 return itrValues.next();
176 }).catch(() => {
177 ok(true, "Second call to next() should convert failure to a rejected promise.");
178 return itrValues.next();
179 }).then(({ done }) => {
180 ok(done, "An earlier failure in next() should set the async iterator's 'is finished' flag to true.");
181 }).catch(() => {
182 ok(false, "An earlier failure in next() shouldn't cause subsequent calls to return a rejected promise.");
185 // eslint-disable-next-line no-undef
186 itr = new TestInterfaceAsyncIterableSingleWithArgs();
187 itrValues = itr.values({ throwFromNext: true });
188 await itrValues.next().then(() => {
189 ok(false, "Should have rejected from the exception");
190 }).catch(() => {
191 ok(true, "Should have rejected from the exception");
194 // eslint-disable-next-line no-undef
195 itr = new TestInterfaceAsyncIterableSingleWithArgs();
196 itrValues = itr.values({ throwFromReturn: () => { throw new DOMException("Throw from return", "InvalidStateError"); } });
197 await itrValues.return().then(() => {
198 ok(false, "Should have rejected from the exception");
199 }).catch(() => {
200 ok(true, "Should have rejected from the exception");
204 async function test_data_double() {
205 info(`AsyncIterableDouble: Testing simple iterable creation and functionality`);
207 // eslint-disable-next-line no-undef
208 let itr = new TestInterfaceAsyncIterableDouble();
209 is(itr.entries, itr[Symbol.asyncIterator],
210 `AsyncIterableDouble: Should be using @@asyncIterator for 'entries'`);
212 let elements = [["a", "b"], ["c", "d"], ["e", "f"]];
213 let key_itr = itr.keys();
214 let value_itr = itr.values();
215 let entries_itr = itr.entries();
216 let key = await key_itr.next();
217 let value = await value_itr.next();
218 let entry = await entries_itr.next();
219 for (let i = 0; i < 3; ++i) {
220 is(key.value, elements[i][0], `AsyncIterableDouble: Key.value should be ${elements[i][0]}, got ${key.value}`);
221 is(key.done, false, `AsyncIterableDouble: Key.done should be false, got ${key.done}`);
222 is(value.value, elements[i][1], `AsyncIterableDouble: Value.value should be ${elements[i][1]}, got ${value.value}`);
223 is(value.done, false, `AsyncIterableDouble: Value.done should be false, got ${value.done}`);
224 is(entry.value[0], elements[i][0], `AsyncIterableDouble: Entry.value[0] should be ${elements[i][0]}, got ${entry.value[0]}`);
225 is(entry.value[1], elements[i][1], `AsyncIterableDouble: Entry.value[1] should be ${elements[i][1]}, got ${entry.value[1]}`);
226 is(entry.done, false, `AsyncIterableDouble: Entry.done should be false, got ${entry.done}`);
228 key = await key_itr.next();
229 value = await value_itr.next();
230 entry = await entries_itr.next();
232 is(key.value, undefined, `AsyncIterableDouble: Key.value should be ${undefined}, got ${key.value}`);
233 is(key.done, true, `AsyncIterableDouble: Key.done should be true, got ${key.done}`);
234 is(value.value, undefined, `AsyncIterableDouble: Value.value should be ${undefined}, got ${value.value}`);
235 is(value.done, true, `AsyncIterableDouble: Value.done should be true, got ${value.done}`);
236 is(entry.value, undefined, `AsyncIterableDouble: Entry.value should be ${undefined}, got ${entry.value}`);
237 is(entry.done, true, `AsyncIterableDouble: Entry.done should be true, got ${entry.done}`);
239 let idx = 0;
240 for await (let [itrkey, itrvalue] of itr) {
241 is(itrkey, elements[idx][0], `AsyncIterableDouble: Looping at ${idx} should have key ${elements[idx][0]}, got ${key}`);
242 is(itrvalue, elements[idx][1], `AsyncIterableDouble: Looping at ${idx} should have value ${elements[idx][1]}, got ${value}`);
243 ++idx;
245 is(idx, 3, `AsyncIterableDouble: Should have 3 loops of for-await-of, got ${idx}`);
248 async function test_data_double_union() {
249 info(`AsyncIterableDoubleUnion: Testing simple iterable creation and functionality`);
251 // eslint-disable-next-line no-undef
252 let itr = new TestInterfaceAsyncIterableDoubleUnion();
253 is(itr.entries, itr[Symbol.asyncIterator],
254 `AsyncIterableDoubleUnion: Should be using @@asyncIterator for 'entries'`);
256 let elements = [["long", 1], ["string", "a"]];
257 let key_itr = itr.keys();
258 let value_itr = itr.values();
259 let entries_itr = itr.entries();
260 let key = await key_itr.next();
261 let value = await value_itr.next();
262 let entry = await entries_itr.next();
263 for (let i = 0; i < 2; ++i) {
264 is(key.value, elements[i][0], `AsyncIterableDoubleUnion: Key.value should be ${elements[i][0]}, got ${key.value}`);
265 is(key.done, false, `AsyncIterableDoubleUnion: Key.done should be false, got ${key.done}`);
266 is(value.value, elements[i][1], `AsyncIterableDoubleUnion: Value.value should be ${elements[i][1]}, got ${value.value}`);
267 is(value.done, false, `AsyncIterableDoubleUnion: Value.done should be false, got ${value.done}`);
268 is(entry.value[0], elements[i][0], `AsyncIterableDoubleUnion: Entry.value[0] should be ${elements[i][0]}, got ${entry.value[0]}`);
269 is(entry.value[1], elements[i][1], `AsyncIterableDoubleUnion: Entry.value[1] should be ${elements[i][1]}, got ${entry.value[1]}`);
270 is(entry.done, false, `AsyncIterableDoubleUnion: Entry.done should be false, got ${entry.done}`);
272 key = await key_itr.next();
273 value = await value_itr.next();
274 entry = await entries_itr.next();
276 is(key.value, undefined, `AsyncIterableDoubleUnion: Key.value should be ${undefined}, got ${key.value}`);
277 is(key.done, true, `AsyncIterableDoubleUnion: Key.done should be true, got ${key.done}`);
278 is(value.value, undefined, `AsyncIterableDoubleUnion: Value.value should be ${undefined}, got ${value.value}`);
279 is(value.done, true, `AsyncIterableDoubleUnion: Value.done should be true, got ${value.done}`);
280 is(entry.value, undefined, `AsyncIterableDoubleUnion: Entry.value should be ${undefined}, got ${entry.value}`);
281 is(entry.done, true, `AsyncIterableDoubleUnion: Entry.done should be true, got ${entry.done}`);
283 let idx = 0;
284 for await (let [itrkey, itrvalue] of itr) {
285 is(itrkey, elements[idx][0], `AsyncIterableDoubleUnion: Looping at ${idx} should have key ${elements[idx][0]}, got ${key}`);
286 is(itrvalue, elements[idx][1], `AsyncIterableDoubleUnion: Looping at ${idx} should have value ${elements[idx][1]}, got ${value}`);
287 ++idx;
289 is(idx, 2, `AsyncIterableDoubleUnion: Should have 2 loops of for-await-of, got ${idx}`);
292 add_task(async function do_tests() {
293 await test_data_single();
294 await test_data_double();
295 await test_data_double_union();
298 </script>
299 </body>
300 </html>