* Document incompatibility between playwright fixtures and MAS * another comment
125 lines
5.4 KiB
TypeScript
125 lines
5.4 KiB
TypeScript
/*
|
|
Copyright 2024-2025 New Vector Ltd.
|
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import { type Page } from "@playwright/test";
|
|
import { sample, uniqueId } from "lodash-es";
|
|
|
|
// We want to avoid using `mergeTests` in index.ts because it drops useful type
|
|
// information about the fixtures. Instead, we add `services` into our fixture
|
|
// suite by using its `test` as a base, so that there is a linear hierarchy.
|
|
import { test as base } from "./panel.js";
|
|
import { type Credentials } from "../utils/api.js";
|
|
|
|
/**
|
|
* Adds an initScript to the given page which will populate localStorage appropriately so that Element will use the given credentials.
|
|
*
|
|
* Warning: this is an incomplete implementation, particularly in MAS environments: for example, a logout operation will not work correctly.
|
|
*/
|
|
export async function populateLocalStorageWithCredentials(page: Page, credentials: Credentials) {
|
|
await page.addInitScript(
|
|
({ credentials }) => {
|
|
window.localStorage.setItem("mx_hs_url", credentials.homeserverBaseUrl);
|
|
window.localStorage.setItem("mx_user_id", credentials.userId);
|
|
window.localStorage.setItem("mx_access_token", credentials.accessToken);
|
|
window.localStorage.setItem("mx_device_id", credentials.deviceId);
|
|
window.localStorage.setItem("mx_is_guest", "false");
|
|
window.localStorage.setItem("mx_has_pickle_key", "false");
|
|
window.localStorage.setItem("mx_has_access_token", "true");
|
|
|
|
// XXX: If the homeserver is using MAS, then Element uses additional localstorage settings to store more
|
|
// data. The fact that we don't populate them means that, for example, when you hit "Remove this device" in
|
|
// the app, it attempts to hit the legacy `/logout` endpoint, which returns a 40x error, which is silently
|
|
// ignored.
|
|
//
|
|
// If you fix this, (1) yay! (2) please remember to update the warning on the doc-comments of this method
|
|
// and its callers.
|
|
|
|
window.localStorage.setItem(
|
|
"mx_local_settings",
|
|
JSON.stringify({
|
|
// Retain any other settings which may have already been set
|
|
...JSON.parse(window.localStorage.getItem("mx_local_settings") ?? "{}"),
|
|
// Ensure the language is set to a consistent value
|
|
language: "en",
|
|
}),
|
|
);
|
|
},
|
|
{ credentials },
|
|
);
|
|
}
|
|
|
|
export interface TestFixtures {
|
|
/**
|
|
* The displayname to use for the user registered in {@link #credentials}.
|
|
*
|
|
* To set it, call `test.use({ displayName: "myDisplayName" })` in the test file or `describe` block.
|
|
* See {@link https://playwright.dev/docs/api/class-test#test-use}.
|
|
*/
|
|
displayName?: string;
|
|
|
|
/**
|
|
* A test fixture which registers a test user on the {@link #homeserver} and supplies the details
|
|
* of the registered user.
|
|
*/
|
|
credentials: Credentials;
|
|
|
|
/**
|
|
* The same as {@link https://playwright.dev/docs/api/class-fixtures#fixtures-page|`page`},
|
|
* but adds an initScript which will populate localStorage with the user's details from
|
|
* {@link #credentials} and {@link #homeserver}.
|
|
*
|
|
* Warning: this is an incomplete implementation, particularly in MAS environments: for example, a logout operation
|
|
* will not work correctly.
|
|
*
|
|
* Similar to {@link #user}, but doesn't load the app.
|
|
*/
|
|
pageWithCredentials: Page;
|
|
|
|
/**
|
|
* A (rather poorly-named) test fixture which registers a user per {@link #credentials}, stores
|
|
* the credentials into localStorage per {@link #pageWithCredentials}, and then loads the front page of the
|
|
* app.
|
|
*
|
|
* Warning: the user credentials are an incomplete implementation, particularly in MAS environments: for example, a logout operation
|
|
* will not work correctly.
|
|
*/
|
|
user: Credentials;
|
|
}
|
|
|
|
export const test = base.extend<TestFixtures>({
|
|
displayName: undefined,
|
|
|
|
// We don't directly depend upon the `context` fixture, but we do need to make sure that it has been run
|
|
// before this fixture, since it is responsible for configuring the APIRequestContext on the homeserver, so
|
|
// without it we cannot register the user.
|
|
credentials: async ({ context, homeserver, displayName: testDisplayName }, use, testInfo) => {
|
|
const names = ["Alice", "Bob", "Charlie", "Daniel", "Eve", "Frank", "Grace", "Hannah", "Isaac", "Judy"];
|
|
const password = uniqueId("password_");
|
|
const displayName = testDisplayName ?? sample(names)!;
|
|
|
|
const credentials = await homeserver.registerUser(`user_${testInfo.testId}`, password, displayName);
|
|
console.log(`Registered test user ${credentials.userId} with displayname ${displayName}`);
|
|
|
|
await use({
|
|
...credentials,
|
|
displayName,
|
|
});
|
|
},
|
|
|
|
pageWithCredentials: async ({ page, credentials }, use) => {
|
|
await populateLocalStorageWithCredentials(page, credentials);
|
|
await use(page);
|
|
},
|
|
|
|
user: async ({ pageWithCredentials: page, credentials }, use) => {
|
|
await page.goto("/");
|
|
await page.waitForSelector(".mx_MatrixChat", { timeout: 30000 });
|
|
await use(credentials);
|
|
},
|
|
});
|