summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/navigation-api/ordering-and-transition/resources/helpers.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/navigation-api/ordering-and-transition/resources/helpers.mjs')
-rw-r--r--testing/web-platform/tests/navigation-api/ordering-and-transition/resources/helpers.mjs193
1 files changed, 193 insertions, 0 deletions
diff --git a/testing/web-platform/tests/navigation-api/ordering-and-transition/resources/helpers.mjs b/testing/web-platform/tests/navigation-api/ordering-and-transition/resources/helpers.mjs
new file mode 100644
index 0000000000..341befc105
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/ordering-and-transition/resources/helpers.mjs
@@ -0,0 +1,193 @@
+const variants = new Set((new URLSearchParams(location.search)).keys());
+
+export function hasVariant(name) {
+ return variants.has(name);
+}
+
+export class Recorder {
+ #events = [];
+ #errors = [];
+ #navigationAPI;
+ #domExceptionConstructor;
+ #location;
+ #skipCurrentChange;
+ #finalExpectedEvent;
+ #finalExpectedEventCount;
+ #currentFinalEventCount = 0;
+
+ #readyToAssertResolve;
+ #readyToAssertPromise = new Promise(resolve => { this.#readyToAssertResolve = resolve; });
+
+ constructor({ window = self, skipCurrentChange = false, finalExpectedEvent, finalExpectedEventCount = 1 }) {
+ assert_equals(typeof finalExpectedEvent, "string", "Must pass a string for finalExpectedEvent");
+
+ this.#navigationAPI = window.navigation;
+ this.#domExceptionConstructor = window.DOMException;
+ this.#location = window.location;
+
+ this.#skipCurrentChange = skipCurrentChange;
+ this.#finalExpectedEvent = finalExpectedEvent;
+ this.#finalExpectedEventCount = finalExpectedEventCount;
+ }
+
+ setUpNavigationAPIListeners() {
+ this.#navigationAPI.addEventListener("navigate", e => {
+ this.record("navigate");
+
+ e.signal.addEventListener("abort", () => {
+ this.recordWithError("AbortSignal abort", e.signal.reason);
+ });
+ });
+
+ this.#navigationAPI.addEventListener("navigateerror", e => {
+ this.recordWithError("navigateerror", e.error);
+
+ this.#navigationAPI.transition?.finished.then(
+ () => this.record("transition.finished fulfilled"),
+ err => this.recordWithError("transition.finished rejected", err)
+ );
+ });
+
+ this.#navigationAPI.addEventListener("navigatesuccess", () => {
+ this.record("navigatesuccess");
+
+ this.#navigationAPI.transition?.finished.then(
+ () => this.record("transition.finished fulfilled"),
+ err => this.recordWithError("transition.finished rejected", err)
+ );
+ });
+
+ if (!this.#skipCurrentChange) {
+ this.#navigationAPI.addEventListener("currententrychange", () => this.record("currententrychange"));
+ }
+ }
+
+ setUpResultListeners(result, suffix = "") {
+ result.committed.then(
+ () => this.record(`committed fulfilled${suffix}`),
+ err => this.recordWithError(`committed rejected${suffix}`, err)
+ );
+
+ result.finished.then(
+ () => this.record(`finished fulfilled${suffix}`),
+ err => this.recordWithError(`finished rejected${suffix}`, err)
+ );
+ }
+
+ record(name) {
+ const transitionProps = this.#navigationAPI.transition === null ? null : {
+ from: this.#navigationAPI.transition.from,
+ navigationType: this.#navigationAPI.transition.navigationType
+ };
+
+ this.#events.push({ name, location: this.#location.hash, transitionProps });
+
+ if (name === this.#finalExpectedEvent && ++this.#currentFinalEventCount === this.#finalExpectedEventCount) {
+ this.#readyToAssertResolve();
+ }
+ }
+
+ recordWithError(name, errorObject) {
+ this.record(name);
+ this.#errors.push({ name, errorObject });
+ }
+
+ get readyToAssert() {
+ return this.#readyToAssertPromise;
+ }
+
+ // Usage:
+ // recorder.assert([
+ // /* event name, location.hash value, navigation.transition properties */
+ // ["currententrychange", "", null],
+ // ["committed fulfilled", "#1", { from, navigationType }],
+ // ...
+ // ]);
+ //
+ // The array format is to avoid repitition at the call site, but I recommend
+ // you document it like above.
+ //
+ // This will automatically also assert that any error objects recorded are
+ // equal to each other. Use the other assert functions to check the actual
+ // contents of the error objects.
+ assert(expectedAsArray) {
+ if (this.#skipCurrentChange) {
+ expectedAsArray = expectedAsArray.filter(expected => expected[0] !== "currententrychange");
+ }
+
+ // Doing this up front gives nicer error messages because
+ // assert_array_equals is nice.
+ const recordedNames = this.#events.map(e => e.name);
+ const expectedNames = expectedAsArray.map(e => e[0]);
+ assert_array_equals(recordedNames, expectedNames);
+
+ for (let i = 0; i < expectedAsArray.length; ++i) {
+ const recorded = this.#events[i];
+ const expected = expectedAsArray[i];
+
+ assert_equals(
+ recorded.location,
+ expected[1],
+ `event ${i} (${recorded.name}): location.hash value`
+ );
+
+ if (expected[2] === null) {
+ assert_equals(
+ recorded.transitionProps,
+ null,
+ `event ${i} (${recorded.name}): navigation.transition expected to be null`
+ );
+ } else {
+ assert_not_equals(
+ recorded.transitionProps,
+ null,
+ `event ${i} (${recorded.name}): navigation.transition expected not to be null`
+ );
+ assert_equals(
+ recorded.transitionProps.from,
+ expected[2].from,
+ `event ${i} (${recorded.name}): navigation.transition.from`
+ );
+ assert_equals(
+ recorded.transitionProps.navigationType,
+ expected[2].navigationType,
+ `event ${i} (${recorded.name}): navigation.transition.navigationType`
+ );
+ }
+ }
+
+ if (this.#errors.length > 1) {
+ for (let i = 1; i < this.#errors.length; ++i) {
+ assert_equals(
+ this.#errors[i].errorObject,
+ this.#errors[0].errorObject,
+ `error objects must match: error object for ${this.#errors[i].name} did not match the one for ${this.#errors[0].name}`
+ );
+ }
+ }
+ }
+
+ assertErrorsAreAbortErrors() {
+ assert_greater_than(
+ this.#errors.length,
+ 0,
+ "No errors were recorded but assertErrorsAreAbortErrors() was called"
+ );
+
+ // Assume assert() has been called so all error objects are the same.
+ const { errorObject } = this.#errors[0];
+ assert_throws_dom("AbortError", this.#domExceptionConstructor, () => { throw errorObject; });
+ }
+
+ assertErrorsAre(expectedErrorObject) {
+ assert_greater_than(
+ this.#errors.length,
+ 0,
+ "No errors were recorded but assertErrorsAre() was called"
+ );
+
+ // Assume assert() has been called so all error objects are the same.
+ const { errorObject } = this.#errors[0];
+ assert_equals(errorObject, expectedErrorObject);
+ }
+}