summaryrefslogtreecommitdiffstats
path: root/remote/shared/test/xpcshell/test_Sync.js
diff options
context:
space:
mode:
Diffstat (limited to 'remote/shared/test/xpcshell/test_Sync.js')
-rw-r--r--remote/shared/test/xpcshell/test_Sync.js389
1 files changed, 389 insertions, 0 deletions
diff --git a/remote/shared/test/xpcshell/test_Sync.js b/remote/shared/test/xpcshell/test_Sync.js
new file mode 100644
index 0000000000..ae9de72718
--- /dev/null
+++ b/remote/shared/test/xpcshell/test_Sync.js
@@ -0,0 +1,389 @@
+/* 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/. */
+
+const { setTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+
+const { AnimationFramePromise, Deferred, EventPromise, PollPromise } =
+ ChromeUtils.importESModule("chrome://remote/content/shared/Sync.sys.mjs");
+
+/**
+ * Mimic a DOM node for listening for events.
+ */
+class MockElement {
+ constructor() {
+ this.capture = false;
+ this.eventName = null;
+ this.func = null;
+ this.mozSystemGroup = false;
+ this.wantUntrusted = false;
+ this.untrusted = false;
+ }
+
+ addEventListener(name, func, options = {}) {
+ const { capture, mozSystemGroup, wantUntrusted } = options;
+
+ this.eventName = name;
+ this.func = func;
+ this.capture = capture ?? false;
+ this.mozSystemGroup = mozSystemGroup ?? false;
+ this.wantUntrusted = wantUntrusted ?? false;
+ }
+
+ click() {
+ if (this.func) {
+ const event = {
+ capture: this.capture,
+ mozSystemGroup: this.mozSystemGroup,
+ target: this,
+ type: this.eventName,
+ untrusted: this.untrusted,
+ wantUntrusted: this.wantUntrusted,
+ };
+ this.func(event);
+ }
+ }
+
+ dispatchEvent(event) {
+ if (this.wantUntrusted) {
+ this.untrusted = true;
+ }
+ this.click();
+ }
+
+ removeEventListener(name, func) {
+ this.capture = false;
+ this.eventName = null;
+ this.func = null;
+ this.mozSystemGroup = false;
+ this.untrusted = false;
+ this.wantUntrusted = false;
+ }
+}
+
+add_task(async function test_AnimationFramePromise() {
+ let called = false;
+ let win = {
+ requestAnimationFrame(callback) {
+ called = true;
+ callback();
+ },
+ };
+ await AnimationFramePromise(win);
+ ok(called);
+});
+
+add_task(async function test_AnimationFramePromiseAbortWhenWindowClosed() {
+ let win = {
+ closed: true,
+ requestAnimationFrame() {},
+ };
+ await AnimationFramePromise(win);
+});
+
+add_task(async function test_DeferredPending() {
+ const deferred = Deferred();
+ ok(deferred.pending);
+
+ deferred.resolve();
+ await deferred.promise;
+ ok(!deferred.pending);
+});
+
+add_task(async function test_DeferredRejected() {
+ const deferred = Deferred();
+
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ setTimeout(() => deferred.reject(new Error("foo")), 100);
+
+ try {
+ await deferred.promise;
+ ok(false);
+ } catch (e) {
+ ok(!deferred.pending);
+
+ ok(!deferred.fulfilled);
+ ok(deferred.rejected);
+ equal(e.message, "foo");
+ }
+});
+
+add_task(async function test_DeferredResolved() {
+ const deferred = Deferred();
+ ok(deferred.pending);
+
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ setTimeout(() => deferred.resolve("foo"), 100);
+
+ const result = await deferred.promise;
+ ok(!deferred.pending);
+
+ ok(deferred.fulfilled);
+ ok(!deferred.rejected);
+ equal(result, "foo");
+});
+
+add_task(async function test_EventPromise_subjectTypes() {
+ for (const subject of ["foo", 42, null, undefined, true, [], {}]) {
+ Assert.throws(() => new EventPromise(subject, "click"), /TypeError/);
+ }
+});
+
+add_task(async function test_EventPromise_eventNameTypes() {
+ const element = new MockElement();
+
+ for (const eventName of [42, null, undefined, true, [], {}]) {
+ Assert.throws(() => new EventPromise(element, eventName), /TypeError/);
+ }
+});
+
+add_task(async function test_EventPromise_subjectAndEventNameEvent() {
+ const element = new MockElement();
+
+ const clicked = new EventPromise(element, "click");
+ element.click();
+ const event = await clicked;
+
+ equal(element, event.target);
+});
+
+add_task(async function test_EventPromise_captureTypes() {
+ const element = new MockElement();
+
+ for (const capture of [null, "foo", 42, [], {}]) {
+ Assert.throws(
+ () => new EventPromise(element, "click", { capture }),
+ /TypeError/
+ );
+ }
+});
+
+add_task(async function test_EventPromise_captureEvent() {
+ const element = new MockElement();
+
+ for (const capture of [undefined, false, true]) {
+ const expectedCapture = capture ?? false;
+
+ const clicked = new EventPromise(element, "click", { capture });
+ element.click();
+ const event = await clicked;
+
+ equal(element, event.target);
+ equal(expectedCapture, event.capture);
+ }
+});
+
+add_task(async function test_EventPromise_checkFnTypes() {
+ const element = new MockElement();
+
+ for (const checkFn of ["foo", 42, true, [], {}]) {
+ Assert.throws(
+ () => new EventPromise(element, "click", { checkFn }),
+ /TypeError/
+ );
+ }
+});
+
+add_task(async function test_EventPromise_checkFnCallback() {
+ const element = new MockElement();
+
+ let count;
+ const data = [
+ { checkFn: null, expected_count: 0 },
+ { checkFn: undefined, expected_count: 0 },
+ {
+ checkFn: event => {
+ throw new Error("foo");
+ },
+ expected_count: 0,
+ },
+ { checkFn: event => count++ > 0, expected_count: 2 },
+ ];
+
+ for (const { checkFn, expected_count } of data) {
+ count = 0;
+
+ const clicked = new EventPromise(element, "click", { checkFn });
+ element.click();
+ element.click();
+ const event = await clicked;
+
+ equal(element, event.target);
+ equal(expected_count, count);
+ }
+});
+
+add_task(async function test_EventPromise_mozSystemGroupTypes() {
+ const element = new MockElement();
+
+ for (const mozSystemGroup of [null, "foo", 42, [], {}]) {
+ Assert.throws(
+ () => new EventPromise(element, "click", { mozSystemGroup }),
+ /TypeError/
+ );
+ }
+});
+
+add_task(async function test_EventPromise_mozSystemGroupEvent() {
+ const element = new MockElement();
+
+ for (const mozSystemGroup of [undefined, false, true]) {
+ const expectedMozSystemGroup = mozSystemGroup ?? false;
+
+ const clicked = new EventPromise(element, "click", { mozSystemGroup });
+ element.click();
+ const event = await clicked;
+
+ equal(element, event.target);
+ equal(expectedMozSystemGroup, event.mozSystemGroup);
+ }
+});
+
+add_task(async function test_EventPromise_wantUntrustedTypes() {
+ const element = new MockElement();
+
+ for (let wantUntrusted of [null, "foo", 42, [], {}]) {
+ Assert.throws(
+ () => new EventPromise(element, "click", { wantUntrusted }),
+ /TypeError/
+ );
+ }
+});
+
+add_task(async function test_EventPromise_wantUntrustedEvent() {
+ for (const wantUntrusted of [undefined, false, true]) {
+ let expected_untrusted = wantUntrusted ?? false;
+
+ const element = new MockElement();
+
+ const clicked = new EventPromise(element, "click", { wantUntrusted });
+ element.dispatchEvent(new CustomEvent("click", {}));
+ const event = await clicked;
+
+ equal(element, event.target);
+ equal(expected_untrusted, event.untrusted);
+ }
+});
+
+add_task(function test_executeSoon_callback() {
+ // executeSoon() is already defined for xpcshell in head.js. As such import
+ // our implementation into a custom namespace.
+ let sync = ChromeUtils.importESModule(
+ "chrome://remote/content/shared/Sync.sys.mjs"
+ );
+
+ for (let func of ["foo", null, true, [], {}]) {
+ Assert.throws(() => sync.executeSoon(func), /TypeError/);
+ }
+
+ let a;
+ sync.executeSoon(() => {
+ a = 1;
+ });
+ executeSoon(() => equal(1, a));
+});
+
+add_task(function test_PollPromise_funcTypes() {
+ for (let type of ["foo", 42, null, undefined, true, [], {}]) {
+ Assert.throws(() => new PollPromise(type), /TypeError/);
+ }
+ new PollPromise(() => {});
+ new PollPromise(function () {});
+});
+
+add_task(function test_PollPromise_timeoutTypes() {
+ for (let timeout of ["foo", true, [], {}]) {
+ Assert.throws(() => new PollPromise(() => {}, { timeout }), /TypeError/);
+ }
+ for (let timeout of [1.2, -1]) {
+ Assert.throws(() => new PollPromise(() => {}, { timeout }), /RangeError/);
+ }
+ for (let timeout of [null, undefined, 42]) {
+ new PollPromise(resolve => resolve(1), { timeout });
+ }
+});
+
+add_task(function test_PollPromise_intervalTypes() {
+ for (let interval of ["foo", null, true, [], {}]) {
+ Assert.throws(() => new PollPromise(() => {}, { interval }), /TypeError/);
+ }
+ for (let interval of [1.2, -1]) {
+ Assert.throws(() => new PollPromise(() => {}, { interval }), /RangeError/);
+ }
+ new PollPromise(() => {}, { interval: 42 });
+});
+
+add_task(async function test_PollPromise_retvalTypes() {
+ for (let typ of [true, false, "foo", 42, [], {}]) {
+ strictEqual(typ, await new PollPromise(resolve => resolve(typ)));
+ }
+});
+
+add_task(async function test_PollPromise_rethrowError() {
+ let nevals = 0;
+ let err;
+ try {
+ await PollPromise(() => {
+ ++nevals;
+ throw new Error();
+ });
+ } catch (e) {
+ err = e;
+ }
+ equal(1, nevals);
+ ok(err instanceof Error);
+});
+
+add_task(async function test_PollPromise_noTimeout() {
+ let nevals = 0;
+ await new PollPromise((resolve, reject) => {
+ ++nevals;
+ nevals < 100 ? reject() : resolve();
+ });
+ equal(100, nevals);
+});
+
+add_task(async function test_PollPromise_zeroTimeout() {
+ // run at least once when timeout is 0
+ let nevals = 0;
+ let start = new Date().getTime();
+ await new PollPromise(
+ (resolve, reject) => {
+ ++nevals;
+ reject();
+ },
+ { timeout: 0 }
+ );
+ let end = new Date().getTime();
+ equal(1, nevals);
+ less(end - start, 500);
+});
+
+add_task(async function test_PollPromise_timeoutElapse() {
+ let nevals = 0;
+ let start = new Date().getTime();
+ await new PollPromise(
+ (resolve, reject) => {
+ ++nevals;
+ reject();
+ },
+ { timeout: 100 }
+ );
+ let end = new Date().getTime();
+ lessOrEqual(nevals, 11);
+ greaterOrEqual(end - start, 100);
+});
+
+add_task(async function test_PollPromise_interval() {
+ let nevals = 0;
+ await new PollPromise(
+ (resolve, reject) => {
+ ++nevals;
+ reject();
+ },
+ { timeout: 100, interval: 100 }
+ );
+ equal(2, nevals);
+});