From 5f28c50af854fe4e1294fe76c2d6f086056bc860 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Mar 2025 10:15:24 +0000 Subject: [PATCH 001/113] Create playwright-common package --- .../element-web-playwright-common/Dockerfile | 9 + .../element-web-playwright-common/README.md | 28 + .../package.json | 30 + .../playwright-screenshots.sh | 53 + .../src/@types/playwright-core.d.ts | 12 + .../src/expect/axe.ts | 37 + .../src/expect/index.ts | 21 + .../src/expect/screenshot.ts | 79 ++ .../src/fixtures/axe.ts | 22 + .../src/fixtures/index.ts | 15 + .../src/fixtures/services.ts | 188 +++ .../src/fixtures/user.ts | 93 ++ .../src/index.ts | 92 ++ .../src/testcontainers/HomeserverContainer.ts | 87 ++ .../src/testcontainers/index.ts | 15 + .../src/testcontainers/mailpit.ts | 62 + .../src/testcontainers/mas.ts | 382 ++++++ .../src/testcontainers/synapse.ts | 493 +++++++ .../src/utils/api.ts | 113 ++ .../src/utils/logger.ts | 79 ++ .../src/utils/object.ts | 16 + .../src/utils/port.ts | 22 + .../src/utils/rand.ts | 17 + .../tsconfig.json | 10 + .../element-web-playwright-common/yarn.lock | 1141 +++++++++++++++++ 25 files changed, 3116 insertions(+) create mode 100644 packages/element-web-playwright-common/Dockerfile create mode 100644 packages/element-web-playwright-common/README.md create mode 100644 packages/element-web-playwright-common/package.json create mode 100755 packages/element-web-playwright-common/playwright-screenshots.sh create mode 100644 packages/element-web-playwright-common/src/@types/playwright-core.d.ts create mode 100644 packages/element-web-playwright-common/src/expect/axe.ts create mode 100644 packages/element-web-playwright-common/src/expect/index.ts create mode 100644 packages/element-web-playwright-common/src/expect/screenshot.ts create mode 100644 packages/element-web-playwright-common/src/fixtures/axe.ts create mode 100644 packages/element-web-playwright-common/src/fixtures/index.ts create mode 100644 packages/element-web-playwright-common/src/fixtures/services.ts create mode 100644 packages/element-web-playwright-common/src/fixtures/user.ts create mode 100644 packages/element-web-playwright-common/src/index.ts create mode 100644 packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts create mode 100644 packages/element-web-playwright-common/src/testcontainers/index.ts create mode 100644 packages/element-web-playwright-common/src/testcontainers/mailpit.ts create mode 100644 packages/element-web-playwright-common/src/testcontainers/mas.ts create mode 100644 packages/element-web-playwright-common/src/testcontainers/synapse.ts create mode 100644 packages/element-web-playwright-common/src/utils/api.ts create mode 100644 packages/element-web-playwright-common/src/utils/logger.ts create mode 100644 packages/element-web-playwright-common/src/utils/object.ts create mode 100644 packages/element-web-playwright-common/src/utils/port.ts create mode 100644 packages/element-web-playwright-common/src/utils/rand.ts create mode 100644 packages/element-web-playwright-common/tsconfig.json create mode 100644 packages/element-web-playwright-common/yarn.lock diff --git a/packages/element-web-playwright-common/Dockerfile b/packages/element-web-playwright-common/Dockerfile new file mode 100644 index 0000000000..c8bf12e503 --- /dev/null +++ b/packages/element-web-playwright-common/Dockerfile @@ -0,0 +1,9 @@ +ARG PLAYWRIGHT_VERSION +FROM mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-noble + +WORKDIR /work + +# fonts-dejavu is needed for the same RTL rendering as on CI +RUN apt-get update && apt-get -y install docker.io fonts-dejavu + +ENTRYPOINT ["npx", "playwright", "test", "--update-snapshots", "--reporter", "line"] diff --git a/packages/element-web-playwright-common/README.md b/packages/element-web-playwright-common/README.md new file mode 100644 index 0000000000..7ca7277b73 --- /dev/null +++ b/packages/element-web-playwright-common/README.md @@ -0,0 +1,28 @@ +# @element-hq/element-web-playwright-common + +Set of Playwright & testcontainers utilities to make it easier to write tests for Element Web, Element Web Modules & Element Desktop. + +The main export includes a number of fixtures and custom assertions as documented in JSDoc. + +The `lib/testcontainers` export contains the following modules: + +- `SynapseContainer` - A testcontainer for running a Synapse server +- `MatrixAuthenticationServiceContainer` - A testcontainer for running a Matrix Authentication Service +- `MailpitContainer` - A testcontainer for running a Mailpit SMTP server + +There are a number of utils available in the `lib/utils` export. + +## Releases + +The API is versioned using semver, with the major version incremented for breaking changes. + +## Copyright & License + +Copyright (c) 2025 New Vector Ltd + +This software is multi licensed by New Vector Ltd (Element). It can be used either: + +(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR + +(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to). +Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses. diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json new file mode 100644 index 0000000000..5acdbdd3bf --- /dev/null +++ b/packages/element-web-playwright-common/package.json @@ -0,0 +1,30 @@ +{ + "name": "@element-hq/element-web-playwright-common", + "type": "module", + "version": "1.0.0", + "license": "SEE LICENSE IN README.md", + "main": "lib/index.js", + "bin": { + "playwright-screenshots": "./playwright-screenshots.sh" + }, + "devDependencies": { + "@types/lodash-es": "^4.17.12", + "typescript": "^5.8.2" + }, + "dependencies": { + "@axe-core/playwright": "^4.10.1", + "@testcontainers/postgresql": "^10.18.0", + "lodash-es": "^4.17.21", + "mailpit-api": "^1.2.0", + "strip-ansi": "^7.1.0", + "testcontainers": "^10.18.0", + "yaml": "^2.7.0" + }, + "peerDependencies": { + "@playwright/test": "^1.51.0", + "playwright-core": "^1.51.0" + }, + "scripts": { + "prepare": "tsc" + } +} diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh new file mode 100755 index 0000000000..a212fc9a94 --- /dev/null +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# Handle symlinks here as we tend to be executed as an npm binary +SCRIPT_PATH=$(readlink -f "$0") +SCRIPT_DIR=$(dirname "$SCRIPT_PATH") + +IMAGE_NAME="element-web-playwright-common" +echo "Building $IMAGE_NAME image in $SCRIPT_DIR" + +# Build image +PW_VERSION=$( + yarn list \ + --pattern @playwright/test \ + --depth=0 \ + --json \ + --non-interactive \ + --no-progress | \ + jq -r '.data.trees[].name | split("@")[2]' \ + ) +echo "with Playwright version $PW_VERSION" + +docker build -t "$IMAGE_NAME" --build-arg "PLAYWRIGHT_VERSION=$PW_VERSION" "$SCRIPT_DIR" + +RUN_ARGS=( + --rm + --network host + # Pass BASE_URL and CI environment variables to the container + -e BASE_URL + -e CI + # Bind mount the working directory into the container + -v $(pwd):/work/ + # Bind mount the docker socket so we can run docker commands from the container + -v /var/run/docker.sock:/var/run/docker.sock + # Bind mount /tmp so we can store temporary files + -v /tmp/:/tmp/ + -it +) + +# Ensure we pass all symlinked node_modules to the container +pushd node_modules || exit > /dev/null +SYMLINKS=$(find . -maxdepth 2 -type l -not -path "./.bin/*") +popd || exit > /dev/null +for LINK in $SYMLINKS; do + TARGET=$(readlink -f "node_modules/$LINK") + if [ -d "$TARGET" ]; then + echo "mounting linked package ${LINK:2} in container" + RUN_ARGS+=( "-v" "$TARGET:/work/node_modules/${LINK:2}" ) + fi +done + +DEFAULT_ARGS=(--grep @screenshot) + +docker run "${RUN_ARGS[@]}" "$IMAGE_NAME" "${DEFAULT_ARGS[@]}" "$@" \ No newline at end of file diff --git a/packages/element-web-playwright-common/src/@types/playwright-core.d.ts b/packages/element-web-playwright-common/src/@types/playwright-core.d.ts new file mode 100644 index 0000000000..3db7bb9c4c --- /dev/null +++ b/packages/element-web-playwright-common/src/@types/playwright-core.d.ts @@ -0,0 +1,12 @@ +/* +Copyright 2024-2025 New Vector Ltd. +Copyright 2024 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. +*/ + +declare module "playwright-core/lib/utils" { + // This type is not public in playwright-core utils + export function sanitizeForFilePath(filePath: string): string; +} diff --git a/packages/element-web-playwright-common/src/expect/axe.ts b/packages/element-web-playwright-common/src/expect/axe.ts new file mode 100644 index 0000000000..7a751f4fbe --- /dev/null +++ b/packages/element-web-playwright-common/src/expect/axe.ts @@ -0,0 +1,37 @@ +/* +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 { test, expect as baseExpect, type ExpectMatcherState, type MatcherReturnType } from "@playwright/test"; + +import type AxeBuilder from "@axe-core/playwright"; + +export type Expectations = { + /** + * Assert that the given AxeBuilder instance has no violations. + * @param receiver - The AxeBuilder instance to check. + */ + toHaveNoViolations: (this: ExpectMatcherState, receiver: AxeBuilder) => Promise; +}; + +export const expect = baseExpect.extend({ + async toHaveNoViolations(this: ExpectMatcherState, receiver: AxeBuilder) { + const testInfo = test.info(); + if (!testInfo) throw new Error(`toHaveNoViolations() must be called during the test`); + + const results = await receiver.analyze(); + + await testInfo.attach("accessibility-scan-results", { + body: JSON.stringify(results, null, 2), + contentType: "application/json", + }); + + baseExpect(results.violations).toEqual([]); + + return { pass: true, message: (): string => "", name: "toHaveNoViolations" }; + }, +}); diff --git a/packages/element-web-playwright-common/src/expect/index.ts b/packages/element-web-playwright-common/src/expect/index.ts new file mode 100644 index 0000000000..b62352d3ea --- /dev/null +++ b/packages/element-web-playwright-common/src/expect/index.ts @@ -0,0 +1,21 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { mergeExpects, type Expect } from "@playwright/test"; + +import { + expect as screenshotExpectations, + Expectations as ScreenshotExpectations, + ToMatchScreenshotOptions, +} from "./screenshot.js"; +import { expect as axeExpectations, Expectations as AxeExpectations } from "./axe.js"; + +export const expect = mergeExpects(screenshotExpectations, axeExpectations) as Expect< + ScreenshotExpectations & AxeExpectations +>; + +export type { ToMatchScreenshotOptions }; diff --git a/packages/element-web-playwright-common/src/expect/screenshot.ts b/packages/element-web-playwright-common/src/expect/screenshot.ts new file mode 100644 index 0000000000..a194b52671 --- /dev/null +++ b/packages/element-web-playwright-common/src/expect/screenshot.ts @@ -0,0 +1,79 @@ +/* +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 { + test, + expect as baseExpect, + ElementHandle, + type ExpectMatcherState, + type Locator, + type Page, + type PageAssertionsToHaveScreenshotOptions, +} from "@playwright/test"; +import { type MatcherReturnType } from "playwright/types/test"; +import { sanitizeForFilePath } from "playwright-core/lib/utils"; +import { extname } from "node:path"; + +// Based on https://github.com/microsoft/playwright/blob/2b77ed4d7aafa85a600caa0b0d101b72c8437eeb/packages/playwright/src/util.ts#L206C8-L210C2 +function sanitizeFilePathBeforeExtension(filePath: string): string { + const ext = extname(filePath); + const base = filePath.substring(0, filePath.length - ext.length); + return sanitizeForFilePath(base) + ext; +} + +export interface ToMatchScreenshotOptions extends PageAssertionsToHaveScreenshotOptions { + css?: string; +} + +export type Expectations = { + toMatchScreenshot: ( + this: ExpectMatcherState, + receiver: Page | Locator, + name: `${string}.png`, + options?: ToMatchScreenshotOptions, + ) => Promise; +}; + +/** + * Provides an upgrade to the `toHaveScreenshot` expectation. + * Unfortunately, we can't just extend the existing `toHaveScreenshot` expectation + */ +export const expect = baseExpect.extend({ + async toMatchScreenshot(receiver, name, options) { + const testInfo = test.info(); + if (!testInfo) throw new Error(`toMatchScreenshot() must be called during the test`); + + if (!testInfo.tags.includes("@screenshot")) { + throw new Error("toMatchScreenshot() must be used in a test tagged with @screenshot"); + } + + const page = "page" in receiver ? receiver.page() : receiver; + + let style: ElementHandle | undefined; + if (options?.css) { + // We add a custom style tag before taking screenshots + style = (await page.addStyleTag({ + content: options.css, + })) as ElementHandle; + } + + const screenshotName = sanitizeFilePathBeforeExtension(name); + await baseExpect(receiver).toHaveScreenshot(screenshotName, options); + + await style?.evaluate((tag) => tag.remove()); + + testInfo.annotations.push({ + // `_` prefix hides it from the HTML reporter + type: "_screenshot", + // include a path relative to `playwright/snapshots/` + description: testInfo.snapshotPath(screenshotName).split("/playwright/snapshots/", 2)[1], + }); + + return { pass: true, message: (): string => "", name: "toMatchScreenshot" }; + }, +}); diff --git a/packages/element-web-playwright-common/src/fixtures/axe.ts b/packages/element-web-playwright-common/src/fixtures/axe.ts new file mode 100644 index 0000000000..eeeb523aed --- /dev/null +++ b/packages/element-web-playwright-common/src/fixtures/axe.ts @@ -0,0 +1,22 @@ +/* +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 { test as base } from "@playwright/test"; +import AxeBuilder from "@axe-core/playwright"; + +export const test = base.extend<{ + /** + * AxeBuilder instance for the current page + */ + axe: AxeBuilder; +}>({ + axe: async ({ page }, use) => { + const builder = new AxeBuilder({ page }); + await use(builder); + }, +}); diff --git a/packages/element-web-playwright-common/src/fixtures/index.ts b/packages/element-web-playwright-common/src/fixtures/index.ts new file mode 100644 index 0000000000..04c8d120e3 --- /dev/null +++ b/packages/element-web-playwright-common/src/fixtures/index.ts @@ -0,0 +1,15 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { mergeTests } from "@playwright/test"; + +import { test as axe } from "./axe.js"; +import { test as user } from "./user.js"; + +export { type Services, type WorkerOptions } from "./services.js"; + +export const test = mergeTests(axe, user); diff --git a/packages/element-web-playwright-common/src/fixtures/services.ts b/packages/element-web-playwright-common/src/fixtures/services.ts new file mode 100644 index 0000000000..800ecd4c3c --- /dev/null +++ b/packages/element-web-playwright-common/src/fixtures/services.ts @@ -0,0 +1,188 @@ +/* +Copyright 2024-2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { test as base } from "@playwright/test"; +import { type MailpitClient } from "mailpit-api"; +import { Network, type StartedNetwork } from "testcontainers"; +import { PostgreSqlContainer, type StartedPostgreSqlContainer } from "@testcontainers/postgresql"; + +import { + type SynapseConfig, + SynapseContainer, + type StartedMatrixAuthenticationServiceContainer, + type HomeserverContainer, + type StartedHomeserverContainer, + MailpitContainer, + type StartedMailpitContainer, +} from "../testcontainers/index.js"; +import { Logger } from "../utils/logger.js"; + +/** + * Test-scoped fixtures available in the test + */ +export interface TestFixtures { + /** + * The mailpit client instance for the test. + * This is a fresh client instance with no messages from prior tests. + */ + mailpitClient: MailpitClient; +} + +export interface WorkerOptions { + /** + * The synapse configuration to use for the homeserver. + */ + synapseConfig: Partial; +} + +/** + * Worker-scoped "service" fixtures available in the test + */ +export interface Services { + /** + * The logger instance for the worker. + */ + logger: Logger; + + /** + * The started testcontainers network instance for the worker. + */ + network: StartedNetwork; + + /** + * The started postgres container instance for the worker. + */ + postgres: StartedPostgreSqlContainer; + + /** + * The started mailpit container instance for the worker. + */ + mailpit: StartedMailpitContainer; + + /** + * The homeserver instance container to use for the worker. + */ + _homeserver: HomeserverContainer; + /** + * The started homeserver instance container for the worker. + */ + homeserver: StartedHomeserverContainer; + + /** + * The Matrix Authentication Service container instance for the worker. + * May be undefined if no delegated auth is in use. + */ + mas?: StartedMatrixAuthenticationServiceContainer; +} + +export const test = base.extend({ + logger: [ + // eslint-disable-next-line no-empty-pattern + async ({}, use) => { + const logger = new Logger(); + await use(logger); + }, + { scope: "worker" }, + ], + network: [ + // eslint-disable-next-line no-empty-pattern + async ({}, use) => { + const network = await new Network().start(); + await use(network); + await network.stop(); + }, + { scope: "worker" }, + ], + postgres: [ + async ({ logger, network }, use) => { + const container = await new PostgreSqlContainer() + .withNetwork(network) + .withNetworkAliases("postgres") + .withLogConsumer(logger.getConsumer("postgres")) + .withTmpFs({ + "/dev/shm/pgdata/data": "", + }) + .withEnvironment({ + PG_DATA: "/dev/shm/pgdata/data", + }) + .withCommand([ + "-c", + "shared_buffers=128MB", + "-c", + `fsync=off`, + "-c", + `synchronous_commit=off`, + "-c", + "full_page_writes=off", + ]) + .start(); + await use(container); + await container.stop(); + }, + { scope: "worker" }, + ], + + mailpit: [ + async ({ logger, network }, use) => { + const container = await new MailpitContainer() + .withNetwork(network) + .withNetworkAliases("mailpit") + .withLogConsumer(logger.getConsumer("mailpit")) + .start(); + await use(container); + await container.stop(); + }, + { scope: "worker" }, + ], + mailpitClient: async ({ mailpit: container }, use) => { + await container.client.deleteMessages(); + await use(container.client); + }, + + synapseConfig: [{}, { scope: "worker" }], + _homeserver: [ + async ({ logger }, use) => { + const container = new SynapseContainer().withLogConsumer(logger.getConsumer("synapse")); + await use(container); + }, + { scope: "worker" }, + ], + homeserver: [ + async ({ logger, network, _homeserver: homeserver, synapseConfig, mas }, use) => { + if (homeserver instanceof SynapseContainer) { + homeserver.withConfig(synapseConfig); + } + const container = await homeserver + .withNetwork(network) + .withNetworkAliases("homeserver") + .withLogConsumer(logger.getConsumer("homeserver")) + .withMatrixAuthenticationService(mas) + .start(); + + await use(container); + await container.stop(); + }, + { scope: "worker" }, + ], + mas: [ + // eslint-disable-next-line no-empty-pattern + async ({}, use) => { + // we stub the mas fixture to allow `homeserver` to depend on it to ensure + // when it is specified by `masHomeserver` it is started before the homeserver + await use(undefined); + }, + { scope: "worker" }, + ], + + context: async ({ logger, context, request, homeserver }, use, testInfo) => { + homeserver.setRequest(request); + await logger.onTestStarted(context); + await use(context); + await logger.onTestFinished(testInfo); + await homeserver.onTestFinished(testInfo); + }, +}); diff --git a/packages/element-web-playwright-common/src/fixtures/user.ts b/packages/element-web-playwright-common/src/fixtures/user.ts new file mode 100644 index 0000000000..f0f0bda05d --- /dev/null +++ b/packages/element-web-playwright-common/src/fixtures/user.ts @@ -0,0 +1,93 @@ +/* +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"; + +import { test as base } from "./services.js"; +import { Credentials } from "../utils/api.js"; + +export const test = base.extend<{ + /** + * 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}. + * + * 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 #homeserver}, and then loads the front page of the + * app. + */ + user: Credentials; +}>({ + displayName: undefined, + credentials: async ({ 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, homeserver, credentials }, use) => { + await page.addInitScript( + ({ baseUrl, credentials }) => { + // Seed the localStorage with the required credentials + window.localStorage.setItem("mx_hs_url", baseUrl); + 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"); + + 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", + }), + ); + }, + { baseUrl: homeserver.baseUrl, credentials }, + ); + await use(page); + }, + + user: async ({ pageWithCredentials: page, credentials }, use) => { + await page.goto("/"); + await page.waitForSelector(".mx_MatrixChat", { timeout: 30000 }); + await use(credentials); + }, +}); diff --git a/packages/element-web-playwright-common/src/index.ts b/packages/element-web-playwright-common/src/index.ts new file mode 100644 index 0000000000..59df040324 --- /dev/null +++ b/packages/element-web-playwright-common/src/index.ts @@ -0,0 +1,92 @@ +/* +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 Config as BaseConfig } from "@element-hq/element-web-module-api"; + +import { test as base } from "./fixtures/index.js"; + +// Enable experimental service worker support +// See https://playwright.dev/docs/service-workers-experimental#how-to-enable +process.env["PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS"] = "1"; + +// We extend the Module API Config interface so that all modules +// which use declaration merging will have their config types correctly applied. +export interface Config extends BaseConfig { + default_server_config: { + "m.homeserver"?: { + base_url: string; + server_name?: string; + }; + "m.identity_server"?: { + base_url: string; + server_name?: string; + }; + }; + setting_defaults: Record; + map_style_url?: string; + features: Record; + modules?: string[]; +} + +// This is deliberately quite a minimal config.json, so that we can test that the default settings actually work. +export const CONFIG_JSON: Partial = { + default_server_config: {}, + + // The default language is set here for test consistency + setting_defaults: { + language: "en-GB", + }, + + // the location tests want a map style url. + map_style_url: "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx", + + features: { + // We don't want to go through the feature announcement during the e2e test + feature_release_announcement: false, + }, +}; + +export interface TestFixtures { + /** + * The contents of the config.json to send when the client requests it. + */ + config: Partial; + + labsFlags: string[]; +} + +export const test = base.extend({ + config: {}, // We merge this atop the default CONFIG_JSON in the page fixture to make extending it easier + labsFlags: [], + page: async ({ homeserver, context, page, config, labsFlags }, use) => { + await context.route(`http://localhost:8080/config.json*`, async (route) => { + const json = { + ...CONFIG_JSON, + ...config, + default_server_config: { + "m.homeserver": { + base_url: homeserver.baseUrl, + }, + ...config.default_server_config, + }, + }; + json["features"] = { + ...json["features"], + // Enable the lab features + ...labsFlags.reduce>((obj, flag) => { + obj[flag] = true; + return obj; + }, {}), + }; + await route.fulfill({ json }); + }); + await use(page); + }, +}); + +export { expect, type ToMatchScreenshotOptions } from "./expect/index.js"; diff --git a/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts b/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts new file mode 100644 index 0000000000..ccb242767b --- /dev/null +++ b/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts @@ -0,0 +1,87 @@ +/* +Copyright 2024-2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type AbstractStartedContainer, type GenericContainer } from "testcontainers"; +import { type APIRequestContext, type TestInfo } from "@playwright/test"; + +import { type StartedMatrixAuthenticationServiceContainer } from "./mas"; +import { ClientServerApi, Credentials } from "../utils/api"; +import { StartedMailpitContainer } from "./mailpit"; + +export interface HomeserverInstance { + readonly baseUrl: string; + readonly csApi: ClientServerApi; + + /** + * Register a user on the given Homeserver using the shared registration secret. + * @param username the username of the user to register + * @param password the password of the user to register + * @param displayName optional display name to set on the newly registered user + */ + registerUser(username: string, password: string, displayName?: string): Promise; + + /** + * Logs into synapse with the given username/password + * @param userId login username + * @param password login password + */ + loginUser(userId: string, password: string): Promise; + + /** + * Sets a third party identifier for the given user. This only supports setting a single 3pid and will + * replace any others. + * @param userId The full ID of the user to edit (as returned from registerUser) + * @param medium The medium of the 3pid to set + * @param address The address of the 3pid to set + */ + setThreepid(userId: string, medium: string, address: string): Promise; +} + +export interface HomeserverContainer extends GenericContainer { + /** + * Set a configuration field in the config + * @param key - the key to set + * @param value - the value to set + */ + withConfigField(key: Key, value: Config[Key]): this; + + /** + * Merge a partial configuration into the config + * @param config - the partial configuration to merge + */ + withConfig(config: Partial): this; + + /** + * Set the SMTP server to use for sending emails + * @param mailpit - the mailpit container to use + */ + withSmtpServer(mailpit: StartedMailpitContainer): this; + /** + * Set the MAS server to use for delegated auth + * @param mas - the MAS container to use + */ + withMatrixAuthenticationService(mas?: StartedMatrixAuthenticationServiceContainer): this; + + /** + * Start the container + */ + start(): Promise; +} + +export interface StartedHomeserverContainer extends AbstractStartedContainer, HomeserverInstance { + /** + * Set the request context for the APIs + * @param request - the request context to set + */ + setRequest(request: APIRequestContext): void; + + /** + * Clean up the server to prevent rooms leaking between tests + * @param testInfo - the test info for the test that just finished + */ + onTestFinished(testInfo: TestInfo): Promise; +} diff --git a/packages/element-web-playwright-common/src/testcontainers/index.ts b/packages/element-web-playwright-common/src/testcontainers/index.ts new file mode 100644 index 0000000000..21cb12027e --- /dev/null +++ b/packages/element-web-playwright-common/src/testcontainers/index.ts @@ -0,0 +1,15 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +export type { HomeserverInstance, HomeserverContainer, StartedHomeserverContainer } from "./HomeserverContainer.js"; +export { type SynapseConfig, SynapseContainer, StartedSynapseContainer } from "./synapse.js"; +export { + type MasConfig, + MatrixAuthenticationServiceContainer, + StartedMatrixAuthenticationServiceContainer, +} from "./mas.js"; +export { MailpitClient, MailpitContainer, StartedMailpitContainer } from "./mailpit.js"; diff --git a/packages/element-web-playwright-common/src/testcontainers/mailpit.ts b/packages/element-web-playwright-common/src/testcontainers/mailpit.ts new file mode 100644 index 0000000000..cfa8ad2b27 --- /dev/null +++ b/packages/element-web-playwright-common/src/testcontainers/mailpit.ts @@ -0,0 +1,62 @@ +/* +Copyright 2024-2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { AbstractStartedContainer, GenericContainer, type StartedTestContainer, Wait } from "testcontainers"; +import { MailpitClient } from "mailpit-api"; + +export type { MailpitClient }; + +/** + * A testcontainer for Mailpit. + * + * Exposes port 8025. + * Waits for listening ports. + * Disables SMTP authentication. + */ +export class MailpitContainer extends GenericContainer { + public constructor() { + super("axllent/mailpit:latest"); + + this.withExposedPorts(8025).withWaitStrategy(Wait.forListeningPorts()).withEnvironment({ + MP_SMTP_AUTH_ALLOW_INSECURE: "true", + MP_SMTP_AUTH_ACCEPT_ANY: "true", + }); + } + + /** + * Start the Mailpit container. + */ + public override async start(): Promise { + return new StartedMailpitContainer(await super.start()); + } +} + +/** + * A started Mailpit container. + */ +export class StartedMailpitContainer extends AbstractStartedContainer { + public readonly client: MailpitClient; + + public constructor(container: StartedTestContainer) { + super(container); + this.client = new MailpitClient(`http://${container.getHost()}:${container.getMappedPort(8025)}`); + } + + /** + * Get the hostname to use to connect to the Mailpit container from inside the docker network. + */ + public get internalHost(): string { + return "mailpit"; + } + + /** + * Get the port to use to connect to the Mailpit container from inside the docker network. + */ + public get internalSmtpPort(): number { + return 1025; + } +} diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts new file mode 100644 index 0000000000..292e7b117c --- /dev/null +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -0,0 +1,382 @@ +/* +Copyright 2024-2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { + AbstractStartedContainer, + GenericContainer, + type StartedTestContainer, + Wait, + type ExecResult, +} from "testcontainers"; +import { type StartedPostgreSqlContainer } from "@testcontainers/postgresql"; +import * as YAML from "yaml"; + +import { getFreePort } from "../utils/port.js"; +import { deepCopy } from "../utils/object.js"; +import { type Credentials } from "../utils/api.js"; + +const DEFAULT_CONFIG = { + http: { + listeners: [ + { + name: "web", + resources: [ + { name: "discovery" }, + { name: "human" }, + { name: "oauth" }, + { name: "compat" }, + { + name: "graphql", + playground: true, + }, + { + name: "assets", + path: "/usr/local/share/mas-cli/assets/", + }, + ], + binds: [ + { + address: "[::]:8080", + }, + ], + proxy_protocol: false, + }, + { + name: "internal", + resources: [ + { + name: "health", + }, + ], + binds: [ + { + address: "[::]:8081", + }, + ], + proxy_protocol: false, + }, + ], + trusted_proxies: ["192.128.0.0/16", "172.16.0.0/12", "10.0.0.0/10", "127.0.0.1/8", "fd00::/8", "::1/128"], + public_base: "", // Needs to be set + issuer: "", // Needs to be set + }, + database: { + host: "postgres", + port: 5432, + database: "postgres", + username: "postgres", + password: "p4S5w0rD", + max_connections: 10, + min_connections: 0, + connect_timeout: 30, + idle_timeout: 600, + max_lifetime: 1800, + }, + telemetry: { + tracing: { + exporter: "none", + propagators: [], + }, + metrics: { + exporter: "none", + }, + sentry: { + dsn: null, + }, + }, + templates: { + path: "/usr/local/share/mas-cli/templates/", + assets_manifest: "/usr/local/share/mas-cli/manifest.json", + translations_path: "/usr/local/share/mas-cli/translations/", + }, + email: { + from: '"Authentication Service" ', + reply_to: '"Authentication Service" ', + transport: "smtp", + mode: "plain", + hostname: "mailpit", + port: 1025, + username: "username", + password: "password", + }, + secrets: { + encryption: "984b18e207c55ad5fbb2a49b217481a722917ee87b2308d4cf314c83fed8e3b5", + keys: [ + { + kid: "YEAhzrKipJ", + key: "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAuIV+AW5vx52I4CuumgSxp6yvKfIAnRdALeZZCoFkIGxUli1B\nS79NJ3ls46oLh1pSD9RrhaMp6HTNoi4K3hnP9Q9v77pD7KwdFKG3UdG1zksIB0s/\n+/Ey/DmX4LPluwBBS7r/LkQ1jk745lENA++oiDqZf2D/uP8jCHlvaSNyVKTqi1ki\nOXPd4T4xBUjzuas9ze5jQVSYtfOidgnv1EzUipbIxgvH1jNt4raRlmP8mOq7xEnW\nR+cF5x6n/g17PdSEfrwO4kz6aKGZuMP5lVlDEEnMHKabFSQDBl7+Mpok6jXutbtA\nuiBnsKEahF9eoj4na4fpbRNPdIVyoaN5eGvm5wIDAQABAoIBAApyFCYEmHNWaa83\nCdVSOrRhRDE9r+c0r79pcNT1ajOjrk4qFa4yEC4R46YntCtfY5Hd1pBkIjU0l4d8\nz8Su9WTMEOwjQUEepS7L0NLi6kXZXYT8L40VpGs+32grBvBFHW0qEtQNrHJ36gMv\nx2rXoFTF7HaXiSJx3wvVxAbRqOE9tBXLsmNHaWaAdWQG5o77V9+zvMri3cAeEg2w\nVkKokb0dza7es7xG3tqS26k69SrwGeeuKo7qCHPH2cfyWmY5Yhv8iOoA59JzzbiK\nUdxyzCHskrPSpRKVkVVwmY3RBt282TmSRG7td7e5ESSj50P2e5BI5uu1Hp/dvU4F\nvYjV7kECgYEA6WqYoUpVsgQiqhvJwJIc/8gRm0mUy8TenI36z4Iim01Nt7fibWH7\nXnsFqLGjXtYNVWvBcCrUl9doEnRbJeG2eRGbGKYAWVrOeFvwM4fYvw9GoOiJdDj4\ncgWDe7eHbHE+UTqR7Nnr/UBfipoNWDh6X68HRBuXowh0Q6tOfxsrRFECgYEAyl/V\n4b8bFp3pKZZCb+KPSYsQf793cRmrBexPcLWcDPYbMZQADEZ/VLjbrNrpTOWxUWJT\nhr8MrWswnHO+l5AFu5CNO+QgV2dHLk+2w8qpdpFRPJCfXfo2D3wZ0c4cv3VCwv1V\n5y7f6XWVjDWZYV4wj6c3shxZJjZ+9Hbhf3/twbcCgYA6fuRRR3fCbRbi2qPtBrEN\nyO3gpMgNaQEA6vP4HPzfPrhDWmn8T5nXS61XYW03zxz4U1De81zj0K/cMBzHmZFJ\nNghQXQmpWwBzWVcREvJWr1Vb7erEnaJlsMwKrSvbGWYspSj82oAxr3hCG+lMOpsw\nb4S6pM+TpAK/EqdRY1WsgQKBgQCGoMaaTRXqL9bC0bEU2XVVCWxKb8c3uEmrwQ7/\n/fD4NmjUzI5TnDps1CVfkqoNe+hAKddDFqmKXHqUOfOaxDbsFje+lf5l5tDVoDYH\nfjTKKdYPIm7CiAeauYY7qpA5Vfq52Opixy4yEwUPp0CII67OggFtPaqY3zwJyWQt\n+57hdQKBgGCXM/KKt7ceUDcNJxSGjvu0zD9D5Sv2ihYlEBT/JLaTCCJdvzREevaJ\n1d+mpUAt0Lq6A8NWOMq8HPaxAik3rMQ0WtM5iG+XgsUqvTSb7NcshArDLuWGnW3m\nMC4rM0UBYAS4QweduUSH1imrwH/1Gu5+PxbiecceRMMggWpzu0Bq\n-----END RSA PRIVATE KEY-----\n", + }, + { + kid: "8J1AxrlNZT", + key: "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIF1cjfIOEdy3BXJ72x6fKpEB8WP1ddZAUJAaqqr/6CpToAoGCCqGSM49\nAwEHoUQDQgAEfHdNuI1Yeh3/uOq2PlnW2vymloOVpwBYebbw4VVsna9xhnutIdQW\ndE8hkX8Yb0pIDasrDiwllVLzSvsWJAI0Kw==\n-----END EC PRIVATE KEY-----\n", + }, + { + kid: "3BW6un1EBi", + key: "-----BEGIN EC PRIVATE KEY-----\nMIGkAgEBBDA+3ZV17r8TsiMdw1cpbTSNbyEd5SMy3VS1Mk/kz6O2Ev/3QZut8GE2\nq3eGtLBoVQigBwYFK4EEACKhZANiAASs8Wxjk/uRimRKXnPr2/wDaXkN9wMDjYQK\nmZULb+0ZP1/cXmuXuri8hUGhQvIU8KWY9PkpV+LMPEdpE54mHPKSLjq5CDXoSZ/P\n9f7cdRaOZ000KQPZfIFR9ujJTtDN7Vs=\n-----END EC PRIVATE KEY-----\n", + }, + { + kid: "pkZ0pTKK0X", + key: "-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIHenfsXYPc5yzjZKUfvmydDR1YRwdsfZYvwHf/2wsYxooAcGBSuBBAAK\noUQDQgAEON1x7Vlu+nA0KvC5vYSOHhDUkfLYNZwYSLPFVT02h9E13yFFMIJegIBl\nAer+6PMZpPc8ycyeH9N+U9NAyliBhQ==\n-----END EC PRIVATE KEY-----\n", + }, + ], + }, + passwords: { + enabled: true, + schemes: [ + { + version: 1, + algorithm: "argon2id", + }, + ], + minimum_complexity: 0, + }, + policy: { + wasm_module: "/usr/local/share/mas-cli/policy.wasm", + client_registration_entrypoint: "client_registration/violation", + register_entrypoint: "register/violation", + authorization_grant_entrypoint: "authorization_grant/violation", + password_entrypoint: "password/violation", + email_entrypoint: "email/violation", + data: { + client_registration: { + // allow non-SSL and localhost URIs + allow_insecure_uris: true, + // EW doesn't have contacts at this time + allow_missing_contacts: true, + }, + }, + }, + upstream_oauth2: { + providers: [], + }, + branding: { + service_name: null, + policy_uri: null, + tos_uri: null, + imprint: null, + logo_uri: null, + }, + account: { + password_registration_enabled: true, + }, + experimental: { + access_token_ttl: 300, + compat_token_ttl: 300, + }, + rate_limiting: { + login: { + burst: 10, + per_second: 1, + }, + registration: { + burst: 10, + per_second: 1, + }, + }, +}; + +/** + * Incomplete type for the MAS configuration. + */ +export type MasConfig = typeof DEFAULT_CONFIG; + +/** + * A container running the Matrix Authentication Service. + * + * Exposes the MAS API on port 8080 and the health check on port 8081. + * Waits for HTTP /health on port 8081 to be available. + */ +export class MatrixAuthenticationServiceContainer extends GenericContainer { + private config: MasConfig; + private readonly args = ["-c", "/config/config.yaml"]; + + public constructor(db: StartedPostgreSqlContainer) { + // We rely on `mas-cli manage add-email` which isn't in a release yet + // https://github.com/element-hq/matrix-authentication-service/pull/3235 + super("ghcr.io/element-hq/matrix-authentication-service:sha-0b90c33"); + + this.config = deepCopy(DEFAULT_CONFIG); + this.config.database.username = db.getUsername(); + this.config.database.password = db.getPassword(); + + this.withExposedPorts(8080, 8081) + .withWaitStrategy(Wait.forHttp("/health", 8081)) + .withCommand(["server", ...this.args]); + } + + /** + * Adds additional configuration to the MAS config. + * @param config - additional config fields to add + */ + public withConfig(config: object): this { + this.config = { + ...this.config, + ...config, + }; + return this; + } + + /** + * Starts the MAS container + */ + public override async start(): Promise { + // MAS config issuer needs to know what URL it'll be accessed from, so we have to map the port manually + const port = await getFreePort(); + + this.config.http.public_base = `http://localhost:${port}/`; + this.config.http.issuer = `http://localhost:${port}/`; + + this.withExposedPorts({ + container: 8080, + host: port, + }).withCopyContentToContainer([ + { + target: "/config/config.yaml", + content: YAML.stringify(this.config), + }, + ]); + + return new StartedMatrixAuthenticationServiceContainer( + await super.start(), + `http://localhost:${port}`, + this.args, + ); + } +} + +/** + * A started Matrix Authentication Service container. + */ +export class StartedMatrixAuthenticationServiceContainer extends AbstractStartedContainer { + private adminTokenPromise?: Promise; + + public constructor( + container: StartedTestContainer, + public readonly baseUrl: string, + private readonly args: string[], + ) { + super(container); + } + + /** + * Retrieves a valid HS admin token + */ + public async getAdminToken(): Promise { + if (this.adminTokenPromise === undefined) { + this.adminTokenPromise = this.registerUserInternal( + "admin", + "totalyinsecureadminpassword", + undefined, + true, + ).then((res) => res.accessToken); + } + return this.adminTokenPromise; + } + + private async manage(cmd: string, ...args: string[]): Promise { + const result = await this.exec(["mas-cli", "manage", cmd, ...this.args, ...args]); + if (result.exitCode !== 0) { + throw new Error(`Failed mas-cli manage ${cmd}: ${result.output}`); + } + return result; + } + + private async manageRegisterUser( + username: string, + password: string, + displayName?: string, + admin = false, + ): Promise { + const args: string[] = []; + if (admin) args.push("-a"); + const result = await this.manage( + "register-user", + ...args, + "-y", + "-p", + password, + "-d", + displayName ?? "", + username, + ); + + const registerLines = result.output.trim().split("\n"); + const userId = registerLines + .find((line) => line.includes("Matrix ID: ")) + ?.split(": ") + .pop(); + + if (!userId) { + throw new Error(`Failed to register user: ${result.output}`); + } + + return userId; + } + + private async manageIssueCompatibilityToken( + username: string, + admin = false, + ): Promise<{ accessToken: string; deviceId: string }> { + const args: string[] = []; + if (admin) args.push("--yes-i-want-to-grant-synapse-admin-privileges"); + const result = await this.manage("issue-compatibility-token", ...args, username); + + const parts = result.output.trim().split(/\s+/); + const accessToken = parts.find((part) => part.startsWith("mct_")); + const deviceId = parts.find((part) => part.startsWith("compat_session.device="))?.split("=")[1]; + + if (!accessToken || !deviceId) { + throw new Error(`Failed to issue compatibility token: ${result.output}`); + } + + return { accessToken, deviceId }; + } + + private async registerUserInternal( + username: string, + password: string, + displayName?: string, + admin = false, + ): Promise { + const userId = await this.manageRegisterUser(username, password, displayName, admin); + const { deviceId, accessToken } = await this.manageIssueCompatibilityToken(username, admin); + + return { + userId, + accessToken, + deviceId, + homeServer: userId.slice(1).split(":").slice(1).join(":"), + displayName, + username, + password, + }; + } + + /** + * Registers a user + * @param username - the username of the user to register + * @param password - the password of the user to register + * @param displayName - optional display name to set on the newly registered user + */ + public async registerUser(username: string, password: string, displayName?: string): Promise { + return this.registerUserInternal(username, password, displayName, false); + } + + /** + * Binds a 3pid + * @param username - the username of the user to bind the 3pid to + * @param medium - the medium of the 3pid to bind + * @param address - the address of the 3pid to bind + */ + public async setThreepid(username: string, medium: string, address: string): Promise { + if (medium !== "email") { + throw new Error("Only email threepids are supported by MAS"); + } + + await this.manage("add-email", username, address); + } +} diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts new file mode 100644 index 0000000000..7c7248ee84 --- /dev/null +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -0,0 +1,493 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { + AbstractStartedContainer, + GenericContainer, + type RestartOptions, + type StartedTestContainer, + Wait, +} from "testcontainers"; +import { type APIRequestContext, type TestInfo } from "@playwright/test"; +import crypto from "node:crypto"; +import * as YAML from "yaml"; +import { set } from "lodash-es"; + +import { getFreePort } from "../utils/port.js"; +import { randB64Bytes } from "../utils/rand.js"; +import { deepCopy } from "../utils/object.js"; +import { type HomeserverContainer, type StartedHomeserverContainer } from "./HomeserverContainer.js"; +import { type StartedMatrixAuthenticationServiceContainer } from "./mas.js"; +import { Api, ClientServerApi, type Verb, type Credentials } from "../utils/api.js"; +import { StartedMailpitContainer } from "./mailpit.js"; + +const TAG = "develop@sha256:8d0049e8e0524ad6817cf7737453fe47de1ed3b8d04704f0c2fd6c136414c9d7"; + +const DEFAULT_CONFIG = { + server_name: "localhost", + public_baseurl: "", // set by start method + pid_file: "/homeserver.pid", + web_client: false, + soft_file_limit: 0, + // Needs to be configured to log to the console like a good docker process + log_config: "/data/log.config", + listeners: [ + { + // Listener is always port 8008 (configured in the container) + port: 8008, + tls: false, + bind_addresses: ["::"], + type: "http", + x_forwarded: true, + resources: [ + { + names: ["client"], + compress: false, + }, + ], + }, + ], + database: { + // An sqlite in-memory database is fast & automatically wipes each time + name: "sqlite3", + args: { + database: ":memory:", + }, + }, + rc_messages_per_second: 10000, + rc_message_burst_count: 10000, + rc_registration: { + per_second: 10000, + burst_count: 10000, + }, + rc_joins: { + local: { + per_second: 9999, + burst_count: 9999, + }, + remote: { + per_second: 9999, + burst_count: 9999, + }, + }, + rc_joins_per_room: { + per_second: 9999, + burst_count: 9999, + }, + rc_3pid_validation: { + per_second: 1000, + burst_count: 1000, + }, + rc_invites: { + per_room: { + per_second: 1000, + burst_count: 1000, + }, + per_user: { + per_second: 1000, + burst_count: 1000, + }, + }, + rc_login: { + address: { + per_second: 10000, + burst_count: 10000, + }, + account: { + per_second: 10000, + burst_count: 10000, + }, + failed_attempts: { + per_second: 10000, + burst_count: 10000, + }, + }, + media_store_path: "/tmp/media_store", + max_upload_size: "50M", + max_image_pixels: "32M", + dynamic_thumbnails: false, + enable_registration: true, + enable_registration_without_verification: true, + disable_msisdn_registration: false, + registrations_require_3pid: [], + enable_metrics: false, + report_stats: false, + // These placeholders will be replaced with values generated at start + registration_shared_secret: "secret", + macaroon_secret_key: "secret", + form_secret: "secret", + // Signing key must be here: it will be generated to this file + signing_key_path: "/data/localhost.signing.key", + trusted_key_servers: [], + password_config: { + enabled: true, + }, + ui_auth: {}, + background_updates: { + // Inhibit background updates as this Synapse isn't long-lived + min_batch_size: 100000, + sleep_duration_ms: 100000, + }, + enable_authenticated_media: true, + email: undefined as + | undefined + | { + enable_notifs: boolean; + smtp_host: string; + smtp_port: number; + smtp_user: string; + smtp_pass: string; + require_transport_security: false; + notif_from: string; + app_name: string; + notif_template_html: string; + notif_template_text: string; + notif_for_new_users: boolean; + client_base_url: string; + }, + user_consent: undefined as + | undefined + | { + template_dir: string; + version: string; + server_notice_content: Record; + send_server_notice_to_guests: boolean; + block_events_error: string; + require_at_registration: boolean; + }, + server_notices: undefined as + | undefined + | { + system_mxid_localpart: string; + system_mxid_display_name: string; + system_mxid_avatar_url: string; + room_name: string; + }, + allow_guest_access: false, + experimental_features: {}, + oidc_providers: [], + serve_server_wellknown: true, + presence: { + enabled: true, + include_offline_users_on_sync: true, + }, + room_list_publication_rules: [{ action: "allow" }], +}; + +/** + * Incomplete type describing the configuration for a Synapse homeserver + */ +export type SynapseConfig = typeof DEFAULT_CONFIG; + +/** + * A Synapse testcontainer + * + * Exposes port 8008. + * Waits for HTTP /health 8008 to 200. + */ +export class SynapseContainer extends GenericContainer implements HomeserverContainer { + private config: SynapseConfig; + private mas?: StartedMatrixAuthenticationServiceContainer; + + public constructor() { + super(`ghcr.io/element-hq/synapse:${TAG}`); + + this.config = deepCopy(DEFAULT_CONFIG); + this.config.registration_shared_secret = randB64Bytes(16); + this.config.macaroon_secret_key = randB64Bytes(16); + this.config.form_secret = randB64Bytes(16); + + const signingKey = randB64Bytes(32); + this.withWaitStrategy(Wait.forHttp("/health", 8008)).withCopyContentToContainer([ + { target: this.config.signing_key_path, content: `ed25519 x ${signingKey}` }, + { + target: this.config.log_config, + content: YAML.stringify({ + version: 1, + formatters: { + precise: { + format: "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s", + }, + }, + handlers: { + console: { + class: "logging.StreamHandler", + formatter: "precise", + }, + }, + loggers: { + "synapse.storage.SQL": { + level: "DEBUG", + }, + "twisted": { + handlers: ["console"], + propagate: false, + }, + }, + root: { + level: "DEBUG", + handlers: ["console"], + }, + disable_existing_loggers: false, + }), + }, + ]); + } + + public withConfigField(key: string, value: unknown): this { + set(this.config, key, value); + return this; + } + + public withConfig(config: Partial): this { + this.config = { + ...this.config, + ...config, + }; + return this; + } + + public withSmtpServer(mailpit: StartedMailpitContainer): this { + this.config.email = { + enable_notifs: false, + smtp_host: mailpit.internalHost, + smtp_port: mailpit.internalSmtpPort, + smtp_user: "username", + smtp_pass: "password", + require_transport_security: false, + notif_from: "Your Friendly %(app)s homeserver ", + app_name: "Matrix", + notif_template_html: "notif_mail.html", + notif_template_text: "notif_mail.txt", + notif_for_new_users: true, + client_base_url: "http://localhost/element", + }; + return this; + } + + public withMatrixAuthenticationService(mas?: StartedMatrixAuthenticationServiceContainer): this { + this.mas = mas; + return this; + } + + public override async start(): Promise { + // Synapse config public_baseurl needs to know what URL it'll be accessed from, so we have to map the port manually + const port = await getFreePort(); + + this.withExposedPorts({ + container: 8008, + host: port, + }) + .withConfig({ + public_baseurl: `http://localhost:${port}`, + }) + .withCopyContentToContainer([ + { + target: "/data/homeserver.yaml", + content: YAML.stringify(this.config), + }, + ]); + + const container = await super.start(); + const baseUrl = `http://localhost:${port}`; + if (this.mas) { + return new StartedSynapseWithMasContainer( + container, + baseUrl, + this.config.registration_shared_secret, + this.mas, + ); + } + + return new StartedSynapseContainer(container, baseUrl, this.config.registration_shared_secret); + } +} + +/** + * A started Synapse testcontainer + */ +export class StartedSynapseContainer extends AbstractStartedContainer implements StartedHomeserverContainer { + protected adminTokenPromise?: Promise; + protected readonly adminApi: Api; + public readonly csApi: ClientServerApi; + + public constructor( + container: StartedTestContainer, + public readonly baseUrl: string, + private readonly registrationSharedSecret: string, + ) { + super(container); + this.adminApi = new Api(`${this.baseUrl}/_synapse/admin`); + this.csApi = new ClientServerApi(this.baseUrl); + } + + /** + * Restart the container + * Useful to reset the state of the homeserver between tests + * @param options - options to pass to the restart + */ + public restart(options?: Partial): Promise { + this.adminTokenPromise = undefined; + return super.restart(options); + } + + public setRequest(request: APIRequestContext): void { + this.csApi.setRequest(request); + this.adminApi.setRequest(request); + } + + public async onTestFinished(testInfo: TestInfo): Promise { + // Clean up the server to prevent rooms leaking between tests + await this.deletePublicRooms(); + } + + protected async deletePublicRooms(): Promise { + const token = await this.getAdminToken(); + // We hide the rooms from the room directory to save time between tests and for portability between homeservers + const { chunk: rooms } = await this.csApi.request<{ + chunk: { room_id: string }[]; + }>("GET", "/v3/publicRooms", token, {}); + await Promise.all( + rooms.map((room) => + this.csApi.request("PUT", `/v3/directory/list/room/${room.room_id}`, token, { visibility: "private" }), + ), + ); + } + + private async registerUserInternal( + username: string, + password: string, + displayName?: string, + admin = false, + ): Promise { + const path = "/v1/register"; + const { nonce } = await this.adminApi.request<{ nonce: string }>("GET", path, undefined, {}); + const mac = crypto + .createHmac("sha1", this.registrationSharedSecret) + .update(`${nonce}\0${username}\0${password}\0${admin ? "" : "not"}admin`) + .digest("hex"); + const data = await this.adminApi.request<{ + home_server: string; + access_token: string; + user_id: string; + device_id: string; + }>("POST", path, undefined, { + nonce, + username, + password, + mac, + admin, + displayname: displayName, + }); + + return { + homeServer: data.home_server || data.user_id.split(":").slice(1).join(":"), + accessToken: data.access_token, + userId: data.user_id, + deviceId: data.device_id, + password, + displayName, + username, + }; + } + + protected async getAdminToken(): Promise { + if (this.adminTokenPromise === undefined) { + this.adminTokenPromise = this.registerUserInternal( + "admin", + "totalyinsecureadminpassword", + undefined, + true, + ).then((res) => res.accessToken); + } + return this.adminTokenPromise; + } + + private async adminRequest(verb: "GET", path: string, data?: never): Promise; + private async adminRequest(verb: Verb, path: string, data?: object): Promise; + private async adminRequest(verb: Verb, path: string, data?: object): Promise { + const adminToken = await this.getAdminToken(); + return this.adminApi.request(verb, path, adminToken, data); + } + + /** + * Register a user on the given Homeserver using the shared registration secret. + * @param username - the username of the user to register + * @param password - the password of the user to register + * @param displayName - optional display name to set on the newly registered user + */ + public registerUser(username: string, password: string, displayName?: string): Promise { + return this.registerUserInternal(username, password, displayName, false); + } + + /** + * Logs into synapse with the given username/password + * @param userId - login username + * @param password - login password + */ + public async loginUser(userId: string, password: string): Promise { + return this.csApi.loginUser(userId, password); + } + + /** + * Binds a 3pid + * @param userId - the username of the user to bind the 3pid to + * @param medium - the medium of the 3pid to bind + * @param address - the address of the 3pid to bind + */ + public async setThreepid(userId: string, medium: string, address: string): Promise { + await this.adminRequest("PUT", `/v2/users/${userId}`, { + threepids: [ + { + medium, + address, + }, + ], + }); + } +} + +/** + * A started Synapse container when delegating auth to MAS + */ +export class StartedSynapseWithMasContainer extends StartedSynapseContainer { + public constructor( + container: StartedTestContainer, + baseUrl: string, + registrationSharedSecret: string, + private readonly mas: StartedMatrixAuthenticationServiceContainer, + ) { + super(container, baseUrl, registrationSharedSecret); + } + + protected async getAdminToken(): Promise { + if (this.adminTokenPromise === undefined) { + this.adminTokenPromise = this.mas.getAdminToken(); + } + return this.adminTokenPromise; + } + + /** + * Register a user on the given Homeserver using the shared registration secret. + * @param username - the username of the user to register + * @param password - the password of the user to register + * @param displayName - optional display name to set on the newly registered user + */ + public registerUser(username: string, password: string, displayName?: string): Promise { + return this.mas.registerUser(username, password, displayName); + } + + /** + * Binds a 3pid + * @param userId - the username of the user to bind the 3pid to + * @param medium - the medium of the 3pid to bind + * @param address - the address of the 3pid to bind + */ + public async setThreepid(userId: string, medium: string, address: string): Promise { + return this.mas.setThreepid(userId, medium, address); + } +} diff --git a/packages/element-web-playwright-common/src/utils/api.ts b/packages/element-web-playwright-common/src/utils/api.ts new file mode 100644 index 0000000000..55258e7408 --- /dev/null +++ b/packages/element-web-playwright-common/src/utils/api.ts @@ -0,0 +1,113 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type APIRequestContext } from "@playwright/test"; + +export type Verb = "GET" | "POST" | "PUT" | "DELETE"; + +/** + * A generic API client. + */ +export class Api { + private _request?: APIRequestContext; + + public constructor(private readonly baseUrl: string) {} + + /** + * Set the request context to use for making requests. + * @param request - The request context to use. + */ + public setRequest(request: APIRequestContext): void { + this._request = request; + } + + /** + * Make a request to the API. + * @param verb - The HTTP verb to use. + * @param path - The path to request. + * @param token - The access token to use for the request. + * @param data - The data to send with the request. + */ + public async request(verb: "GET", path: string, token?: string, data?: never): Promise; + public async request(verb: Verb, path: string, token?: string, data?: object): Promise; + public async request(verb: Verb, path: string, token?: string, data?: object): Promise { + if (!this._request) { + throw new Error("No request context set"); + } + + const url = `${this.baseUrl}${path}`; + const res = await this._request.fetch(url, { + data, + method: verb, + headers: token + ? { + Authorization: `Bearer ${token}`, + } + : undefined, + }); + + if (!res.ok()) { + throw new Error( + `Request to ${url} failed with status ${res.status()}: ${JSON.stringify(await res.json())}`, + ); + } + + return res.json(); + } +} + +/** + * Credentials for a user. + */ +export interface Credentials { + accessToken: string; + userId: string; + deviceId: string; + homeServer: string; + password: string | null; // null for password-less users + displayName?: string; + username: string; // the localpart of the userId +} + +/** + * A client-server API for interacting with a Matrix homeserver. + */ +export class ClientServerApi extends Api { + public constructor(baseUrl: string) { + super(`${baseUrl}/_matrix/client`); + } + + /** + * Register a user on the homeserver. + * @param userId - The user ID to register. + * @param password - The password to use for the user. + */ + public async loginUser(userId: string, password: string): Promise { + const json = await this.request<{ + access_token: string; + user_id: string; + device_id: string; + home_server: string; + }>("POST", "/v3/login", undefined, { + type: "m.login.password", + identifier: { + type: "m.id.user", + user: userId, + }, + password: password, + }); + + return { + password, + accessToken: json.access_token, + userId: json.user_id, + deviceId: json.device_id, + homeServer: json.home_server || json.user_id.split(":").slice(1).join(":"), + username: userId.slice(1).split(":")[0], + }; + } +} diff --git a/packages/element-web-playwright-common/src/utils/logger.ts b/packages/element-web-playwright-common/src/utils/logger.ts new file mode 100644 index 0000000000..294a87067b --- /dev/null +++ b/packages/element-web-playwright-common/src/utils/logger.ts @@ -0,0 +1,79 @@ +/* +Copyright 2024-2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { type BrowserContext, type Page, type TestInfo } from "@playwright/test"; +import { type Readable } from "node:stream"; +import stripAnsi from "strip-ansi"; + +/** + * A logger that captures console logs from pages and testcontainers. + */ +export class Logger { + private pages: Page[] = []; + private logs: Record = {}; + + /** + * Get a consumer function that captures logs for a given container. + * @param container - the human-readable name of the container. + */ + public getConsumer(container: string) { + this.logs[container] = ""; + return (stream: Readable) => { + stream.on("data", (chunk) => { + this.logs[container] += chunk.toString(); + }); + stream.on("err", (chunk) => { + this.logs[container] += "ERR " + chunk.toString(); + }); + }; + } + + /** + * Hook to call when a test starts. + * @param context - the browser context of the test. + */ + public async onTestStarted(context: BrowserContext) { + this.pages = []; + for (const id in this.logs) { + if (id.startsWith("page-")) { + delete this.logs[id]; + } else { + this.logs[id] = ""; + } + } + + context.on("console", (msg) => { + const page = msg.page(); + if (!page) return; + let pageIdx = this.pages.indexOf(page); + if (pageIdx === -1) { + this.pages.push(page); + pageIdx = this.pages.length - 1; + this.logs[`page-${pageIdx}`] = `Console logs for page with URL: ${page.url()}\n\n`; + } + const type = msg.type(); + const text = msg.text(); + this.logs[`page-${pageIdx}`] += `${type}: ${text}\n`; + }); + } + + /** + * Hook to call when a test finishes. + * @param testInfo - the info about the test that just finished. + */ + public async onTestFinished(testInfo: TestInfo) { + if (testInfo.status !== "passed") { + for (const id in this.logs) { + if (!this.logs[id]) continue; + await testInfo.attach(id, { + body: stripAnsi(this.logs[id]), + contentType: "text/plain", + }); + } + } + } +} diff --git a/packages/element-web-playwright-common/src/utils/object.ts b/packages/element-web-playwright-common/src/utils/object.ts new file mode 100644 index 0000000000..645e50bbad --- /dev/null +++ b/packages/element-web-playwright-common/src/utils/object.ts @@ -0,0 +1,16 @@ +/* +Copyright 2024-2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only +Please see LICENSE files in the repository root for full details. +*/ + +/** + * Deep copy the given object. The object MUST NOT have circular references and + * MUST NOT have functions. + * @param obj - The object to deep copy. + * @returns A copy of the object without any references to the original. + */ +export function deepCopy(obj: T): T { + return JSON.parse(JSON.stringify(obj)); +} diff --git a/packages/element-web-playwright-common/src/utils/port.ts b/packages/element-web-playwright-common/src/utils/port.ts new file mode 100644 index 0000000000..d39594caac --- /dev/null +++ b/packages/element-web-playwright-common/src/utils/port.ts @@ -0,0 +1,22 @@ +/* +Copyright 2024-2025 New Vector Ltd. +Copyright 2022 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 * as net from "node:net"; + +/** + * Get a free networking port on the system. + */ +export async function getFreePort(): Promise { + return new Promise((resolve) => { + const srv = net.createServer(); + srv.listen(0, () => { + const port = (srv.address()).port; + srv.close(() => resolve(port)); + }); + }); +} diff --git a/packages/element-web-playwright-common/src/utils/rand.ts b/packages/element-web-playwright-common/src/utils/rand.ts new file mode 100644 index 0000000000..debb5bc69a --- /dev/null +++ b/packages/element-web-playwright-common/src/utils/rand.ts @@ -0,0 +1,17 @@ +/* +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 crypto from "node:crypto"; + +/** + * Generate a random base64 string of the given number of bytes. + * @param numBytes - The number of bytes to generate. + */ +export function randB64Bytes(numBytes: number): string { + return crypto.randomBytes(numBytes).toString("base64").replace(/=*$/, ""); +} diff --git a/packages/element-web-playwright-common/tsconfig.json b/packages/element-web-playwright-common/tsconfig.json new file mode 100644 index 0000000000..04bbd964f0 --- /dev/null +++ b/packages/element-web-playwright-common/tsconfig.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "lib", + "declarationMap": true, + "allowImportingTsExtensions": false + }, + "include": ["src"] +} diff --git a/packages/element-web-playwright-common/yarn.lock b/packages/element-web-playwright-common/yarn.lock new file mode 100644 index 0000000000..9cf11fe42f --- /dev/null +++ b/packages/element-web-playwright-common/yarn.lock @@ -0,0 +1,1141 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@axe-core/playwright@^4.10.1": + version "4.10.1" + resolved "https://registry.yarnpkg.com/@axe-core/playwright/-/playwright-4.10.1.tgz#c811ba8bfa244833cce422c4131e0043828c42cc" + integrity sha512-EV5t39VV68kuAfMKqb/RL+YjYKhfuGim9rgIaQ6Vntb2HgaCaau0h98Y3WEUqW1+PbdzxDtDNjFAipbtZuBmEA== + dependencies: + axe-core "~4.10.2" + +"@balena/dockerignore@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" + integrity sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q== + +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@playwright/test@^1.51.0": + version "1.51.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.51.0.tgz#8d5c8400b465a0bfdbcf993e390ceecb903ea6d2" + integrity sha512-dJ0dMbZeHhI+wb77+ljx/FeC8VBP6j/rj9OAojO08JI80wTZy6vRk9KvHKiDCUh4iMpEiseMgqRBIeW+eKX6RA== + dependencies: + playwright "1.51.0" + +"@testcontainers/postgresql@^10.18.0": + version "10.18.0" + resolved "https://registry.yarnpkg.com/@testcontainers/postgresql/-/postgresql-10.18.0.tgz#87d3acb3a4bc2196bd8e9b7496fe6e3146b59f2b" + integrity sha512-WxkE/tBlBpoKvqDEqL3i/mL6BOBWnXb8FXKtLhEeZ3lSt0zlldkTozMmewNsKJtFTBZdv7uFwMzWyXP12t0sxQ== + dependencies: + testcontainers "^10.18.0" + +"@types/docker-modem@*": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/docker-modem/-/docker-modem-3.0.6.tgz#1f9262fcf85425b158ca725699a03eb23cddbf87" + integrity sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg== + dependencies: + "@types/node" "*" + "@types/ssh2" "*" + +"@types/dockerode@^3.3.29": + version "3.3.35" + resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-3.3.35.tgz#d78c844a246f8717e3bcf2cc134a976bfa630b10" + integrity sha512-P+DCMASlsH+QaKkDpekKrP5pLls767PPs+/LrlVbKnEnY5tMpEUa2C6U4gRsdFZengOqxdCIqy16R22Q3pLB6Q== + dependencies: + "@types/docker-modem" "*" + "@types/node" "*" + "@types/ssh2" "*" + +"@types/lodash-es@^4.17.12": + version "4.17.12" + resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" + integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.17.16" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.16.tgz#94ae78fab4a38d73086e962d0b65c30d816bfb0a" + integrity sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g== + +"@types/node@*": + version "22.13.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.10.tgz#df9ea358c5ed991266becc3109dc2dc9125d77e4" + integrity sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw== + dependencies: + undici-types "~6.20.0" + +"@types/node@^18.11.18": + version "18.19.80" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.80.tgz#6d6008e8920dddcd23f9dd33da24684ef57d487c" + integrity sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ== + dependencies: + undici-types "~5.26.4" + +"@types/ssh2-streams@*": + version "0.1.12" + resolved "https://registry.yarnpkg.com/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz#e68795ba2bf01c76b93f9c9809e1f42f0eaaec5f" + integrity sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg== + dependencies: + "@types/node" "*" + +"@types/ssh2@*": + version "1.15.4" + resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-1.15.4.tgz#2347d2ff079e205b077c02407d822803bfd23c45" + integrity sha512-9JTQgVBWSgq6mAen6PVnrAmty1lqgCMvpfN+1Ck5WRUsyMYPa6qd50/vMJ0y1zkGpOEgLzm8m8Dx/Y5vRouLaA== + dependencies: + "@types/node" "^18.11.18" + +"@types/ssh2@^0.5.48": + version "0.5.52" + resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-0.5.52.tgz#9dbd8084e2a976e551d5e5e70b978ed8b5965741" + integrity sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg== + dependencies: + "@types/node" "*" + "@types/ssh2-streams" "*" + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +archiver-utils@^5.0.0, archiver-utils@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-5.0.2.tgz#63bc719d951803efc72cf961a56ef810760dd14d" + integrity sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA== + dependencies: + glob "^10.0.0" + graceful-fs "^4.2.0" + is-stream "^2.0.1" + lazystream "^1.0.0" + lodash "^4.17.15" + normalize-path "^3.0.0" + readable-stream "^4.0.0" + +archiver@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-7.0.1.tgz#c9d91c350362040b8927379c7aa69c0655122f61" + integrity sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ== + dependencies: + archiver-utils "^5.0.2" + async "^3.2.4" + buffer-crc32 "^1.0.0" + readable-stream "^4.0.0" + readdir-glob "^1.1.2" + tar-stream "^3.0.0" + zip-stream "^6.0.1" + +asn1@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +async-lock@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.4.1.tgz#56b8718915a9b68b10fce2f2a9a3dddf765ef53f" + integrity sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ== + +async@^3.2.4: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axe-core@~4.10.2: + version "4.10.3" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.3.tgz#04145965ac7894faddbac30861e5d8f11bfd14fc" + integrity sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg== + +axios@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.2.tgz#fabe06e241dfe83071d4edfbcaa7b1c3a40f7979" + integrity sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +b4a@^1.6.4: + version "1.6.7" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4" + integrity sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +bare-events@^2.0.0, bare-events@^2.2.0: + version "2.5.4" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.5.4.tgz#16143d435e1ed9eafd1ab85f12b89b3357a41745" + integrity sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA== + +bare-fs@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-4.0.1.tgz#85844f34da819c76754d545323a8b23ed3617c76" + integrity sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg== + dependencies: + bare-events "^2.0.0" + bare-path "^3.0.0" + bare-stream "^2.0.0" + +bare-os@^3.0.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-3.5.1.tgz#8e59ad8db6d0eab35cfe499208db643fd5f4c594" + integrity sha512-LvfVNDcWLw2AnIw5f2mWUgumW3I3N/WYGiWeimhQC1Ybt71n2FjlS9GJKeCnFeg1MKZHxzIFmpFnBXDI+sBeFg== + +bare-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-3.0.0.tgz#b59d18130ba52a6af9276db3e96a2e3d3ea52178" + integrity sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw== + dependencies: + bare-os "^3.0.1" + +bare-stream@^2.0.0: + version "2.6.5" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.6.5.tgz#bba8e879674c4c27f7e27805df005c15d7a2ca07" + integrity sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA== + dependencies: + streamx "^2.21.0" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +buffer-crc32@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405" + integrity sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w== + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +buildcheck@~0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" + integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== + +byline@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +compress-commons@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-6.0.2.tgz#26d31251a66b9d6ba23a84064ecd3a6a71d2609e" + integrity sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg== + dependencies: + crc-32 "^1.2.0" + crc32-stream "^6.0.0" + is-stream "^2.0.1" + normalize-path "^3.0.0" + readable-stream "^4.0.0" + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cpu-features@~0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.10.tgz#9aae536db2710c7254d7ed67cb3cbc7d29ad79c5" + integrity sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA== + dependencies: + buildcheck "~0.0.6" + nan "^2.19.0" + +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +crc32-stream@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-6.0.0.tgz#8529a3868f8b27abb915f6c3617c0fadedbf9430" + integrity sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g== + dependencies: + crc-32 "^1.2.0" + readable-stream "^4.0.0" + +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.1.1, debug@^4.3.5: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +docker-compose@^0.24.8: + version "0.24.8" + resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.24.8.tgz#6c125e6b9e04cf68ced47e2596ef2bb93ee9694e" + integrity sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw== + dependencies: + yaml "^2.2.2" + +docker-modem@^3.0.0: + version "3.0.8" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-3.0.8.tgz#ef62c8bdff6e8a7d12f0160988c295ea8705e77a" + integrity sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ== + dependencies: + debug "^4.1.1" + readable-stream "^3.5.0" + split-ca "^1.0.1" + ssh2 "^1.11.0" + +dockerode@^3.3.5: + version "3.3.5" + resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-3.3.5.tgz#7ae3f40f2bec53ae5e9a741ce655fff459745629" + integrity sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA== + dependencies: + "@balena/dockerignore" "^1.0.2" + docker-modem "^3.0.0" + tar-fs "~2.0.1" + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +fast-fifo@^1.2.0, fast-fifo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +foreground-child@^3.1.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" + +form-data@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c" + integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + mime-types "^2.1.12" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fsevents@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-port@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +glob@^10.0.0: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.2.0, graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +ieee754@^1.1.13, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-stream@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +lazystream@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" + integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== + dependencies: + readable-stream "^2.0.5" + +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + +lodash@^4.17.15: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +mailpit-api@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mailpit-api/-/mailpit-api-1.2.0.tgz#6cbd7c5c091fd74b000385790a1fe0c9f2a83fba" + integrity sha512-oni/IwQhtbwk3ERwJ6IarKIFgz2U5684SK6Bbkau2GBo2FLoiT14UGkL3CXleYPBH5SCsnymHap1eevEOLwqaA== + dependencies: + axios "^1.8.1" + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +minimatch@^5.1.0: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nan@^2.19.0, nan@^2.20.0: + version "2.22.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.2.tgz#6b504fd029fb8f38c0990e52ad5c26772fdacfbb" + integrity sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ== + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +playwright-core@1.51.0, playwright-core@^1.51.0: + version "1.51.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.51.0.tgz#bb23ea6bb6298242d088ae5e966ffcf8dc9827e8" + integrity sha512-x47yPE3Zwhlil7wlNU/iktF7t2r/URR3VLbH6EknJd/04Qc/PSJ0EY3CMXipmglLG+zyRxW6HNo2EGbKLHPWMg== + +playwright@1.51.0: + version "1.51.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.51.0.tgz#9ba154497ba62bc6dc199c58ee19295eb35a4707" + integrity sha512-442pTfGM0xxfCYxuBa/Pu6B2OqxqqaYq39JS8QDMGThUvIOCd6s0ANDog3uwA0cHavVlnTQzGCN7Id2YekDSXA== + dependencies: + playwright-core "1.51.0" + optionalDependencies: + fsevents "2.3.2" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +proper-lockfile@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + +properties-reader@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/properties-reader/-/properties-reader-2.3.0.tgz#f3ab84224c9535a7a36e011ae489a79a13b472b2" + integrity sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw== + dependencies: + mkdirp "^1.0.4" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +pump@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" + integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +readable-stream@^2.0.5: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^4.0.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.7.0.tgz#cedbd8a1146c13dfff8dab14068028d58c15ac91" + integrity sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + +readdir-glob@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" + integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== + dependencies: + minimatch "^5.1.0" + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +split-ca@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" + integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== + +ssh-remote-port-forward@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz#72b0c5df8ec27ca300c75805cc6b266dee07e298" + integrity sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ== + dependencies: + "@types/ssh2" "^0.5.48" + ssh2 "^1.4.0" + +ssh2@^1.11.0, ssh2@^1.4.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.16.0.tgz#79221d40cbf4d03d07fe881149de0a9de928c9f0" + integrity sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg== + dependencies: + asn1 "^0.2.6" + bcrypt-pbkdf "^1.0.2" + optionalDependencies: + cpu-features "~0.0.10" + nan "^2.20.0" + +streamx@^2.15.0, streamx@^2.21.0: + version "2.22.0" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.22.0.tgz#cd7b5e57c95aaef0ff9b2aef7905afa62ec6e4a7" + integrity sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw== + dependencies: + fast-fifo "^1.3.2" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string_decoder@^1.1.1, string_decoder@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1, strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +tar-fs@^3.0.6: + version "3.0.8" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.8.tgz#8f62012537d5ff89252d01e48690dc4ebed33ab7" + integrity sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg== + dependencies: + pump "^3.0.0" + tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^4.0.1" + bare-path "^3.0.0" + +tar-fs@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2" + integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.0.0" + +tar-stream@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar-stream@^3.0.0, tar-stream@^3.1.5: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + +testcontainers@^10.18.0: + version "10.18.0" + resolved "https://registry.yarnpkg.com/testcontainers/-/testcontainers-10.18.0.tgz#dbd74e1d6e5de431414a06fb2c2ed9d82971739e" + integrity sha512-MnwWsPjsN5QVe+lSU1LwLZVOyjgwSwv1INzkw8FekdwgvOtvJ7FThQEkbmzRcguQootgwmA9FG54NoTChZDRvA== + dependencies: + "@balena/dockerignore" "^1.0.2" + "@types/dockerode" "^3.3.29" + archiver "^7.0.1" + async-lock "^1.4.1" + byline "^5.0.0" + debug "^4.3.5" + docker-compose "^0.24.8" + dockerode "^3.3.5" + get-port "^5.1.1" + proper-lockfile "^4.1.2" + properties-reader "^2.3.0" + ssh-remote-port-forward "^1.0.4" + tar-fs "^3.0.6" + tmp "^0.2.3" + undici "^5.28.5" + +text-decoder@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.3.tgz#b19da364d981b2326d5f43099c310cc80d770c65" + integrity sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA== + dependencies: + b4a "^1.6.4" + +tmp@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" + integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== + +tweetnacl@^0.14.3: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + +typescript@^5.8.2: + version "5.8.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4" + integrity sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + +undici@^5.28.5: + version "5.28.5" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.5.tgz#b2b94b6bf8f1d919bc5a6f31f2c01deb02e54d4b" + integrity sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA== + dependencies: + "@fastify/busboy" "^2.0.0" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yaml@^2.2.2, yaml@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" + integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== + +zip-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-6.0.1.tgz#e141b930ed60ccaf5d7fa9c8260e0d1748a2bbfb" + integrity sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA== + dependencies: + archiver-utils "^5.0.0" + compress-commons "^6.0.2" + readable-stream "^4.0.0" From 0b12fac20064ebb8fea4260ab6f110d2e08ad4d6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Mar 2025 10:33:29 +0000 Subject: [PATCH 002/113] Iterate --- packages/element-web-playwright-common/package.json | 1 + packages/element-web-playwright-common/src/expect/screenshot.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 5acdbdd3bf..5157e2a52e 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -21,6 +21,7 @@ "yaml": "^2.7.0" }, "peerDependencies": { + "@element-hq/element-web-module-api": "*", "@playwright/test": "^1.51.0", "playwright-core": "^1.51.0" }, diff --git a/packages/element-web-playwright-common/src/expect/screenshot.ts b/packages/element-web-playwright-common/src/expect/screenshot.ts index a194b52671..37726fd958 100644 --- a/packages/element-web-playwright-common/src/expect/screenshot.ts +++ b/packages/element-web-playwright-common/src/expect/screenshot.ts @@ -14,8 +14,8 @@ import { type Locator, type Page, type PageAssertionsToHaveScreenshotOptions, + type MatcherReturnType, } from "@playwright/test"; -import { type MatcherReturnType } from "playwright/types/test"; import { sanitizeForFilePath } from "playwright-core/lib/utils"; import { extname } from "node:path"; From 0a4706da1e318bda121e7acbd2d5c0aff5cfa72d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Mar 2025 10:39:48 +0000 Subject: [PATCH 003/113] Iterate --- packages/element-web-playwright-common/package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 5157e2a52e..97ceec39de 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -7,6 +7,10 @@ "bin": { "playwright-screenshots": "./playwright-screenshots.sh" }, + "scripts": { + "prepare": "tsc", + "test": "echo No tests for @element-hq/element-web-playwright-common" + }, "devDependencies": { "@types/lodash-es": "^4.17.12", "typescript": "^5.8.2" @@ -24,8 +28,5 @@ "@element-hq/element-web-module-api": "*", "@playwright/test": "^1.51.0", "playwright-core": "^1.51.0" - }, - "scripts": { - "prepare": "tsc" } } From c9288b846ebecab31ce479fda8cdf2bd8d3a68f3 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Mar 2025 10:50:09 +0000 Subject: [PATCH 004/113] Iterate --- packages/element-web-playwright-common/package.json | 2 +- .../src/testcontainers/synapse.ts | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 97ceec39de..e67fde9403 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.0.0", + "version": "1.1.0", "license": "SEE LICENSE IN README.md", "main": "lib/index.js", "bin": { diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index 7c7248ee84..0ccdfb4070 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -1,5 +1,5 @@ /* -Copyright 2024 New Vector Ltd. +Copyright 2024-2025 New Vector Ltd. SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. @@ -25,8 +25,6 @@ import { type StartedMatrixAuthenticationServiceContainer } from "./mas.js"; import { Api, ClientServerApi, type Verb, type Credentials } from "../utils/api.js"; import { StartedMailpitContainer } from "./mailpit.js"; -const TAG = "develop@sha256:8d0049e8e0524ad6817cf7737453fe47de1ed3b8d04704f0c2fd6c136414c9d7"; - const DEFAULT_CONFIG = { server_name: "localhost", public_baseurl: "", // set by start method @@ -193,8 +191,8 @@ export class SynapseContainer extends GenericContainer implements HomeserverCont private config: SynapseConfig; private mas?: StartedMatrixAuthenticationServiceContainer; - public constructor() { - super(`ghcr.io/element-hq/synapse:${TAG}`); + public constructor(image = "ghcr.io/element-hq/synapse:develop") { + super(image); this.config = deepCopy(DEFAULT_CONFIG); this.config.registration_shared_secret = randB64Bytes(16); From 2fa2805b8d94849cf49d960e17433cf168309b15 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Mar 2025 11:31:38 +0000 Subject: [PATCH 005/113] Iterate --- .../element-web-playwright-common/src/index.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/src/index.ts b/packages/element-web-playwright-common/src/index.ts index 59df040324..4f13e8be2d 100644 --- a/packages/element-web-playwright-common/src/index.ts +++ b/packages/element-web-playwright-common/src/index.ts @@ -27,6 +27,7 @@ export interface Config extends BaseConfig { server_name?: string; }; }; + enable_presence_by_hs_url?: Record; setting_defaults: Record; map_style_url?: string; features: Record; @@ -58,12 +59,15 @@ export interface TestFixtures { config: Partial; labsFlags: string[]; + disablePresence: boolean; } export const test = base.extend({ - config: {}, // We merge this atop the default CONFIG_JSON in the page fixture to make extending it easier - labsFlags: [], - page: async ({ homeserver, context, page, config, labsFlags }, use) => { + // We merge this atop the default CONFIG_JSON in the page fixture to make extending it easier + config: async ({}, use) => use({}), + labsFlags: async ({}, use) => use([]), + disablePresence: async ({}, use) => use(false), + page: async ({ homeserver, context, page, config, labsFlags, disablePresence }, use) => { await context.route(`http://localhost:8080/config.json*`, async (route) => { const json = { ...CONFIG_JSON, @@ -83,6 +87,11 @@ export const test = base.extend({ return obj; }, {}), }; + if (disablePresence) { + json["enable_presence_by_hs_url"] = { + [homeserver.baseUrl]: false, + }; + } await route.fulfill({ json }); }); await use(page); From cae6c42eb4426f8b822ac5eff06f15df678e6773 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Mar 2025 11:41:28 +0000 Subject: [PATCH 006/113] Iterate --- packages/element-web-playwright-common/src/fixtures/user.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/src/fixtures/user.ts b/packages/element-web-playwright-common/src/fixtures/user.ts index f0f0bda05d..74c4c17a3d 100644 --- a/packages/element-web-playwright-common/src/fixtures/user.ts +++ b/packages/element-web-playwright-common/src/fixtures/user.ts @@ -74,7 +74,7 @@ export const test = base.extend<{ "mx_local_settings", JSON.stringify({ // Retain any other settings which may have already been set - ...JSON.parse(window.localStorage.getItem("mx_local_settings") || "{}"), + ...JSON.parse(window.localStorage.getItem("mx_local_settings") ?? "{}"), // Ensure the language is set to a consistent value language: "en", }), From 5d866e86ba22546e039e7992810110f6e9e0d4ee Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 11:24:52 +0000 Subject: [PATCH 007/113] Add repository field to fix provenance --- packages/element-web-playwright-common/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index e67fde9403..e0f84eed8d 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,8 +1,9 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.1.0", + "version": "1.1.1", "license": "SEE LICENSE IN README.md", + "repository": "https://github.com/element-hq/element-web-modules", "main": "lib/index.js", "bin": { "playwright-screenshots": "./playwright-screenshots.sh" From 0fa87a2d7e214071b292b98218b138a6520f685d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 11:32:18 +0000 Subject: [PATCH 008/113] Fix package.json bin & repository fields --- packages/element-web-playwright-common/package.json | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index e0f84eed8d..d24ee68a49 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,12 +1,16 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.1.1", + "version": "1.1.2", "license": "SEE LICENSE IN README.md", - "repository": "https://github.com/element-hq/element-web-modules", + "repository": { + "type": "git", + "url": "git+https://github.com/element-hq/element-web-modules.git", + "directory": "packages/element-web-playwright-common" + }, "main": "lib/index.js", "bin": { - "playwright-screenshots": "./playwright-screenshots.sh" + "playwright-screenshots": "playwright-screenshots.sh" }, "scripts": { "prepare": "tsc", From 98ae690c6f4f35cc29be45d312f69e9cd9f43149 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 11:40:42 +0000 Subject: [PATCH 009/113] Who'd have thunk provenance doesn't behave as the docs describe --- packages/element-web-playwright-common/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index d24ee68a49..d64642aa1b 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,11 +1,11 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.1.2", + "version": "1.1.3", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", - "url": "git+https://github.com/element-hq/element-web-modules.git", + "url": "https://github.com/element-hq/element-web-modules", "directory": "packages/element-web-playwright-common" }, "main": "lib/index.js", From df19fbc259e4fc6297ba41d1f9eeeb7baf4ab6de Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 11:53:26 +0000 Subject: [PATCH 010/113] Specify node version --- packages/element-web-playwright-common/package.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index d64642aa1b..97f381e59d 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,13 +1,17 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.1.3", + "version": "1.1.4", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", - "url": "https://github.com/element-hq/element-web-modules", + "url": "git+https://github.com/element-hq/element-web-modules.git", "directory": "packages/element-web-playwright-common" }, + "author": "element-hq", + "engines": { + "node": ">=20.0.0" + }, "main": "lib/index.js", "bin": { "playwright-screenshots": "playwright-screenshots.sh" From e6bdd86c4c2f7e0c83ad8225a1f4c2aa0475a8f4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 11:59:17 +0000 Subject: [PATCH 011/113] Fix repository URLs --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 97f381e59d..46855425b0 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -5,7 +5,7 @@ "license": "SEE LICENSE IN README.md", "repository": { "type": "git", - "url": "git+https://github.com/element-hq/element-web-modules.git", + "url": "git+https://github.com/element-hq/element-modules.git", "directory": "packages/element-web-playwright-common" }, "author": "element-hq", From 98cc18fd5f2e8518ecc900c36dbfe3f1ca0ce347 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Mar 2025 11:59:43 +0000 Subject: [PATCH 012/113] Bump versions --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 46855425b0..c67f2745ab 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.1.4", + "version": "1.1.5", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From d93c34926b1e7502119fe2cd687f5ab9f656e561 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 21 Mar 2025 17:34:39 +0000 Subject: [PATCH 013/113] Make mas-cli testcontainer method public --- .../element-web-playwright-common/src/testcontainers/mas.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts index 292e7b117c..70c2e50c2f 100644 --- a/packages/element-web-playwright-common/src/testcontainers/mas.ts +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -277,7 +277,7 @@ export class StartedMatrixAuthenticationServiceContainer extends AbstractStarted return this.adminTokenPromise; } - private async manage(cmd: string, ...args: string[]): Promise { + public async manage(cmd: string, ...args: string[]): Promise { const result = await this.exec(["mas-cli", "manage", cmd, ...this.args, ...args]); if (result.exitCode !== 0) { throw new Error(`Failed mas-cli manage ${cmd}: ${result.output}`); From 586ad25600447f6a682fad87f42bccbb1d261c88 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 11 Apr 2025 09:26:32 +0100 Subject: [PATCH 014/113] Update testcontainers, tar-fs and remove duplicate script and stray lockfiles --- .../package.json | 4 +- .../element-web-playwright-common/yarn.lock | 1141 ----------------- 2 files changed, 2 insertions(+), 1143 deletions(-) delete mode 100644 packages/element-web-playwright-common/yarn.lock diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index c67f2745ab..6cfdc59adc 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -26,11 +26,11 @@ }, "dependencies": { "@axe-core/playwright": "^4.10.1", - "@testcontainers/postgresql": "^10.18.0", + "@testcontainers/postgresql": "^10.24.2", "lodash-es": "^4.17.21", "mailpit-api": "^1.2.0", "strip-ansi": "^7.1.0", - "testcontainers": "^10.18.0", + "testcontainers": "^10.24.2", "yaml": "^2.7.0" }, "peerDependencies": { diff --git a/packages/element-web-playwright-common/yarn.lock b/packages/element-web-playwright-common/yarn.lock deleted file mode 100644 index 9cf11fe42f..0000000000 --- a/packages/element-web-playwright-common/yarn.lock +++ /dev/null @@ -1,1141 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@axe-core/playwright@^4.10.1": - version "4.10.1" - resolved "https://registry.yarnpkg.com/@axe-core/playwright/-/playwright-4.10.1.tgz#c811ba8bfa244833cce422c4131e0043828c42cc" - integrity sha512-EV5t39VV68kuAfMKqb/RL+YjYKhfuGim9rgIaQ6Vntb2HgaCaau0h98Y3WEUqW1+PbdzxDtDNjFAipbtZuBmEA== - dependencies: - axe-core "~4.10.2" - -"@balena/dockerignore@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" - integrity sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q== - -"@fastify/busboy@^2.0.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" - integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== - -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - -"@playwright/test@^1.51.0": - version "1.51.0" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.51.0.tgz#8d5c8400b465a0bfdbcf993e390ceecb903ea6d2" - integrity sha512-dJ0dMbZeHhI+wb77+ljx/FeC8VBP6j/rj9OAojO08JI80wTZy6vRk9KvHKiDCUh4iMpEiseMgqRBIeW+eKX6RA== - dependencies: - playwright "1.51.0" - -"@testcontainers/postgresql@^10.18.0": - version "10.18.0" - resolved "https://registry.yarnpkg.com/@testcontainers/postgresql/-/postgresql-10.18.0.tgz#87d3acb3a4bc2196bd8e9b7496fe6e3146b59f2b" - integrity sha512-WxkE/tBlBpoKvqDEqL3i/mL6BOBWnXb8FXKtLhEeZ3lSt0zlldkTozMmewNsKJtFTBZdv7uFwMzWyXP12t0sxQ== - dependencies: - testcontainers "^10.18.0" - -"@types/docker-modem@*": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@types/docker-modem/-/docker-modem-3.0.6.tgz#1f9262fcf85425b158ca725699a03eb23cddbf87" - integrity sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg== - dependencies: - "@types/node" "*" - "@types/ssh2" "*" - -"@types/dockerode@^3.3.29": - version "3.3.35" - resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-3.3.35.tgz#d78c844a246f8717e3bcf2cc134a976bfa630b10" - integrity sha512-P+DCMASlsH+QaKkDpekKrP5pLls767PPs+/LrlVbKnEnY5tMpEUa2C6U4gRsdFZengOqxdCIqy16R22Q3pLB6Q== - dependencies: - "@types/docker-modem" "*" - "@types/node" "*" - "@types/ssh2" "*" - -"@types/lodash-es@^4.17.12": - version "4.17.12" - resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" - integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== - dependencies: - "@types/lodash" "*" - -"@types/lodash@*": - version "4.17.16" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.16.tgz#94ae78fab4a38d73086e962d0b65c30d816bfb0a" - integrity sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g== - -"@types/node@*": - version "22.13.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.10.tgz#df9ea358c5ed991266becc3109dc2dc9125d77e4" - integrity sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw== - dependencies: - undici-types "~6.20.0" - -"@types/node@^18.11.18": - version "18.19.80" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.80.tgz#6d6008e8920dddcd23f9dd33da24684ef57d487c" - integrity sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ== - dependencies: - undici-types "~5.26.4" - -"@types/ssh2-streams@*": - version "0.1.12" - resolved "https://registry.yarnpkg.com/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz#e68795ba2bf01c76b93f9c9809e1f42f0eaaec5f" - integrity sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg== - dependencies: - "@types/node" "*" - -"@types/ssh2@*": - version "1.15.4" - resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-1.15.4.tgz#2347d2ff079e205b077c02407d822803bfd23c45" - integrity sha512-9JTQgVBWSgq6mAen6PVnrAmty1lqgCMvpfN+1Ck5WRUsyMYPa6qd50/vMJ0y1zkGpOEgLzm8m8Dx/Y5vRouLaA== - dependencies: - "@types/node" "^18.11.18" - -"@types/ssh2@^0.5.48": - version "0.5.52" - resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-0.5.52.tgz#9dbd8084e2a976e551d5e5e70b978ed8b5965741" - integrity sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg== - dependencies: - "@types/node" "*" - "@types/ssh2-streams" "*" - -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== - -ansi-styles@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - -archiver-utils@^5.0.0, archiver-utils@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-5.0.2.tgz#63bc719d951803efc72cf961a56ef810760dd14d" - integrity sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA== - dependencies: - glob "^10.0.0" - graceful-fs "^4.2.0" - is-stream "^2.0.1" - lazystream "^1.0.0" - lodash "^4.17.15" - normalize-path "^3.0.0" - readable-stream "^4.0.0" - -archiver@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/archiver/-/archiver-7.0.1.tgz#c9d91c350362040b8927379c7aa69c0655122f61" - integrity sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ== - dependencies: - archiver-utils "^5.0.2" - async "^3.2.4" - buffer-crc32 "^1.0.0" - readable-stream "^4.0.0" - readdir-glob "^1.1.2" - tar-stream "^3.0.0" - zip-stream "^6.0.1" - -asn1@^0.2.6: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - -async-lock@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.4.1.tgz#56b8718915a9b68b10fce2f2a9a3dddf765ef53f" - integrity sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ== - -async@^3.2.4: - version "3.2.6" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" - integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -axe-core@~4.10.2: - version "4.10.3" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.3.tgz#04145965ac7894faddbac30861e5d8f11bfd14fc" - integrity sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg== - -axios@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.2.tgz#fabe06e241dfe83071d4edfbcaa7b1c3a40f7979" - integrity sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg== - dependencies: - follow-redirects "^1.15.6" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - -b4a@^1.6.4: - version "1.6.7" - resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4" - integrity sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -bare-events@^2.0.0, bare-events@^2.2.0: - version "2.5.4" - resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.5.4.tgz#16143d435e1ed9eafd1ab85f12b89b3357a41745" - integrity sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA== - -bare-fs@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-4.0.1.tgz#85844f34da819c76754d545323a8b23ed3617c76" - integrity sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg== - dependencies: - bare-events "^2.0.0" - bare-path "^3.0.0" - bare-stream "^2.0.0" - -bare-os@^3.0.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-3.5.1.tgz#8e59ad8db6d0eab35cfe499208db643fd5f4c594" - integrity sha512-LvfVNDcWLw2AnIw5f2mWUgumW3I3N/WYGiWeimhQC1Ybt71n2FjlS9GJKeCnFeg1MKZHxzIFmpFnBXDI+sBeFg== - -bare-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-3.0.0.tgz#b59d18130ba52a6af9276db3e96a2e3d3ea52178" - integrity sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw== - dependencies: - bare-os "^3.0.1" - -bare-stream@^2.0.0: - version "2.6.5" - resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.6.5.tgz#bba8e879674c4c27f7e27805df005c15d7a2ca07" - integrity sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA== - dependencies: - streamx "^2.21.0" - -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -bcrypt-pbkdf@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - -bl@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -buffer-crc32@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405" - integrity sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w== - -buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -buildcheck@~0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" - integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== - -byline@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" - integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== - -call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" - integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -compress-commons@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-6.0.2.tgz#26d31251a66b9d6ba23a84064ecd3a6a71d2609e" - integrity sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg== - dependencies: - crc-32 "^1.2.0" - crc32-stream "^6.0.0" - is-stream "^2.0.1" - normalize-path "^3.0.0" - readable-stream "^4.0.0" - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -cpu-features@~0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.10.tgz#9aae536db2710c7254d7ed67cb3cbc7d29ad79c5" - integrity sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA== - dependencies: - buildcheck "~0.0.6" - nan "^2.19.0" - -crc-32@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" - integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== - -crc32-stream@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-6.0.0.tgz#8529a3868f8b27abb915f6c3617c0fadedbf9430" - integrity sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g== - dependencies: - crc-32 "^1.2.0" - readable-stream "^4.0.0" - -cross-spawn@^7.0.6: - version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" - integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -debug@^4.1.1, debug@^4.3.5: - version "4.4.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" - integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== - dependencies: - ms "^2.1.3" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -docker-compose@^0.24.8: - version "0.24.8" - resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.24.8.tgz#6c125e6b9e04cf68ced47e2596ef2bb93ee9694e" - integrity sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw== - dependencies: - yaml "^2.2.2" - -docker-modem@^3.0.0: - version "3.0.8" - resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-3.0.8.tgz#ef62c8bdff6e8a7d12f0160988c295ea8705e77a" - integrity sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ== - dependencies: - debug "^4.1.1" - readable-stream "^3.5.0" - split-ca "^1.0.1" - ssh2 "^1.11.0" - -dockerode@^3.3.5: - version "3.3.5" - resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-3.3.5.tgz#7ae3f40f2bec53ae5e9a741ce655fff459745629" - integrity sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA== - dependencies: - "@balena/dockerignore" "^1.0.2" - docker-modem "^3.0.0" - tar-fs "~2.0.1" - -dunder-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" - integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== - dependencies: - call-bind-apply-helpers "^1.0.1" - es-errors "^1.3.0" - gopd "^1.2.0" - -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -es-define-property@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" - integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" - integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== - dependencies: - es-errors "^1.3.0" - -es-set-tostringtag@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" - integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== - dependencies: - es-errors "^1.3.0" - get-intrinsic "^1.2.6" - has-tostringtag "^1.0.2" - hasown "^2.0.2" - -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - -events@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -fast-fifo@^1.2.0, fast-fifo@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" - integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== - -follow-redirects@^1.15.6: - version "1.15.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" - integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== - -foreground-child@^3.1.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" - integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== - dependencies: - cross-spawn "^7.0.6" - signal-exit "^4.0.1" - -form-data@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c" - integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - es-set-tostringtag "^2.1.0" - mime-types "^2.1.12" - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fsevents@2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -get-intrinsic@^1.2.6: - version "1.3.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" - integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== - dependencies: - call-bind-apply-helpers "^1.0.2" - es-define-property "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.1.1" - function-bind "^1.1.2" - get-proto "^1.0.1" - gopd "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - math-intrinsics "^1.1.0" - -get-port@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - -get-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" - integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== - dependencies: - dunder-proto "^1.0.1" - es-object-atoms "^1.0.0" - -glob@^10.0.0: - version "10.4.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" - integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^1.11.1" - -gopd@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" - integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== - -graceful-fs@^4.2.0, graceful-fs@^4.2.4: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -has-symbols@^1.0.3, has-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" - integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== - -has-tostringtag@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -ieee754@^1.1.13, ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-stream@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -jackspeak@^3.1.2: - version "3.4.3" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" - integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - -lazystream@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" - integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== - dependencies: - readable-stream "^2.0.5" - -lodash-es@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" - integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== - -lodash@^4.17.15: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lru-cache@^10.2.0: - version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== - -mailpit-api@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mailpit-api/-/mailpit-api-1.2.0.tgz#6cbd7c5c091fd74b000385790a1fe0c9f2a83fba" - integrity sha512-oni/IwQhtbwk3ERwJ6IarKIFgz2U5684SK6Bbkau2GBo2FLoiT14UGkL3CXleYPBH5SCsnymHap1eevEOLwqaA== - dependencies: - axios "^1.8.1" - -math-intrinsics@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" - integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -minimatch@^5.1.0: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^9.0.4: - version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== - dependencies: - brace-expansion "^2.0.1" - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== - -mkdirp-classic@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -ms@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nan@^2.19.0, nan@^2.20.0: - version "2.22.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.2.tgz#6b504fd029fb8f38c0990e52ad5c26772fdacfbb" - integrity sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ== - -normalize-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -package-json-from-dist@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" - integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-scurry@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" - integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - -playwright-core@1.51.0, playwright-core@^1.51.0: - version "1.51.0" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.51.0.tgz#bb23ea6bb6298242d088ae5e966ffcf8dc9827e8" - integrity sha512-x47yPE3Zwhlil7wlNU/iktF7t2r/URR3VLbH6EknJd/04Qc/PSJ0EY3CMXipmglLG+zyRxW6HNo2EGbKLHPWMg== - -playwright@1.51.0: - version "1.51.0" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.51.0.tgz#9ba154497ba62bc6dc199c58ee19295eb35a4707" - integrity sha512-442pTfGM0xxfCYxuBa/Pu6B2OqxqqaYq39JS8QDMGThUvIOCd6s0ANDog3uwA0cHavVlnTQzGCN7Id2YekDSXA== - dependencies: - playwright-core "1.51.0" - optionalDependencies: - fsevents "2.3.2" - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== - -proper-lockfile@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" - integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== - dependencies: - graceful-fs "^4.2.4" - retry "^0.12.0" - signal-exit "^3.0.2" - -properties-reader@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/properties-reader/-/properties-reader-2.3.0.tgz#f3ab84224c9535a7a36e011ae489a79a13b472b2" - integrity sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw== - dependencies: - mkdirp "^1.0.4" - -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -pump@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" - integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -readable-stream@^2.0.5: - version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@^4.0.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.7.0.tgz#cedbd8a1146c13dfff8dab14068028d58c15ac91" - integrity sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg== - dependencies: - abort-controller "^3.0.0" - buffer "^6.0.3" - events "^3.3.0" - process "^0.11.10" - string_decoder "^1.3.0" - -readdir-glob@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" - integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== - dependencies: - minimatch "^5.1.0" - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -signal-exit@^3.0.2: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - -split-ca@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" - integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== - -ssh-remote-port-forward@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz#72b0c5df8ec27ca300c75805cc6b266dee07e298" - integrity sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ== - dependencies: - "@types/ssh2" "^0.5.48" - ssh2 "^1.4.0" - -ssh2@^1.11.0, ssh2@^1.4.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.16.0.tgz#79221d40cbf4d03d07fe881149de0a9de928c9f0" - integrity sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg== - dependencies: - asn1 "^0.2.6" - bcrypt-pbkdf "^1.0.2" - optionalDependencies: - cpu-features "~0.0.10" - nan "^2.20.0" - -streamx@^2.15.0, streamx@^2.21.0: - version "2.22.0" - resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.22.0.tgz#cd7b5e57c95aaef0ff9b2aef7905afa62ec6e4a7" - integrity sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw== - dependencies: - fast-fifo "^1.3.2" - text-decoder "^1.1.0" - optionalDependencies: - bare-events "^2.2.0" - -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -string_decoder@^1.1.1, string_decoder@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1, strip-ansi@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - -tar-fs@^3.0.6: - version "3.0.8" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.8.tgz#8f62012537d5ff89252d01e48690dc4ebed33ab7" - integrity sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg== - dependencies: - pump "^3.0.0" - tar-stream "^3.1.5" - optionalDependencies: - bare-fs "^4.0.1" - bare-path "^3.0.0" - -tar-fs@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2" - integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.0.0" - -tar-stream@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -tar-stream@^3.0.0, tar-stream@^3.1.5: - version "3.1.7" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" - integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== - dependencies: - b4a "^1.6.4" - fast-fifo "^1.2.0" - streamx "^2.15.0" - -testcontainers@^10.18.0: - version "10.18.0" - resolved "https://registry.yarnpkg.com/testcontainers/-/testcontainers-10.18.0.tgz#dbd74e1d6e5de431414a06fb2c2ed9d82971739e" - integrity sha512-MnwWsPjsN5QVe+lSU1LwLZVOyjgwSwv1INzkw8FekdwgvOtvJ7FThQEkbmzRcguQootgwmA9FG54NoTChZDRvA== - dependencies: - "@balena/dockerignore" "^1.0.2" - "@types/dockerode" "^3.3.29" - archiver "^7.0.1" - async-lock "^1.4.1" - byline "^5.0.0" - debug "^4.3.5" - docker-compose "^0.24.8" - dockerode "^3.3.5" - get-port "^5.1.1" - proper-lockfile "^4.1.2" - properties-reader "^2.3.0" - ssh-remote-port-forward "^1.0.4" - tar-fs "^3.0.6" - tmp "^0.2.3" - undici "^5.28.5" - -text-decoder@^1.1.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.3.tgz#b19da364d981b2326d5f43099c310cc80d770c65" - integrity sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA== - dependencies: - b4a "^1.6.4" - -tmp@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" - integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== - -tweetnacl@^0.14.3: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - -typescript@^5.8.2: - version "5.8.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4" - integrity sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ== - -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - -undici-types@~6.20.0: - version "6.20.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" - integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== - -undici@^5.28.5: - version "5.28.5" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.5.tgz#b2b94b6bf8f1d919bc5a6f31f2c01deb02e54d4b" - integrity sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA== - dependencies: - "@fastify/busboy" "^2.0.0" - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -yaml@^2.2.2, yaml@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" - integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== - -zip-stream@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-6.0.1.tgz#e141b930ed60ccaf5d7fa9c8260e0d1748a2bbfb" - integrity sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA== - dependencies: - archiver-utils "^5.0.0" - compress-commons "^6.0.2" - readable-stream "^4.0.0" From e8ed2f8f41218c1764ba539d248ba56aa1ecd300 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 30 Apr 2025 13:26:49 +0100 Subject: [PATCH 015/113] Update playwright --- packages/element-web-playwright-common/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 6cfdc59adc..abbf57755c 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -35,7 +35,7 @@ }, "peerDependencies": { "@element-hq/element-web-module-api": "*", - "@playwright/test": "^1.51.0", - "playwright-core": "^1.51.0" + "@playwright/test": "^1.52.0", + "playwright-core": "^1.52.0" } } From ad0a276af2036dc63dd11c663888795f99b3344d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 7 May 2025 12:02:12 +0100 Subject: [PATCH 016/113] Bump element-web-playwright-common version --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index abbf57755c..f09cee86ae 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.1.5", + "version": "1.1.6", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From b55feb5a7bf91c487c81fb3c19d0a262d00556f7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 8 May 2025 12:23:13 +0100 Subject: [PATCH 017/113] Standardise lint/type scripts between modules/packages --- packages/element-web-playwright-common/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index f09cee86ae..d0569decc9 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -18,6 +18,8 @@ }, "scripts": { "prepare": "tsc", + "lint:types": "tsc --noEmit", + "lint:codestyle": "echo 'handled by lint:eslint'", "test": "echo No tests for @element-hq/element-web-playwright-common" }, "devDependencies": { From 29d1c61d8239678687d6c412709acd8cc2eb3a72 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 9 May 2025 17:08:40 +0100 Subject: [PATCH 018/113] Update mas version --- .../element-web-playwright-common/src/testcontainers/mas.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts index 70c2e50c2f..935451cc70 100644 --- a/packages/element-web-playwright-common/src/testcontainers/mas.ts +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -195,9 +195,8 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { private readonly args = ["-c", "/config/config.yaml"]; public constructor(db: StartedPostgreSqlContainer) { - // We rely on `mas-cli manage add-email` which isn't in a release yet - // https://github.com/element-hq/matrix-authentication-service/pull/3235 - super("ghcr.io/element-hq/matrix-authentication-service:sha-0b90c33"); + // We rely on https://github.com/element-hq/matrix-authentication-service/pull/4529 which isn't in a release yet + super("ghcr.io/element-hq/matrix-authentication-service:sha-98f2776"); this.config = deepCopy(DEFAULT_CONFIG); this.config.database.username = db.getUsername(); From 715758766819319368752c2b8e94d51032f5cb53 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 9 May 2025 17:09:03 +0100 Subject: [PATCH 019/113] Bump element-web-playwright-common version --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index f09cee86ae..34c333fa00 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.1.6", + "version": "1.1.7", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From b9f200d3492ad5c3ff180d51fc181ddafe25899b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 28 May 2025 16:41:43 +0100 Subject: [PATCH 020/113] Update testcontainers to v11 --- packages/element-web-playwright-common/package.json | 6 +++--- .../element-web-playwright-common/src/fixtures/services.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 6173f3e999..97f6a40b34 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.1.7", + "version": "1.2.0", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", @@ -28,11 +28,11 @@ }, "dependencies": { "@axe-core/playwright": "^4.10.1", - "@testcontainers/postgresql": "^10.24.2", + "@testcontainers/postgresql": "^11.0.0", "lodash-es": "^4.17.21", "mailpit-api": "^1.2.0", "strip-ansi": "^7.1.0", - "testcontainers": "^10.24.2", + "testcontainers": "^11.0.0", "yaml": "^2.7.0" }, "peerDependencies": { diff --git a/packages/element-web-playwright-common/src/fixtures/services.ts b/packages/element-web-playwright-common/src/fixtures/services.ts index 800ecd4c3c..2c91bae1a0 100644 --- a/packages/element-web-playwright-common/src/fixtures/services.ts +++ b/packages/element-web-playwright-common/src/fixtures/services.ts @@ -99,7 +99,7 @@ export const test = base.extend({ ], postgres: [ async ({ logger, network }, use) => { - const container = await new PostgreSqlContainer() + const container = await new PostgreSqlContainer("postgres:13.3-alpine") .withNetwork(network) .withNetworkAliases("postgres") .withLogConsumer(logger.getConsumer("postgres")) From 3befdeb1672abcbb2122247899029acea0da56b1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 4 Jun 2025 14:02:42 +0100 Subject: [PATCH 021/113] Update mas.ts --- .../element-web-playwright-common/src/testcontainers/mas.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts index 935451cc70..aa68c4f1ed 100644 --- a/packages/element-web-playwright-common/src/testcontainers/mas.ts +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -195,8 +195,8 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { private readonly args = ["-c", "/config/config.yaml"]; public constructor(db: StartedPostgreSqlContainer) { - // We rely on https://github.com/element-hq/matrix-authentication-service/pull/4529 which isn't in a release yet - super("ghcr.io/element-hq/matrix-authentication-service:sha-98f2776"); + // We rely on https://github.com/element-hq/matrix-authentication-service/pull/4563 which isn't in a release yet + super("ghcr.io/element-hq/matrix-authentication-service:sha-3207d23"); this.config = deepCopy(DEFAULT_CONFIG); this.config.database.username = db.getUsername(); From 3941f99e1725986d713691758be761a974d77134 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 4 Jun 2025 16:29:41 +0100 Subject: [PATCH 022/113] Bump playwright-common version --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 97f6a40b34..50e38f11cd 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.2.0", + "version": "1.3.0", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 2af22fb54f2caaab3b1225529a3185a4b25b40bc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 09:59:28 +0100 Subject: [PATCH 023/113] Add stale-screenshot-reporter.ts --- .../src/stale-screenshot-reporter.ts | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 packages/element-web-playwright-common/src/stale-screenshot-reporter.ts diff --git a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts new file mode 100644 index 0000000000..7e2ab5b438 --- /dev/null +++ b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts @@ -0,0 +1,88 @@ +/* +Copyright 2024 - 2025 New Vector Ltd. +Copyright 2024 The Matrix.org Foundation C.I.C. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/** + * Test reporter which compares the reported screenshots vs those on disk to find stale screenshots + * Only intended to run from within GitHub Actions + */ + +import { glob } from "glob"; +import { type Reporter, type TestCase } from "@playwright/test/reporter"; +import { type FullConfig } from "@playwright/test"; + +class StaleScreenshotReporter implements Reporter { + private readonly snapshotRoots = new Set(); + private readonly screenshots = new Set(); + private failing = false; + private success = true; + + public onBegin(config: FullConfig): void { + for (const project of config.projects) { + console.log("@@ found snapshotDir", project.snapshotDir); + this.snapshotRoots.add(project.snapshotDir); + } + } + + public onTestEnd(test: TestCase): void { + if (!test.ok()) { + this.failing = true; + } + for (const annotation of test.annotations) { + if (annotation.type === "_screenshot" && annotation.description) { + this.screenshots.add(annotation.description); + } + } + } + + private error(msg: string, file: string) { + if (process.env.GITHUB_ACTIONS) { + console.log(`::error file=${file}::${msg}`); + } + console.error(msg, file); + this.success = false; + } + + public async onExit(): Promise { + if (this.failing) return; + if (!this.snapshotRoots.size) { + this.error("No snapshot directories found, did you set the snapshotDir in your Playwright config?", ""); + return; + } + + const screenshotFiles = new Set(); + for (const snapshotRoot of this.snapshotRoots) { + const files = await glob(`**/*.png`, { cwd: snapshotRoot }); + for (const file of files) { + screenshotFiles.add(file); + } + } + + for (const screenshot of screenshotFiles) { + if (screenshot.split("-").at(-1) !== "linux.png") { + this.error( + "Found screenshot belonging to different platform, this should not be checked in", + screenshot, + ); + } + } + for (const screenshot of this.screenshots) { + screenshotFiles.delete(screenshot); + } + if (screenshotFiles.size > 0) { + for (const screenshot of screenshotFiles) { + this.error("Stale screenshot file", screenshot); + } + } + + if (!this.success) { + process.exit(1); + } + } +} + +export default StaleScreenshotReporter; From 683dfaef6947e2d06d09078b940c5fbdc05f807f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 10:03:32 +0100 Subject: [PATCH 024/113] Add entrypoint --- packages/element-web-playwright-common/package.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 50e38f11cd..d4b0230d63 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -12,7 +12,16 @@ "engines": { "node": ">=20.0.0" }, - "main": "lib/index.js", + "exports": { + ".": { + "import": "./lib/index.js", + "types": "./lib/index.d.ts" + }, + "./stale-screenshots-reporter": { + "import": "./lib/stale-screenshots-reporter.js", + "types": "./lib/stale-screenshots-reporter.d.ts" + } + }, "bin": { "playwright-screenshots": "playwright-screenshots.sh" }, From 02833dac5e5644293194e98bcf79b665b8ad1fac Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 10:06:49 +0100 Subject: [PATCH 025/113] Allow extending SynapseContainer testcontainer --- .../src/testcontainers/synapse.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index 0ccdfb4070..46c5667227 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -188,8 +188,8 @@ export type SynapseConfig = typeof DEFAULT_CONFIG; * Waits for HTTP /health 8008 to 200. */ export class SynapseContainer extends GenericContainer implements HomeserverContainer { - private config: SynapseConfig; - private mas?: StartedMatrixAuthenticationServiceContainer; + protected config: SynapseConfig; + protected mas?: StartedMatrixAuthenticationServiceContainer; public constructor(image = "ghcr.io/element-hq/synapse:develop") { super(image); From a6f851bbdfccb878919e78cc7644381609213aca Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 10:14:18 +0100 Subject: [PATCH 026/113] Iterate --- packages/element-web-playwright-common/package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index d4b0230d63..8cca3237f1 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -15,11 +15,13 @@ "exports": { ".": { "import": "./lib/index.js", + "require": "./lib/index.js", "types": "./lib/index.d.ts" }, - "./stale-screenshots-reporter": { - "import": "./lib/stale-screenshots-reporter.js", - "types": "./lib/stale-screenshots-reporter.d.ts" + "./stale-screenshot-reporter": { + "import": "./lib/stale-screenshot-reporter.js", + "require": "./lib/stale-screenshot-reporter.js", + "types": "./lib/stale-screenshot-reporter.d.ts" } }, "bin": { From 78bc53ef0a499f27de49cf6cd50fe70f3b1e4b50 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 10:21:26 +0100 Subject: [PATCH 027/113] Iterate --- .../element-web-playwright-common/src/expect/screenshot.ts | 3 +-- .../src/stale-screenshot-reporter.ts | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/src/expect/screenshot.ts b/packages/element-web-playwright-common/src/expect/screenshot.ts index 37726fd958..6a4fadf0dc 100644 --- a/packages/element-web-playwright-common/src/expect/screenshot.ts +++ b/packages/element-web-playwright-common/src/expect/screenshot.ts @@ -70,8 +70,7 @@ export const expect = baseExpect.extend({ testInfo.annotations.push({ // `_` prefix hides it from the HTML reporter type: "_screenshot", - // include a path relative to `playwright/snapshots/` - description: testInfo.snapshotPath(screenshotName).split("/playwright/snapshots/", 2)[1], + description: testInfo.snapshotPath(screenshotName), }); return { pass: true, message: (): string => "", name: "toMatchScreenshot" }; diff --git a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts index 7e2ab5b438..3814daa322 100644 --- a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts +++ b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts @@ -12,6 +12,7 @@ Please see LICENSE files in the repository root for full details. */ import { glob } from "glob"; +import path from "node:path"; import { type Reporter, type TestCase } from "@playwright/test/reporter"; import { type FullConfig } from "@playwright/test"; @@ -58,7 +59,7 @@ class StaleScreenshotReporter implements Reporter { for (const snapshotRoot of this.snapshotRoots) { const files = await glob(`**/*.png`, { cwd: snapshotRoot }); for (const file of files) { - screenshotFiles.add(file); + screenshotFiles.add(path.join(snapshotRoot, file)); } } From 75cf1ee738d44e86d8bdcce8b136e6cae62de611 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 10:28:04 +0100 Subject: [PATCH 028/113] Reuse annotation var --- .../src/expect/screenshot.ts | 5 +++-- .../src/stale-screenshot-reporter.ts | 8 +++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/src/expect/screenshot.ts b/packages/element-web-playwright-common/src/expect/screenshot.ts index 6a4fadf0dc..86687b28e4 100644 --- a/packages/element-web-playwright-common/src/expect/screenshot.ts +++ b/packages/element-web-playwright-common/src/expect/screenshot.ts @@ -19,6 +19,8 @@ import { import { sanitizeForFilePath } from "playwright-core/lib/utils"; import { extname } from "node:path"; +import { ANNOTATION } from "../stale-screenshot-reporter.js"; + // Based on https://github.com/microsoft/playwright/blob/2b77ed4d7aafa85a600caa0b0d101b72c8437eeb/packages/playwright/src/util.ts#L206C8-L210C2 function sanitizeFilePathBeforeExtension(filePath: string): string { const ext = extname(filePath); @@ -68,8 +70,7 @@ export const expect = baseExpect.extend({ await style?.evaluate((tag) => tag.remove()); testInfo.annotations.push({ - // `_` prefix hides it from the HTML reporter - type: "_screenshot", + type: ANNOTATION, description: testInfo.snapshotPath(screenshotName), }); diff --git a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts index 3814daa322..f2e0d39de7 100644 --- a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts +++ b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts @@ -16,6 +16,12 @@ import path from "node:path"; import { type Reporter, type TestCase } from "@playwright/test/reporter"; import { type FullConfig } from "@playwright/test"; +/** + * The annotation type used to mark screenshots in tests. + * `_` prefix hides it from the HTML reporter + */ +export const ANNOTATION = "_screenshot"; + class StaleScreenshotReporter implements Reporter { private readonly snapshotRoots = new Set(); private readonly screenshots = new Set(); @@ -34,7 +40,7 @@ class StaleScreenshotReporter implements Reporter { this.failing = true; } for (const annotation of test.annotations) { - if (annotation.type === "_screenshot" && annotation.description) { + if (annotation.type === ANNOTATION && annotation.description) { this.screenshots.add(annotation.description); } } From a440678ffd32aa7fb161ecb88f2decb17f06b3bf Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 10:28:38 +0100 Subject: [PATCH 029/113] Remove console.log --- .../src/stale-screenshot-reporter.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts index f2e0d39de7..a6ac4f5967 100644 --- a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts +++ b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts @@ -30,7 +30,6 @@ class StaleScreenshotReporter implements Reporter { public onBegin(config: FullConfig): void { for (const project of config.projects) { - console.log("@@ found snapshotDir", project.snapshotDir); this.snapshotRoots.add(project.snapshotDir); } } From 3ad03e123bcc129c5d12752f5bfb0e3a9c8f5b8c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 11:18:15 +0100 Subject: [PATCH 030/113] Bump element-web-playwright-common version --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 8cca3237f1..87610dd81e 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.3.0", + "version": "1.4.0", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 62b8d1b3abb4d8878a9c07613186d479bc78e6a4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 11:33:25 +0100 Subject: [PATCH 031/113] Remove exports from playwright-common package --- .../element-web-playwright-common/package.json | 14 ++------------ .../src/testcontainers/HomeserverContainer.ts | 4 ++-- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 87610dd81e..01cd478cb6 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -12,18 +12,8 @@ "engines": { "node": ">=20.0.0" }, - "exports": { - ".": { - "import": "./lib/index.js", - "require": "./lib/index.js", - "types": "./lib/index.d.ts" - }, - "./stale-screenshot-reporter": { - "import": "./lib/stale-screenshot-reporter.js", - "require": "./lib/stale-screenshot-reporter.js", - "types": "./lib/stale-screenshot-reporter.d.ts" - } - }, + "main": "lib/index.js", + "types": "lib/index.d.ts", "bin": { "playwright-screenshots": "playwright-screenshots.sh" }, diff --git a/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts b/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts index ccb242767b..18bc613aeb 100644 --- a/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts +++ b/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { type AbstractStartedContainer, type GenericContainer } from "testcontainers"; +import { type AbstractStartedContainer, type TestContainer } from "testcontainers"; import { type APIRequestContext, type TestInfo } from "@playwright/test"; import { type StartedMatrixAuthenticationServiceContainer } from "./mas"; @@ -41,7 +41,7 @@ export interface HomeserverInstance { setThreepid(userId: string, medium: string, address: string): Promise; } -export interface HomeserverContainer extends GenericContainer { +export interface HomeserverContainer extends TestContainer { /** * Set a configuration field in the config * @param key - the key to set From a44fe85209d965fec14815814a101413de7c88ac Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 11:37:09 +0100 Subject: [PATCH 032/113] Fix types --- .../src/testcontainers/HomeserverContainer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts b/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts index 18bc613aeb..ccb242767b 100644 --- a/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts +++ b/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { type AbstractStartedContainer, type TestContainer } from "testcontainers"; +import { type AbstractStartedContainer, type GenericContainer } from "testcontainers"; import { type APIRequestContext, type TestInfo } from "@playwright/test"; import { type StartedMatrixAuthenticationServiceContainer } from "./mas"; @@ -41,7 +41,7 @@ export interface HomeserverInstance { setThreepid(userId: string, medium: string, address: string): Promise; } -export interface HomeserverContainer extends TestContainer { +export interface HomeserverContainer extends GenericContainer { /** * Set a configuration field in the config * @param key - the key to set From 46912a6a67d9c8d6c9a8d5a09302c7610b1aec48 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 11:41:24 +0100 Subject: [PATCH 033/113] Bump version playwright-common --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 01cd478cb6..a69c60788f 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.4.0", + "version": "1.4.1", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 03c2f2cd5fbf7ea20d9bf5e65e4d6c78c3b81b4f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 20 Jun 2025 12:09:34 +0100 Subject: [PATCH 034/113] Add missing glob dependency for playwright-common --- packages/element-web-playwright-common/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index a69c60788f..20a3ecbbf6 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.4.1", + "version": "1.4.2", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", @@ -30,6 +30,7 @@ "dependencies": { "@axe-core/playwright": "^4.10.1", "@testcontainers/postgresql": "^11.0.0", + "glob": "^11.0.3", "lodash-es": "^4.17.21", "mailpit-api": "^1.2.0", "strip-ansi": "^7.1.0", From 4df86c4d3fc3027bd28e09ad918fc7ea5213c93d Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 18 Jul 2025 15:18:37 +0100 Subject: [PATCH 035/113] Add options to use playwright screenshot script for other things In this case, storybook screenshots, but this keeps it so this script doesnt know about storybook itself (we can move the actual storybook specific code to here if we standardise on it more). --- .../playwright-screenshots.sh | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index a212fc9a94..b75bdc5325 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -1,25 +1,29 @@ #!/bin/bash +set -x # Handle symlinks here as we tend to be executed as an npm binary SCRIPT_PATH=$(readlink -f "$0") SCRIPT_DIR=$(dirname "$SCRIPT_PATH") IMAGE_NAME="element-web-playwright-common" -echo "Building $IMAGE_NAME image in $SCRIPT_DIR" -# Build image -PW_VERSION=$( - yarn list \ - --pattern @playwright/test \ - --depth=0 \ - --json \ - --non-interactive \ - --no-progress | \ - jq -r '.data.trees[].name | split("@")[2]' \ - ) -echo "with Playwright version $PW_VERSION" +build_image() { + echo "Building $IMAGE_NAME image in $SCRIPT_DIR" -docker build -t "$IMAGE_NAME" --build-arg "PLAYWRIGHT_VERSION=$PW_VERSION" "$SCRIPT_DIR" + # Build image + PW_VERSION=$( + yarn list \ + --pattern @playwright/test \ + --depth=0 \ + --json \ + --non-interactive \ + --no-progress | \ + jq -r '.data.trees[].name | split("@")[2]' \ + ) + echo "with Playwright version $PW_VERSION" + + docker build -t "$IMAGE_NAME" --build-arg "PLAYWRIGHT_VERSION=$PW_VERSION" "$SCRIPT_DIR" +} RUN_ARGS=( --rm @@ -36,6 +40,35 @@ RUN_ARGS=( -it ) +DEFAULT_ARGS=(--grep @screenshot) + +# Some arguments to customise behaviour so the same script / image can be +# re-used for other screenshot generation. +while [[ $# -gt 0 ]]; do + case "$1" in + # Mounts a separate node_modules directory from a docker volume in the container. + # Must be used if executing something that requires native node modules + # It's a volume rather than a directory because otherwise things tend to start picking up + # files from it in the native environment and break. + --with-node-modules) + RUN_ARGS+=(--mount "type=volume,src=ew-docker-node-modules,dst=/work/node_modules,volume-nocopy") + shift + ;; + # Sets a different entrypoint (in which case the default arguments to the script will be ignored) + --entrypoint) + shift + RUN_ARGS+=(--entrypoint "$1") + DEFAULT_ARGS=() + shift + ;; + *) + break + ;; + esac +done + +build_image + # Ensure we pass all symlinked node_modules to the container pushd node_modules || exit > /dev/null SYMLINKS=$(find . -maxdepth 2 -type l -not -path "./.bin/*") @@ -48,6 +81,4 @@ for LINK in $SYMLINKS; do fi done -DEFAULT_ARGS=(--grep @screenshot) - -docker run "${RUN_ARGS[@]}" "$IMAGE_NAME" "${DEFAULT_ARGS[@]}" "$@" \ No newline at end of file +docker run "${RUN_ARGS[@]}" "$IMAGE_NAME" "${DEFAULT_ARGS[@]}" "$@" From d1d69fbc56c851be0bd300d94f5797e552039bda Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 18 Jul 2025 16:54:01 +0100 Subject: [PATCH 036/113] Probably better not to keep this given how spammy it is --- packages/element-web-playwright-common/playwright-screenshots.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index b75bdc5325..a676491b28 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -1,5 +1,4 @@ #!/bin/bash -set -x # Handle symlinks here as we tend to be executed as an npm binary SCRIPT_PATH=$(readlink -f "$0") From 8fba68db786814ea03a9324cb2b8ec1e589fe7cf Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 24 Jul 2025 11:07:17 +0100 Subject: [PATCH 037/113] 1.4.3 --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 20a3ecbbf6..2b9f782360 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.4.2", + "version": "1.4.3", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 41887c61a9febf76e98e2e92f659871583049a10 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 24 Jul 2025 16:27:09 +0100 Subject: [PATCH 038/113] Iterate --- .../element-web-playwright-common/src/testcontainers/synapse.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index 46c5667227..069bfdf2a3 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -174,6 +174,7 @@ const DEFAULT_CONFIG = { include_offline_users_on_sync: true, }, room_list_publication_rules: [{ action: "allow" }], + modules: [] as Array<{ module: string; config?: Record }>, }; /** From 7a24eecde8c92fe2d386cca88b02af837dda66a2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 31 Jul 2025 10:10:24 +0100 Subject: [PATCH 039/113] Add rc_room_creation to Synapse config --- packages/element-web-playwright-common/package.json | 2 +- .../src/testcontainers/synapse.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 2b9f782360..f69e65903e 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.4.3", + "version": "1.4.4", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index 069bfdf2a3..84ac60957b 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -104,6 +104,10 @@ const DEFAULT_CONFIG = { burst_count: 10000, }, }, + rc_room_creation: { + per_second: 1000, + burst_count: 1000, + }, media_store_path: "/tmp/media_store", max_upload_size: "50M", max_image_pixels: "32M", From dd402c834cbcef43c94b92261fd7f7c0f00b3b93 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 31 Jul 2025 10:15:14 +0100 Subject: [PATCH 040/113] Add missing internal dev dependency --- packages/element-web-playwright-common/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index f69e65903e..556708d027 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -24,6 +24,7 @@ "test": "echo No tests for @element-hq/element-web-playwright-common" }, "devDependencies": { + "@element-hq/element-web-module-api": "*", "@types/lodash-es": "^4.17.12", "typescript": "^5.8.2" }, From c7f5d91504443dbbb8ed4f8191b2fe4cfea01c22 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 5 Aug 2025 09:34:16 +0200 Subject: [PATCH 041/113] Simplify the default MAS configuration --- .../src/testcontainers/mas.ts | 55 +------------------ 1 file changed, 2 insertions(+), 53 deletions(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts index aa68c4f1ed..7d248cbd9c 100644 --- a/packages/element-web-playwright-common/src/testcontainers/mas.ts +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -29,14 +29,8 @@ const DEFAULT_CONFIG = { { name: "human" }, { name: "oauth" }, { name: "compat" }, - { - name: "graphql", - playground: true, - }, - { - name: "assets", - path: "/usr/local/share/mas-cli/assets/", - }, + { name: "graphql" }, + { name: "assets" }, ], binds: [ { @@ -60,7 +54,6 @@ const DEFAULT_CONFIG = { proxy_protocol: false, }, ], - trusted_proxies: ["192.128.0.0/16", "172.16.0.0/12", "10.0.0.0/10", "127.0.0.1/8", "fd00::/8", "::1/128"], public_base: "", // Needs to be set issuer: "", // Needs to be set }, @@ -70,28 +63,6 @@ const DEFAULT_CONFIG = { database: "postgres", username: "postgres", password: "p4S5w0rD", - max_connections: 10, - min_connections: 0, - connect_timeout: 30, - idle_timeout: 600, - max_lifetime: 1800, - }, - telemetry: { - tracing: { - exporter: "none", - propagators: [], - }, - metrics: { - exporter: "none", - }, - sentry: { - dsn: null, - }, - }, - templates: { - path: "/usr/local/share/mas-cli/templates/", - assets_manifest: "/usr/local/share/mas-cli/manifest.json", - translations_path: "/usr/local/share/mas-cli/translations/", }, email: { from: '"Authentication Service" ', @@ -135,38 +106,16 @@ const DEFAULT_CONFIG = { minimum_complexity: 0, }, policy: { - wasm_module: "/usr/local/share/mas-cli/policy.wasm", - client_registration_entrypoint: "client_registration/violation", - register_entrypoint: "register/violation", - authorization_grant_entrypoint: "authorization_grant/violation", - password_entrypoint: "password/violation", - email_entrypoint: "email/violation", data: { client_registration: { // allow non-SSL and localhost URIs allow_insecure_uris: true, - // EW doesn't have contacts at this time - allow_missing_contacts: true, }, }, }, - upstream_oauth2: { - providers: [], - }, - branding: { - service_name: null, - policy_uri: null, - tos_uri: null, - imprint: null, - logo_uri: null, - }, account: { password_registration_enabled: true, }, - experimental: { - access_token_ttl: 300, - compat_token_ttl: 300, - }, rate_limiting: { login: { burst: 10, From 90a51d4487653b333cf66ff5daca6b23dbb63d7a Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 5 Aug 2025 09:35:13 +0200 Subject: [PATCH 042/113] Allow overriding the MAS docker image --- .../src/testcontainers/mas.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts index 7d248cbd9c..c177376ee4 100644 --- a/packages/element-web-playwright-common/src/testcontainers/mas.ts +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -143,9 +143,11 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { private config: MasConfig; private readonly args = ["-c", "/config/config.yaml"]; - public constructor(db: StartedPostgreSqlContainer) { - // We rely on https://github.com/element-hq/matrix-authentication-service/pull/4563 which isn't in a release yet - super("ghcr.io/element-hq/matrix-authentication-service:sha-3207d23"); + public constructor( + db: StartedPostgreSqlContainer, + image: string = "ghcr.io/element-hq/matrix-authentication-service:latest", + ) { + super(image); this.config = deepCopy(DEFAULT_CONFIG); this.config.database.username = db.getUsername(); From b4a59ce009587b9b98a0c7c37c5de0ad50cb1f4a Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 5 Aug 2025 09:45:34 +0200 Subject: [PATCH 043/113] Re-export the Postgres testcontainer --- .../element-web-playwright-common/src/testcontainers/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/element-web-playwright-common/src/testcontainers/index.ts b/packages/element-web-playwright-common/src/testcontainers/index.ts index 21cb12027e..bb34e4a832 100644 --- a/packages/element-web-playwright-common/src/testcontainers/index.ts +++ b/packages/element-web-playwright-common/src/testcontainers/index.ts @@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ +export { PostgreSqlContainer, StartedPostgreSqlContainer } from "@testcontainers/postgresql"; export type { HomeserverInstance, HomeserverContainer, StartedHomeserverContainer } from "./HomeserverContainer.js"; export { type SynapseConfig, SynapseContainer, StartedSynapseContainer } from "./synapse.js"; export { From 5360f309f15dafcb8bb4d3e1fb2606f43f1dcf1b Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 5 Aug 2025 10:53:24 +0200 Subject: [PATCH 044/113] Generate accurate MAS config type from its JSON schema --- .../src/testcontainers/mas-config.ts | 1383 +++++++++++++++++ .../src/testcontainers/mas.ts | 34 +- 2 files changed, 1405 insertions(+), 12 deletions(-) create mode 100644 packages/element-web-playwright-common/src/testcontainers/mas-config.ts diff --git a/packages/element-web-playwright-common/src/testcontainers/mas-config.ts b/packages/element-web-playwright-common/src/testcontainers/mas-config.ts new file mode 100644 index 0000000000..1f3a911ecd --- /dev/null +++ b/packages/element-web-playwright-common/src/testcontainers/mas-config.ts @@ -0,0 +1,1383 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +/** + * Authentication method used by clients + */ +export type ClientAuthMethodConfig = + | "none" + | "client_secret_basic" + | "client_secret_post" + | "client_secret_jwt" + | "private_key_jwt"; +export type JsonWebKeyFor_JsonWebKeyPublicParameters = { + "use"?: JsonWebKeyUse; + "key_ops"?: JsonWebKeyOperation[]; + "alg"?: JsonWebSignatureAlg; + "kid"?: string; + "x5u"?: string; + "x5c"?: string[]; + "x5t"?: string; + "x5t#S256"?: string; + [k: string]: unknown; +} & JsonWebKeyFor_JsonWebKeyPublicParameters1; +/** + * JSON Web Key Use + */ +export type JsonWebKeyUse = "sig" | "enc"; +/** + * JSON Web Key Operation + */ +export type JsonWebKeyOperation = + | "sign" + | "verify" + | "encrypt" + | "decrypt" + | "wrapKey" + | "unwrapKey" + | "deriveKey" + | "deriveBits"; +/** + * JSON Web Signature "alg" parameter + */ +export type JsonWebSignatureAlg = + | "HS256" + | "HS384" + | "HS512" + | "RS256" + | "RS384" + | "RS512" + | "ES256" + | "ES384" + | "ES512" + | "PS256" + | "PS384" + | "PS512" + | "none" + | "EdDSA" + | "ES256K" + | "Ed25519" + | "Ed448"; +export type JsonWebKeyFor_JsonWebKeyPublicParameters1 = + | { + kty: "RSA"; + n: string; + e: string; + [k: string]: unknown; + } + | { + kty: "EC"; + crv: JsonWebKeyEcEllipticCurve; + x: string; + y: string; + [k: string]: unknown; + } + | { + kty: "OKP"; + crv: JsonWebKeyOkpEllipticCurve; + x: string; + [k: string]: unknown; + }; +/** + * JSON Web Key EC Elliptic Curve + */ +export type JsonWebKeyEcEllipticCurve = "P-256" | "P-384" | "P-521" | "secp256k1"; +/** + * JSON Web Key OKP Elliptic Curve + */ +export type JsonWebKeyOkpEllipticCurve = "Ed25519" | "Ed448" | "X25519" | "X448"; +/** + * HTTP resources to mount + */ +export type Resource = + | { + name: "health"; + [k: string]: unknown; + } + | { + name: "prometheus"; + [k: string]: unknown; + } + | { + name: "discovery"; + [k: string]: unknown; + } + | { + name: "human"; + [k: string]: unknown; + } + | { + name: "graphql"; + /** + * Enabled the GraphQL playground + */ + playground?: boolean; + /** + * Allow access for OAuth 2.0 clients (undocumented) + */ + undocumented_oauth2_access?: boolean; + [k: string]: unknown; + } + | { + name: "oauth"; + [k: string]: unknown; + } + | { + name: "compat"; + [k: string]: unknown; + } + | { + name: "assets"; + /** + * Path to the directory to serve. + */ + path?: string; + [k: string]: unknown; + } + | { + name: "adminapi"; + [k: string]: unknown; + } + | { + name: "connection-info"; + [k: string]: unknown; + }; +/** + * Configuration of a single listener + */ +export type BindConfig = + | { + /** + * Host on which to listen. + * + * Defaults to listening on all addresses + */ + host?: string; + /** + * Port on which to listen. + */ + port: number; + [k: string]: unknown; + } + | { + /** + * Host and port on which to listen + */ + address: string; + [k: string]: unknown; + } + | { + /** + * Path to the socket + */ + socket: string; + [k: string]: unknown; + } + | { + /** + * Index of the file descriptor. Note that this is offseted by 3 because of the standard input/output sockets, so setting here a value of `0` will grab the file descriptor `3` + */ + fd?: number; + /** + * Whether the socket is a TCP socket or a UNIX domain socket. Defaults to TCP. + */ + kind?: UnixOrTcp & string; + [k: string]: unknown; + }; +/** + * Kind of socket + */ +export type UnixOrTcp = "unix" | "tcp"; +export type IpNetwork = V4 | V6; +export type V4 = Ipv4Network; +export type Ipv4Network = string; +export type V6 = Ipv6Network; +export type Ipv6Network = string; +export type Hostname = string; +/** + * Options for controlling the level of protection provided for PostgreSQL SSL connections. + */ +export type PgSslMode = "disable" | "allow" | "prefer" | "require" | "verify-ca" | "verify-full"; +/** + * Exporter to use when exporting traces + */ +export type TracingExporterKind = "none" | "stdout" | "otlp"; +/** + * Propagation format for incoming and outgoing requests + */ +export type Propagator = "tracecontext" | "baggage" | "jaeger"; +/** + * Exporter to use when exporting metrics + */ +export type MetricsExporterKind = "none" | "stdout" | "otlp" | "prometheus"; +/** + * What backend should be used when sending emails + */ +export type EmailTransportKind = "blackhole" | "smtp" | "sendmail"; +/** + * Encryption mode to use + */ +export type EmailSmtpMode = "plain" | "starttls" | "tls"; +/** + * A hashing algorithm + */ +export type Algorithm = "bcrypt" | "argon2id" | "pbkdf2"; +/** + * The kind of homeserver it is. + */ +export type HomeserverKind = "synapse" | "synapse_read_only" | "synapse_legacy" | "synapse_modern"; +/** + * Authentication methods used against the OAuth 2.0 provider + */ +export type TokenAuthMethod = + | "none" + | "client_secret_basic" + | "client_secret_post" + | "client_secret_jwt" + | "private_key_jwt" + | "sign_in_with_apple"; +/** + * How to discover the provider's configuration + */ +export type DiscoveryMode = "oidc" | "insecure" | "disabled"; +/** + * Whether to use proof key for code exchange (PKCE) when requesting and exchanging the token. + */ +export type PkceMethod = "auto" | "always" | "never"; +/** + * The response mode we ask the provider to use for the callback + */ +export type ResponseMode = "query" | "form_post"; +/** + * How to handle a claim + */ +export type ImportAction = "ignore" | "suggest" | "force" | "require"; +/** + * How to handle an existing localpart claim + */ +export type OnConflict = "fail" | "add"; +/** + * What to do when receiving an OIDC Backchannel logout request. + */ +export type OnBackchannelLogout = "do_nothing" | "logout_browser_only" | "logout_all"; +/** + * Which service should be used for CAPTCHA protection + */ +export type CaptchaServiceKind = "recaptcha_v2" | "cloudflare_turnstile" | "hcaptcha"; + +/** + * Application configuration root + */ +export interface RootConfig { + /** + * List of OAuth 2.0/OIDC clients config + */ + clients?: ClientConfig[]; + /** + * Configuration of the HTTP server + */ + http?: HttpConfig; + /** + * Database connection configuration + */ + database?: DatabaseConfig; + /** + * Configuration related to sending monitoring data + */ + telemetry?: TelemetryConfig; + /** + * Configuration related to templates + */ + templates?: TemplatesConfig; + /** + * Configuration related to sending emails + */ + email?: EmailConfig; + /** + * Application secrets + */ + secrets: SecretsConfig; + /** + * Configuration related to user passwords + */ + passwords?: PasswordsConfig; + /** + * Configuration related to the homeserver + */ + matrix: MatrixConfig; + /** + * Configuration related to the OPA policies + */ + policy?: PolicyConfig; + /** + * Configuration related to limiting the rate of user actions to prevent abuse + */ + rate_limiting?: RateLimitingConfig; + /** + * Configuration related to upstream OAuth providers + */ + upstream_oauth2?: UpstreamOAuth2Config; + /** + * Configuration section for tweaking the branding of the service + */ + branding?: BrandingConfig; + /** + * Configuration section to setup CAPTCHA protection on a few operations + */ + captcha?: CaptchaConfig; + /** + * Configuration section to configure features related to account management + */ + account?: AccountConfig; + /** + * Experimental configuration options + */ + experimental?: ExperimentalConfig; + [k: string]: unknown; +} +/** + * An OAuth 2.0 client configuration + */ +export interface ClientConfig { + /** + * A ULID as per https://github.com/ulid/spec + */ + client_id: string; + /** + * Authentication method used for this client + */ + client_auth_method: ClientAuthMethodConfig; + /** + * Name of the `OAuth2` client + */ + client_name?: string; + /** + * The client secret, used by the `client_secret_basic`, `client_secret_post` and `client_secret_jwt` authentication methods + */ + client_secret?: string; + /** + * The JSON Web Key Set (JWKS) used by the `private_key_jwt` authentication method. Mutually exclusive with `jwks_uri` + */ + jwks?: JsonWebKeySetFor_JsonWebKeyPublicParameters; + /** + * The URL of the JSON Web Key Set (JWKS) used by the `private_key_jwt` authentication method. Mutually exclusive with `jwks` + */ + jwks_uri?: string; + /** + * List of allowed redirect URIs + */ + redirect_uris?: string[]; + [k: string]: unknown; +} +export interface JsonWebKeySetFor_JsonWebKeyPublicParameters { + keys: JsonWebKeyFor_JsonWebKeyPublicParameters[]; + [k: string]: unknown; +} +/** + * Configuration related to the web server + */ +export interface HttpConfig { + /** + * List of listeners to run + */ + listeners?: ListenerConfig[]; + /** + * List of trusted reverse proxies that can set the `X-Forwarded-For` header + */ + trusted_proxies?: IpNetwork[]; + /** + * Public URL base from where the authentication service is reachable + */ + public_base: string; + /** + * OIDC issuer URL. Defaults to `public_base` if not set. + */ + issuer?: string; + [k: string]: unknown; +} +/** + * Configuration of a listener + */ +export interface ListenerConfig { + /** + * A unique name for this listener which will be shown in traces and in metrics labels + */ + name?: string; + /** + * List of resources to mount + */ + resources: Resource[]; + /** + * HTTP prefix to mount the resources on + */ + prefix?: string; + /** + * List of sockets to bind + */ + binds: BindConfig[]; + /** + * Accept `HAProxy`'s Proxy Protocol V1 + */ + proxy_protocol?: boolean; + /** + * If set, makes the listener use TLS with the provided certificate and key + */ + tls?: TlsConfig; + [k: string]: unknown; +} +/** + * Configuration related to TLS on a listener + */ +export interface TlsConfig { + /** + * PEM-encoded X509 certificate chain + * + * Exactly one of `certificate` or `certificate_file` must be set. + */ + certificate?: string; + /** + * File containing the PEM-encoded X509 certificate chain + * + * Exactly one of `certificate` or `certificate_file` must be set. + */ + certificate_file?: string; + /** + * PEM-encoded private key + * + * Exactly one of `key` or `key_file` must be set. + */ + key?: string; + /** + * File containing a PEM or DER-encoded private key + * + * Exactly one of `key` or `key_file` must be set. + */ + key_file?: string; + /** + * Password used to decode the private key + * + * One of `password` or `password_file` must be set if the key is encrypted. + */ + password?: string; + /** + * Password file used to decode the private key + * + * One of `password` or `password_file` must be set if the key is encrypted. + */ + password_file?: string; + [k: string]: unknown; +} +/** + * Database connection configuration + */ +export interface DatabaseConfig { + /** + * Connection URI + * + * This must not be specified if `host`, `port`, `socket`, `username`, `password`, or `database` are specified. + */ + uri?: string; + /** + * Name of host to connect to + * + * This must not be specified if `uri` is specified. + */ + host?: Hostname; + /** + * Port number to connect at the server host + * + * This must not be specified if `uri` is specified. + */ + port?: number; + /** + * Directory containing the UNIX socket to connect to + * + * This must not be specified if `uri` is specified. + */ + socket?: string; + /** + * PostgreSQL user name to connect as + * + * This must not be specified if `uri` is specified. + */ + username?: string; + /** + * Password to be used if the server demands password authentication + * + * This must not be specified if `uri` is specified. + */ + password?: string; + /** + * The database name + * + * This must not be specified if `uri` is specified. + */ + database?: string; + /** + * How to handle SSL connections + */ + ssl_mode?: PgSslMode; + /** + * The PEM-encoded root certificate for SSL connections + * + * This must not be specified if the `ssl_ca_file` option is specified. + */ + ssl_ca?: string; + /** + * Path to the root certificate for SSL connections + * + * This must not be specified if the `ssl_ca` option is specified. + */ + ssl_ca_file?: string; + /** + * The PEM-encoded client certificate for SSL connections + * + * This must not be specified if the `ssl_certificate_file` option is specified. + */ + ssl_certificate?: string; + /** + * Path to the client certificate for SSL connections + * + * This must not be specified if the `ssl_certificate` option is specified. + */ + ssl_certificate_file?: string; + /** + * The PEM-encoded client key for SSL connections + * + * This must not be specified if the `ssl_key_file` option is specified. + */ + ssl_key?: string; + /** + * Path to the client key for SSL connections + * + * This must not be specified if the `ssl_key` option is specified. + */ + ssl_key_file?: string; + /** + * Set the maximum number of connections the pool should maintain + */ + max_connections?: number; + /** + * Set the minimum number of connections the pool should maintain + */ + min_connections?: number; + /** + * Set the amount of time to attempt connecting to the database + */ + connect_timeout?: number; + /** + * Set a maximum idle duration for individual connections + */ + idle_timeout?: number; + /** + * Set the maximum lifetime of individual connections + */ + max_lifetime?: number; + [k: string]: unknown; +} +/** + * Configuration related to sending monitoring data + */ +export interface TelemetryConfig { + /** + * Configuration related to exporting traces + */ + tracing?: TracingConfig; + /** + * Configuration related to exporting metrics + */ + metrics?: MetricsConfig; + /** + * Configuration related to the Sentry integration + */ + sentry?: SentryConfig; + [k: string]: unknown; +} +/** + * Configuration related to exporting traces + */ +export interface TracingConfig { + /** + * Exporter to use when exporting traces + */ + exporter?: TracingExporterKind & string; + /** + * OTLP exporter: OTLP over HTTP compatible endpoint + */ + endpoint?: string; + /** + * List of propagation formats to use for incoming and outgoing requests + */ + propagators?: Propagator[]; + /** + * Sample rate for traces + * + * Defaults to `1.0` if not set. + */ + sample_rate?: number; + [k: string]: unknown; +} +/** + * Configuration related to exporting metrics + */ +export interface MetricsConfig { + /** + * Exporter to use when exporting metrics + */ + exporter?: MetricsExporterKind & string; + /** + * OTLP exporter: OTLP over HTTP compatible endpoint + */ + endpoint?: string; + [k: string]: unknown; +} +/** + * Configuration related to the Sentry integration + */ +export interface SentryConfig { + /** + * Sentry DSN + */ + dsn?: string; + /** + * Environment to use when sending events to Sentry + * + * Defaults to `production` if not set. + */ + environment?: string; + /** + * Sample rate for event submissions + * + * Defaults to `1.0` if not set. + */ + sample_rate?: number; + /** + * Sample rate for tracing transactions + * + * Defaults to `0.0` if not set. + */ + traces_sample_rate?: number; + [k: string]: unknown; +} +/** + * Configuration related to templates + */ +export interface TemplatesConfig { + /** + * Path to the folder which holds the templates + */ + path?: string; + /** + * Path to the assets manifest + */ + assets_manifest?: string; + /** + * Path to the translations + */ + translations_path?: string; + [k: string]: unknown; +} +/** + * Configuration related to sending emails + */ +export interface EmailConfig { + /** + * Email address to use as From when sending emails + */ + from?: string; + /** + * Email address to use as Reply-To when sending emails + */ + reply_to?: string; + /** + * What backend should be used when sending emails + */ + transport: EmailTransportKind; + /** + * SMTP transport: Connection mode to the relay + */ + mode?: EmailSmtpMode; + /** + * SMTP transport: Hostname to connect to + */ + hostname?: Hostname; + /** + * SMTP transport: Port to connect to. Default is 25 for plain, 465 for TLS and 587 for `StartTLS` + */ + port?: number; + /** + * SMTP transport: Username for use to authenticate when connecting to the SMTP server + * + * Must be set if the `password` field is set + */ + username?: string; + /** + * SMTP transport: Password for use to authenticate when connecting to the SMTP server + * + * Must be set if the `username` field is set + */ + password?: string; + /** + * Sendmail transport: Command to use to send emails + */ + command?: string; + [k: string]: unknown; +} +/** + * Application secrets + */ +export interface SecretsConfig { + /** + * List of private keys to use for signing and encrypting payloads + */ + keys?: KeyConfig[]; + /** + * File containing the encryption key for secure cookies. + */ + encryption_file?: string; + /** + * Encryption key for secure cookies. + */ + encryption?: string; + [k: string]: unknown; +} +/** + * A single key with its key ID and optional password. + */ +export interface KeyConfig { + kid: string; + password_file?: string; + password?: string; + key_file?: string; + key?: string; + [k: string]: unknown; +} +/** + * User password hashing config + */ +export interface PasswordsConfig { + /** + * Whether password-based authentication is enabled + */ + enabled?: boolean; + /** + * The hashing schemes to use for hashing and validating passwords + * + * The hashing scheme with the highest version number will be used for hashing new passwords. + */ + schemes?: HashingScheme[]; + /** + * Score between 0 and 4 determining the minimum allowed password complexity. Scores are based on the ESTIMATED number of guesses needed to guess the password. + * + * - 0: less than 10^2 (100) - 1: less than 10^4 (10'000) - 2: less than 10^6 (1'000'000) - 3: less than 10^8 (100'000'000) - 4: any more than that + */ + minimum_complexity?: number; + [k: string]: unknown; +} +/** + * Parameters for a password hashing scheme + */ +export interface HashingScheme { + /** + * The version of the hashing scheme. They must be unique, and the highest version will be used for hashing new passwords. + */ + version: number; + /** + * The hashing algorithm to use + */ + algorithm: Algorithm; + /** + * Whether to apply Unicode normalization to the password before hashing + * + * Defaults to `false`, and generally recommended to stay false. This is although recommended when importing password hashs from Synapse, as it applies an NFKC normalization to the password before hashing it. + */ + unicode_normalization?: boolean; + /** + * Cost for the bcrypt algorithm + */ + cost?: number; + /** + * An optional secret to use when hashing passwords. This makes it harder to brute-force the passwords in case of a database leak. + */ + secret?: string; + /** + * Same as `secret`, but read from a file. + */ + secret_file?: string; + [k: string]: unknown; +} +/** + * Configuration related to the Matrix homeserver + */ +export interface MatrixConfig { + /** + * The kind of homeserver it is. + */ + kind?: HomeserverKind & string; + /** + * The server name of the homeserver. + */ + homeserver?: string; + /** + * Shared secret to use for calls to the admin API + */ + secret: string; + /** + * The base URL of the homeserver's client API + */ + endpoint?: string; + [k: string]: unknown; +} +/** + * Application secrets + */ +export interface PolicyConfig { + /** + * Path to the WASM module + */ + wasm_module?: string; + /** + * Entrypoint to use when evaluating client registrations + */ + client_registration_entrypoint?: string; + /** + * Entrypoint to use when evaluating user registrations + */ + register_entrypoint?: string; + /** + * Entrypoint to use when evaluating authorization grants + */ + authorization_grant_entrypoint?: string; + /** + * Entrypoint to use when changing password + */ + password_entrypoint?: string; + /** + * Entrypoint to use when adding an email address + */ + email_entrypoint?: string; + /** + * Arbitrary data to pass to the policy + */ + data?: { + [k: string]: unknown; + }; + [k: string]: unknown; +} +/** + * Configuration related to sending emails + */ +export interface RateLimitingConfig { + /** + * Account Recovery-specific rate limits + */ + account_recovery?: AccountRecoveryRateLimitingConfig; + /** + * Login-specific rate limits + */ + login?: LoginRateLimitingConfig; + /** + * Controls how many registrations attempts are permitted based on source address. + */ + registration?: RateLimiterConfiguration; + /** + * Email authentication-specific rate limits + */ + email_authentication?: EmailauthenticationRateLimitingConfig; + [k: string]: unknown; +} +export interface AccountRecoveryRateLimitingConfig { + /** + * Controls how many account recovery attempts are permitted based on source IP address. This can protect against causing e-mail spam to many targets. + * + * Note: this limit also applies to re-sends. + */ + per_ip?: RateLimiterConfiguration; + /** + * Controls how many account recovery attempts are permitted based on the e-mail address entered into the recovery form. This can protect against causing e-mail spam to one target. + * + * Note: this limit also applies to re-sends. + */ + per_address?: RateLimiterConfiguration; + [k: string]: unknown; +} +export interface RateLimiterConfiguration { + /** + * A one-off burst of actions that the user can perform in one go without waiting. + */ + burst: number; + /** + * How quickly the allowance replenishes, in number of actions per second. Can be fractional to replenish slower. + */ + per_second: number; + [k: string]: unknown; +} +export interface LoginRateLimitingConfig { + /** + * Controls how many login attempts are permitted based on source IP address. This can protect against brute force login attempts. + * + * Note: this limit also applies to password checks when a user attempts to change their own password. + */ + per_ip?: RateLimiterConfiguration; + /** + * Controls how many login attempts are permitted based on the account that is being attempted to be logged into. This can protect against a distributed brute force attack but should be set high enough to prevent someone's account being casually locked out. + * + * Note: this limit also applies to password checks when a user attempts to change their own password. + */ + per_account?: RateLimiterConfiguration; + [k: string]: unknown; +} +export interface EmailauthenticationRateLimitingConfig { + /** + * Controls how many email authentication attempts are permitted based on the source IP address. This can protect against causing e-mail spam to many targets. + */ + per_ip?: RateLimiterConfiguration; + /** + * Controls how many email authentication attempts are permitted based on the e-mail address entered into the authentication form. This can protect against causing e-mail spam to one target. + * + * Note: this limit also applies to re-sends. + */ + per_address?: RateLimiterConfiguration; + /** + * Controls how many authentication emails are permitted to be sent per authentication session. This ensures not too many authentication codes are created for the same authentication session. + */ + emails_per_session?: RateLimiterConfiguration; + /** + * Controls how many code authentication attempts are permitted per authentication session. This can protect against brute-forcing the code. + */ + attempt_per_session?: RateLimiterConfiguration; + [k: string]: unknown; +} +/** + * Upstream OAuth 2.0 providers configuration + */ +export interface UpstreamOAuth2Config { + /** + * List of OAuth 2.0 providers + */ + providers: Provider[]; + [k: string]: unknown; +} +/** + * Configuration for one upstream OAuth 2 provider. + */ +export interface Provider { + /** + * Whether this provider is enabled. + * + * Defaults to `true` + */ + enabled?: boolean; + /** + * A ULID as per https://github.com/ulid/spec + */ + id: string; + /** + * The ID of the provider that was used by Synapse. In order to perform a Synapse-to-MAS migration, this must be specified. + * + * ## For providers that used OAuth 2.0 or OpenID Connect in Synapse + * + * ### For `oidc_providers`: This should be specified as `oidc-` followed by the ID that was configured as `idp_id` in one of the `oidc_providers` in the Synapse configuration. For example, if Synapse's configuration contained `idp_id: wombat` for this provider, then specify `oidc-wombat` here. + * + * ### For `oidc_config` (legacy): Specify `oidc` here. + */ + synapse_idp_id?: string; + /** + * The OIDC issuer URL + * + * This is required if OIDC discovery is enabled (which is the default) + */ + issuer?: string; + /** + * A human-readable name for the provider, that will be shown to users + */ + human_name?: string; + /** + * A brand identifier used to customise the UI, e.g. `apple`, `google`, `github`, etc. + * + * Values supported by the default template are: + * + * - `apple` - `google` - `facebook` - `github` - `gitlab` - `twitter` - `discord` + */ + brand_name?: string; + /** + * The client ID to use when authenticating with the provider + */ + client_id: string; + /** + * The client secret to use when authenticating with the provider + * + * Used by the `client_secret_basic`, `client_secret_post`, and `client_secret_jwt` methods + */ + client_secret?: string; + /** + * The method to authenticate the client with the provider + */ + token_endpoint_auth_method: TokenAuthMethod; + /** + * Additional parameters for the `sign_in_with_apple` method + */ + sign_in_with_apple?: SignInWithApple; + /** + * The JWS algorithm to use when authenticating the client with the provider + * + * Used by the `client_secret_jwt` and `private_key_jwt` methods + */ + token_endpoint_auth_signing_alg?: JsonWebSignatureAlg; + /** + * Expected signature for the JWT payload returned by the token authentication endpoint. + * + * Defaults to `RS256`. + */ + id_token_signed_response_alg?: JsonWebSignatureAlg; + /** + * The scopes to request from the provider + * + * Defaults to `openid`. + */ + scope?: string; + /** + * How to discover the provider's configuration + * + * Defaults to `oidc`, which uses OIDC discovery with strict metadata verification + */ + discovery_mode?: DiscoveryMode; + /** + * Whether to use proof key for code exchange (PKCE) when requesting and exchanging the token. + * + * Defaults to `auto`, which uses PKCE if the provider supports it. + */ + pkce_method?: PkceMethod; + /** + * Whether to fetch the user profile from the userinfo endpoint, or to rely on the data returned in the `id_token` from the `token_endpoint`. + * + * Defaults to `false`. + */ + fetch_userinfo?: boolean; + /** + * Expected signature for the JWT payload returned by the userinfo endpoint. + * + * If not specified, the response is expected to be an unsigned JSON payload. + */ + userinfo_signed_response_alg?: JsonWebSignatureAlg; + /** + * The URL to use for the provider's authorization endpoint + * + * Defaults to the `authorization_endpoint` provided through discovery + */ + authorization_endpoint?: string; + /** + * The URL to use for the provider's userinfo endpoint + * + * Defaults to the `userinfo_endpoint` provided through discovery + */ + userinfo_endpoint?: string; + /** + * The URL to use for the provider's token endpoint + * + * Defaults to the `token_endpoint` provided through discovery + */ + token_endpoint?: string; + /** + * The URL to use for getting the provider's public keys + * + * Defaults to the `jwks_uri` provided through discovery + */ + jwks_uri?: string; + /** + * The response mode we ask the provider to use for the callback + */ + response_mode?: ResponseMode; + /** + * How claims should be imported from the `id_token` provided by the provider + */ + claims_imports?: ClaimsImports; + /** + * Additional parameters to include in the authorization request + * + * Orders of the keys are not preserved. + */ + additional_authorization_parameters?: { + [k: string]: string; + }; + /** + * Whether the `login_hint` should be forwarded to the provider in the authorization request. + * + * Defaults to `false`. + */ + forward_login_hint?: boolean; + /** + * What to do when receiving an OIDC Backchannel logout request. + * + * Defaults to "do_nothing". + */ + on_backchannel_logout?: OnBackchannelLogout; + [k: string]: unknown; +} +export interface SignInWithApple { + /** + * The private key file used to sign the `id_token` + */ + private_key_file?: string; + /** + * The private key used to sign the `id_token` + */ + private_key?: string; + /** + * The Team ID of the Apple Developer Portal + */ + team_id: string; + /** + * The key ID of the Apple Developer Portal + */ + key_id: string; + [k: string]: unknown; +} +/** + * How claims should be imported + */ +export interface ClaimsImports { + /** + * How to determine the subject of the user + */ + subject?: SubjectImportPreference; + /** + * Import the localpart of the MXID + */ + localpart?: LocalpartImportPreference; + /** + * Import the displayname of the user. + */ + displayname?: DisplaynameImportPreference; + /** + * Import the email address of the user based on the `email` and `email_verified` claims + */ + email?: EmailImportPreference; + /** + * Set a human-readable name for the upstream account for display purposes + */ + account_name?: AccountNameImportPreference; + [k: string]: unknown; +} +/** + * What should be done for the subject attribute + */ +export interface SubjectImportPreference { + /** + * The Jinja2 template to use for the subject attribute + * + * If not provided, the default template is `{{ user.sub }}` + */ + template?: string; + [k: string]: unknown; +} +/** + * What should be done for the localpart attribute + */ +export interface LocalpartImportPreference { + /** + * How to handle the attribute + */ + action?: ImportAction; + /** + * The Jinja2 template to use for the localpart attribute + * + * If not provided, the default template is `{{ user.preferred_username }}` + */ + template?: string; + /** + * How to handle conflicts on the claim, default value is `Fail` + */ + on_conflict?: OnConflict; + [k: string]: unknown; +} +/** + * What should be done for the displayname attribute + */ +export interface DisplaynameImportPreference { + /** + * How to handle the attribute + */ + action?: ImportAction; + /** + * The Jinja2 template to use for the displayname attribute + * + * If not provided, the default template is `{{ user.name }}` + */ + template?: string; + [k: string]: unknown; +} +/** + * What should be done with the email attribute + */ +export interface EmailImportPreference { + /** + * How to handle the claim + */ + action?: ImportAction; + /** + * The Jinja2 template to use for the email address attribute + * + * If not provided, the default template is `{{ user.email }}` + */ + template?: string; + [k: string]: unknown; +} +/** + * What should be done for the account name attribute + */ +export interface AccountNameImportPreference { + /** + * The Jinja2 template to use for the account name. This name is only used for display purposes. + * + * If not provided, it will be ignored. + */ + template?: string; + [k: string]: unknown; +} +/** + * Configuration section for tweaking the branding of the service + */ +export interface BrandingConfig { + /** + * A human-readable name. Defaults to the server's address. + */ + service_name?: string; + /** + * Link to a privacy policy, displayed in the footer of web pages and emails. It is also advertised to clients through the `op_policy_uri` OIDC provider metadata. + */ + policy_uri?: string; + /** + * Link to a terms of service document, displayed in the footer of web pages and emails. It is also advertised to clients through the `op_tos_uri` OIDC provider metadata. + */ + tos_uri?: string; + /** + * Legal imprint, displayed in the footer in the footer of web pages and emails. + */ + imprint?: string; + /** + * Logo displayed in some web pages. + */ + logo_uri?: string; + [k: string]: unknown; +} +/** + * Configuration section to setup CAPTCHA protection on a few operations + */ +export interface CaptchaConfig { + /** + * Which service should be used for CAPTCHA protection + */ + service?: CaptchaServiceKind; + /** + * The site key to use + */ + site_key?: string; + /** + * The secret key to use + */ + secret_key?: string; + [k: string]: unknown; +} +/** + * Configuration section to configure features related to account management + */ +export interface AccountConfig { + /** + * Whether users are allowed to change their email addresses. Defaults to `true`. + */ + email_change_allowed?: boolean; + /** + * Whether users are allowed to change their display names. Defaults to `true`. + * + * This should be in sync with the policy in the homeserver configuration. + */ + displayname_change_allowed?: boolean; + /** + * Whether to enable self-service password registration. Defaults to `false` if password authentication is enabled. + * + * This has no effect if password login is disabled. + */ + password_registration_enabled?: boolean; + /** + * Whether users are allowed to change their passwords. Defaults to `true`. + * + * This has no effect if password login is disabled. + */ + password_change_allowed?: boolean; + /** + * Whether email-based password recovery is enabled. Defaults to `false`. + * + * This has no effect if password login is disabled. + */ + password_recovery_enabled?: boolean; + /** + * Whether users are allowed to delete their own account. Defaults to `true`. + */ + account_deactivation_allowed?: boolean; + /** + * Whether users can log in with their email address. Defaults to `false`. + * + * This has no effect if password login is disabled. + */ + login_with_email_allowed?: boolean; + /** + * Whether registration tokens are required for password registrations. Defaults to `false`. + * + * When enabled, users must provide a valid registration token during password registration. This has no effect if password registration is disabled. + */ + registration_token_required?: boolean; + [k: string]: unknown; +} +/** + * Configuration sections for experimental options + * + * Do not change these options unless you know what you are doing. + */ +export interface ExperimentalConfig { + /** + * Time-to-live of access tokens in seconds. Defaults to 5 minutes. + */ + access_token_ttl?: number; + /** + * Time-to-live of compatibility access tokens in seconds. Defaults to 5 minutes. + */ + compat_token_ttl?: number; + /** + * Experimetal feature to automatically expire inactive sessions + * + * Disabled by default + */ + inactive_session_expiration?: InactiveSessionExpirationConfig; + /** + * Experimental feature to show a plan management tab and iframe. This value is passed through "as is" to the client without any validation. + */ + plan_management_iframe_uri?: string; + [k: string]: unknown; +} +/** + * Configuration options for the inactive session expiration feature + */ +export interface InactiveSessionExpirationConfig { + /** + * Time after which an inactive session is automatically finished + */ + ttl: number; + /** + * Should compatibility sessions expire after inactivity + */ + expire_compat_sessions?: boolean; + /** + * Should OAuth 2.0 sessions expire after inactivity + */ + expire_oauth_sessions?: boolean; + /** + * Should user sessions expire after inactivity + */ + expire_user_sessions?: boolean; + [k: string]: unknown; +} diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts index c177376ee4..a9f3d553ec 100644 --- a/packages/element-web-playwright-common/src/testcontainers/mas.ts +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -19,6 +19,12 @@ import { getFreePort } from "../utils/port.js"; import { deepCopy } from "../utils/object.js"; import { type Credentials } from "../utils/api.js"; +// This file can be updated by running: +// +// curl -sL https://element-hq.github.io/matrix-authentication-service/config.schema.json \ +// | npx json-schema-to-typescript -o packages/element-web-playwright-common/src/testconainers/mas-config.ts +import type { RootConfig as MasConfig } from "./mas-config.js"; + const DEFAULT_CONFIG = { http: { listeners: [ @@ -116,6 +122,10 @@ const DEFAULT_CONFIG = { account: { password_registration_enabled: true, }, + matrix: { + kind: "synapse", + secret: "", // Needs to be set + }, rate_limiting: { login: { burst: 10, @@ -126,12 +136,7 @@ const DEFAULT_CONFIG = { per_second: 1, }, }, -}; - -/** - * Incomplete type for the MAS configuration. - */ -export type MasConfig = typeof DEFAULT_CONFIG; +} satisfies MasConfig; /** * A container running the Matrix Authentication Service. @@ -149,9 +154,11 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { ) { super(image); - this.config = deepCopy(DEFAULT_CONFIG); - this.config.database.username = db.getUsername(); - this.config.database.password = db.getPassword(); + const initialConfig = deepCopy(DEFAULT_CONFIG); + initialConfig.database.username = db.getUsername(); + initialConfig.database.password = db.getPassword(); + + this.config = initialConfig; this.withExposedPorts(8080, 8081) .withWaitStrategy(Wait.forHttp("/health", 8081)) @@ -162,7 +169,7 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { * Adds additional configuration to the MAS config. * @param config - additional config fields to add */ - public withConfig(config: object): this { + public withConfig(config: Partial): this { this.config = { ...this.config, ...config, @@ -177,8 +184,11 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { // MAS config issuer needs to know what URL it'll be accessed from, so we have to map the port manually const port = await getFreePort(); - this.config.http.public_base = `http://localhost:${port}/`; - this.config.http.issuer = `http://localhost:${port}/`; + this.config.http = { + public_base: `http://localhost:${port}/`, + issuer: `http://localhost:${port}/`, + ...this.config.http, + }; this.withExposedPorts({ container: 8080, From 6520352453f5ecdfab50192cb37ee6de9227573e Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 5 Aug 2025 11:03:37 +0200 Subject: [PATCH 045/113] Re-export the MasConfig type again --- .../element-web-playwright-common/src/testcontainers/mas.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts index a9f3d553ec..c8605ae5c1 100644 --- a/packages/element-web-playwright-common/src/testcontainers/mas.ts +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -18,13 +18,14 @@ import * as YAML from "yaml"; import { getFreePort } from "../utils/port.js"; import { deepCopy } from "../utils/object.js"; import { type Credentials } from "../utils/api.js"; - // This file can be updated by running: // // curl -sL https://element-hq.github.io/matrix-authentication-service/config.schema.json \ // | npx json-schema-to-typescript -o packages/element-web-playwright-common/src/testconainers/mas-config.ts import type { RootConfig as MasConfig } from "./mas-config.js"; +export { type MasConfig }; + const DEFAULT_CONFIG = { http: { listeners: [ From 4a9559bb169b389db14a3cc081b4e1db3aa4a697 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 5 Aug 2025 11:25:47 +0200 Subject: [PATCH 046/113] element-web-playwright-common: bump version to 1.4.5 --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 556708d027..c97f1d6b8c 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.4.4", + "version": "1.4.5", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 636cee5dcb281f28792c4ef25aeab94954da52df Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 5 Aug 2025 12:04:01 +0200 Subject: [PATCH 047/113] Fix the MAS config override ordering --- packages/element-web-playwright-common/package.json | 2 +- .../element-web-playwright-common/src/testcontainers/mas.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index c97f1d6b8c..9a5d8e3d1c 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.4.5", + "version": "1.4.6", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts index c8605ae5c1..6330897ada 100644 --- a/packages/element-web-playwright-common/src/testcontainers/mas.ts +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -186,9 +186,9 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { const port = await getFreePort(); this.config.http = { + ...this.config.http, public_base: `http://localhost:${port}/`, issuer: `http://localhost:${port}/`, - ...this.config.http, }; this.withExposedPorts({ From 885e7c36d9eaaeb483016d9ca44ab9641a3052e7 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 3 Oct 2025 12:25:21 +0100 Subject: [PATCH 048/113] Add `homeserverBaseUrl` field to `Credentials` `Credentials` is used to record the access token etc once we log in a or register a user. An access token isn't much use to you unless you know where to send it, so this patch addes a new field, `homeserverUrl`, which contains the base public URL for the homeserver. --- .../src/testcontainers/mas.ts | 9 +++++++-- .../src/testcontainers/synapse.ts | 11 ++++++++--- .../element-web-playwright-common/src/utils/api.ts | 8 +++++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts index 6330897ada..294afb7343 100644 --- a/packages/element-web-playwright-common/src/testcontainers/mas.ts +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -302,7 +302,7 @@ export class StartedMatrixAuthenticationServiceContainer extends AbstractStarted password: string, displayName?: string, admin = false, - ): Promise { + ): Promise> { const userId = await this.manageRegisterUser(username, password, displayName, admin); const { deviceId, accessToken } = await this.manageIssueCompatibilityToken(username, admin); @@ -319,11 +319,16 @@ export class StartedMatrixAuthenticationServiceContainer extends AbstractStarted /** * Registers a user + * * @param username - the username of the user to register * @param password - the password of the user to register * @param displayName - optional display name to set on the newly registered user */ - public async registerUser(username: string, password: string, displayName?: string): Promise { + public async registerUser( + username: string, + password: string, + displayName?: string, + ): Promise> { return this.registerUserInternal(username, password, displayName, false); } diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index 84ac60957b..4662031473 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -389,6 +389,7 @@ export class StartedSynapseContainer extends AbstractStartedContainer implements return { homeServer: data.home_server || data.user_id.split(":").slice(1).join(":"), + homeserverBaseUrl: this.baseUrl, accessToken: data.access_token, userId: data.user_id, deviceId: data.device_id, @@ -433,7 +434,10 @@ export class StartedSynapseContainer extends AbstractStartedContainer implements * @param password - login password */ public async loginUser(userId: string, password: string): Promise { - return this.csApi.loginUser(userId, password); + return { + ...(await this.csApi.loginUser(userId, password)), + homeserverBaseUrl: this.baseUrl, + }; } /** @@ -480,8 +484,9 @@ export class StartedSynapseWithMasContainer extends StartedSynapseContainer { * @param password - the password of the user to register * @param displayName - optional display name to set on the newly registered user */ - public registerUser(username: string, password: string, displayName?: string): Promise { - return this.mas.registerUser(username, password, displayName); + public async registerUser(username: string, password: string, displayName?: string): Promise { + const registered = await this.mas.registerUser(username, password, displayName); + return { ...registered, homeserverBaseUrl: this.baseUrl }; } /** diff --git a/packages/element-web-playwright-common/src/utils/api.ts b/packages/element-web-playwright-common/src/utils/api.ts index 55258e7408..8ce6f2f5dc 100644 --- a/packages/element-web-playwright-common/src/utils/api.ts +++ b/packages/element-web-playwright-common/src/utils/api.ts @@ -64,10 +64,16 @@ export class Api { * Credentials for a user. */ export interface Credentials { + /** The base URL of the homeserver's CS API. */ + homeserverBaseUrl: string; + accessToken: string; userId: string; deviceId: string; + + /** The domain part of the user's matrix ID. */ homeServer: string; + password: string | null; // null for password-less users displayName?: string; username: string; // the localpart of the userId @@ -86,7 +92,7 @@ export class ClientServerApi extends Api { * @param userId - The user ID to register. * @param password - The password to use for the user. */ - public async loginUser(userId: string, password: string): Promise { + public async loginUser(userId: string, password: string): Promise> { const json = await this.request<{ access_token: string; user_id: string; From 92b9896debea15c9ff8f51691cda2e3cbbbd7b2b Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 3 Oct 2025 12:28:56 +0100 Subject: [PATCH 049/113] Factor out a helper function `populateLocalStorageWithCredentials` This just takes the innards of the `pageWithCredentials` fixture and makes it reusable in contexts outside the fixture. (This can be useful if you want to use Element with an account other than the default provided by the `credentials` fixture.) --- .../src/fixtures/user.ts | 54 ++++++++++--------- .../src/index.ts | 2 + 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/packages/element-web-playwright-common/src/fixtures/user.ts b/packages/element-web-playwright-common/src/fixtures/user.ts index 74c4c17a3d..96e358c458 100644 --- a/packages/element-web-playwright-common/src/fixtures/user.ts +++ b/packages/element-web-playwright-common/src/fixtures/user.ts @@ -12,6 +12,32 @@ import { sample, uniqueId } from "lodash-es"; import { test as base } from "./services.js"; import { 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. */ +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"); + + 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 const test = base.extend<{ /** * The displayname to use for the user registered in {@link #credentials}. @@ -38,7 +64,7 @@ export const test = base.extend<{ /** * A (rather poorly-named) test fixture which registers a user per {@link #credentials}, stores - * the credentials into localStorage per {@link #homeserver}, and then loads the front page of the + * the credentials into localStorage per {@link #pageWithCredentials}, and then loads the front page of the * app. */ user: Credentials; @@ -58,30 +84,8 @@ export const test = base.extend<{ }); }, - pageWithCredentials: async ({ page, homeserver, credentials }, use) => { - await page.addInitScript( - ({ baseUrl, credentials }) => { - // Seed the localStorage with the required credentials - window.localStorage.setItem("mx_hs_url", baseUrl); - 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"); - - 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", - }), - ); - }, - { baseUrl: homeserver.baseUrl, credentials }, - ); + pageWithCredentials: async ({ page, credentials }, use) => { + await populateLocalStorageWithCredentials(page, credentials); await use(page); }, diff --git a/packages/element-web-playwright-common/src/index.ts b/packages/element-web-playwright-common/src/index.ts index 4f13e8be2d..40fb2eef3f 100644 --- a/packages/element-web-playwright-common/src/index.ts +++ b/packages/element-web-playwright-common/src/index.ts @@ -10,6 +10,8 @@ import { type Config as BaseConfig } from "@element-hq/element-web-module-api"; import { test as base } from "./fixtures/index.js"; +export { populateLocalStorageWithCredentials } from "./fixtures/user.js"; + // Enable experimental service worker support // See https://playwright.dev/docs/service-workers-experimental#how-to-enable process.env["PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS"] = "1"; From 595141cf4b280002a8b0a8c2fd450c5edfb73401 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 3 Oct 2025 12:50:02 +0100 Subject: [PATCH 050/113] Playwright: factor out helpers for config.json Factor out some helper functions for building `config.json` files, and adding the relevant routes to browser contexts. --- .../src/index.ts | 30 ++------ .../src/utils/config_json.ts | 71 +++++++++++++++++++ 2 files changed, 75 insertions(+), 26 deletions(-) create mode 100644 packages/element-web-playwright-common/src/utils/config_json.ts diff --git a/packages/element-web-playwright-common/src/index.ts b/packages/element-web-playwright-common/src/index.ts index 4f13e8be2d..cffc44668a 100644 --- a/packages/element-web-playwright-common/src/index.ts +++ b/packages/element-web-playwright-common/src/index.ts @@ -9,6 +9,9 @@ Please see LICENSE files in the repository root for full details. import { type Config as BaseConfig } from "@element-hq/element-web-module-api"; import { test as base } from "./fixtures/index.js"; +import { routeConfigJson } from "./utils/config_json.js"; + +export * from "./utils/config_json.js"; // Enable experimental service worker support // See https://playwright.dev/docs/service-workers-experimental#how-to-enable @@ -68,32 +71,7 @@ export const test = base.extend({ labsFlags: async ({}, use) => use([]), disablePresence: async ({}, use) => use(false), page: async ({ homeserver, context, page, config, labsFlags, disablePresence }, use) => { - await context.route(`http://localhost:8080/config.json*`, async (route) => { - const json = { - ...CONFIG_JSON, - ...config, - default_server_config: { - "m.homeserver": { - base_url: homeserver.baseUrl, - }, - ...config.default_server_config, - }, - }; - json["features"] = { - ...json["features"], - // Enable the lab features - ...labsFlags.reduce>((obj, flag) => { - obj[flag] = true; - return obj; - }, {}), - }; - if (disablePresence) { - json["enable_presence_by_hs_url"] = { - [homeserver.baseUrl]: false, - }; - } - await route.fulfill({ json }); - }); + await routeConfigJson(context, homeserver.baseUrl, config, labsFlags, disablePresence); await use(page); }, }); diff --git a/packages/element-web-playwright-common/src/utils/config_json.ts b/packages/element-web-playwright-common/src/utils/config_json.ts new file mode 100644 index 0000000000..f2a98f47a4 --- /dev/null +++ b/packages/element-web-playwright-common/src/utils/config_json.ts @@ -0,0 +1,71 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { BrowserContext, Page } from "@playwright/test"; + +import { Config, CONFIG_JSON } from "../index.js"; + +/** Construct a suitable config.json for the given homeserver + * + * @param homeserverBaseUrl - The `baseUrl` of the homeserver that the client should be configured to connect to. + * @param additionalConfig - Additional config to add to the default config.json. + * @param labsFlags - Lab flags to enable in the client. + * @param disablePresence - Whether to disable presence for the given homeserver. + */ +export function buildConfigJson( + homeserverBaseUrl: string, + additionalConfig: Partial = {}, + labsFlags: string[] = [], + disablePresence: boolean = false, +): Partial { + const json = { + ...CONFIG_JSON, + ...additionalConfig, + default_server_config: { + "m.homeserver": { + base_url: homeserverBaseUrl, + }, + ...additionalConfig.default_server_config, + }, + }; + json["features"] = { + ...json["features"], + // Enable the lab features + ...labsFlags.reduce>((obj, flag) => { + obj[flag] = true; + return obj; + }, {}), + }; + if (disablePresence) { + json["enable_presence_by_hs_url"] = { + [homeserverBaseUrl]: false, + }; + } + return json; +} + +/** + * Add a route to the browser context/page which will serve a suitable config.json for the given homeserver. + * + * @param context - The browser context or page to route the config.json to. + * @param homeserverBaseUrl - The `baseUrl` of the homeserver that the client should be configured to connect to. + * @param additionalConfig - Additional config to add to the default config.json. + * @param labsFlags - Lab flags to enable in the client. + * @param disablePresence - Whether to disable presence for the given homeserver. + */ +export async function routeConfigJson( + context: BrowserContext | Page, + homeserverBaseUrl: string, + additionalConfig: Partial = {}, + labsFlags: string[] = [], + disablePresence: boolean = false, +): Promise { + await context.route(`http://localhost:8080/config.json*`, async (route) => { + const json = buildConfigJson(homeserverBaseUrl, additionalConfig, labsFlags, disablePresence); + await route.fulfill({ json }); + }); +} From 68de37a6cbca389464008d1f7d97bb154adfd844 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 3 Oct 2025 12:54:41 +0100 Subject: [PATCH 051/113] Playwright: add a `createNewInstance` utility function Sometimes it's useful to be able to use two separate Element-Web instances, in isolated browsers. This patch adds a utility method which creates such an instance. --- .../src/index.ts | 1 + .../src/utils/context.ts | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 packages/element-web-playwright-common/src/utils/context.ts diff --git a/packages/element-web-playwright-common/src/index.ts b/packages/element-web-playwright-common/src/index.ts index 2e041d0355..518def9eaa 100644 --- a/packages/element-web-playwright-common/src/index.ts +++ b/packages/element-web-playwright-common/src/index.ts @@ -12,6 +12,7 @@ import { test as base } from "./fixtures/index.js"; import { routeConfigJson } from "./utils/config_json.js"; export * from "./utils/config_json.js"; +export * from "./utils/context.js"; export { populateLocalStorageWithCredentials } from "./fixtures/user.js"; diff --git a/packages/element-web-playwright-common/src/utils/context.ts b/packages/element-web-playwright-common/src/utils/context.ts new file mode 100644 index 0000000000..fdc45a6736 --- /dev/null +++ b/packages/element-web-playwright-common/src/utils/context.ts @@ -0,0 +1,38 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { Browser } from "playwright-core"; +import { Page } from "@playwright/test"; + +import { Credentials } from "./api.js"; +import { Config } from "../index.js"; +import { routeConfigJson } from "./config_json.js"; +import { populateLocalStorageWithCredentials } from "../fixtures/user.js"; + +/** Create a new instance of the application, in a separate browser context, using the given credentials. + * + * @param browser - the browser to use + * @param credentials - the credentials to use for the new instance + * @param additionalConfig - additional config for the `config.json` for the new instance + * @param labsFlags - additional labs flags for the `config.json` for the new instance + * @param disablePresence - whether to disable presence for the new instance + */ +export async function createNewInstance( + browser: Browser, + credentials: Credentials, + additionalConfig: Partial = {}, + labsFlags: string[] = [], + disablePresence: boolean = false, +): Promise { + const context = await browser.newContext(); + await routeConfigJson(context, credentials.homeserverBaseUrl, additionalConfig, labsFlags, disablePresence); + const page = await context.newPage(); + await populateLocalStorageWithCredentials(page, credentials); + await page.goto("/"); + await page.waitForSelector(".mx_MatrixChat", { timeout: 30000 }); + return page; +} From 31d6b73476df93464ad9ad551659bba9837af5c3 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 3 Oct 2025 16:59:18 +0100 Subject: [PATCH 052/113] v2.0.0 --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 9a5d8e3d1c..1d07e7af9e 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "1.4.6", + "version": "2.0.0", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 4cfa5b3265c068bb3f74337f9ec904beb53f3577 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 4 Nov 2025 23:04:44 +0000 Subject: [PATCH 053/113] Use DOCKER_HOST env var to find docker socket --- .../playwright-screenshots.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index a676491b28..ca4525fe40 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -24,6 +24,20 @@ build_image() { docker build -t "$IMAGE_NAME" --build-arg "PLAYWRIGHT_VERSION=$PW_VERSION" "$SCRIPT_DIR" } +# Find the docker socket on the host +case "$DOCKER_HOST" in + unix://*) + docker_sock="${DOCKER_HOST:7}" + ;; + "") + docker_sock="/var/run/docker.sock" + ;; + *) + echo "$0: unsupported DOCKER_HOST setting '${DOCKER_HOST}'" >&2 + exit 1; + ;; +esac + RUN_ARGS=( --rm --network host @@ -33,7 +47,7 @@ RUN_ARGS=( # Bind mount the working directory into the container -v $(pwd):/work/ # Bind mount the docker socket so we can run docker commands from the container - -v /var/run/docker.sock:/var/run/docker.sock + -v "${docker_sock}":/var/run/docker.sock # Bind mount /tmp so we can store temporary files -v /tmp/:/tmp/ -it From 6502ffcbe08390059ba4974b7d4c74453cfe90fe Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 4 Nov 2025 23:46:13 +0000 Subject: [PATCH 054/113] Disable ryuk reaper in the playwright container ... thus making it possible to use `testcontainers` inside unprivileged containers. --- packages/element-web-playwright-common/Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/element-web-playwright-common/Dockerfile b/packages/element-web-playwright-common/Dockerfile index c8bf12e503..e6ac702766 100644 --- a/packages/element-web-playwright-common/Dockerfile +++ b/packages/element-web-playwright-common/Dockerfile @@ -6,4 +6,8 @@ WORKDIR /work # fonts-dejavu is needed for the same RTL rendering as on CI RUN apt-get update && apt-get -y install docker.io fonts-dejavu +# Disable the ryuk resource reaper, because it doesn't work in environments +# that disallow starting privileged containers (such as rootless containers) +ENV TESTCONTAINERS_RYUK_DISABLED=true + ENTRYPOINT ["npx", "playwright", "test", "--update-snapshots", "--reporter", "line"] From 28384f11123ad9a7813370f2ea81f2e72ea02b47 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 4 Nov 2025 23:47:09 +0000 Subject: [PATCH 055/113] playwright-screenshots: clean up output and error handling Somebody wrote those `pushd` and `popd` lines without testing them. Redirections bind tighter than `||` so those lines were always spamming the console. Using `set -e` is more reliable than always checking exit codes anyway. --- .../playwright-screenshots.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index ca4525fe40..6b927f7cf5 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -e + # Handle symlinks here as we tend to be executed as an npm binary SCRIPT_PATH=$(readlink -f "$0") SCRIPT_DIR=$(dirname "$SCRIPT_PATH") @@ -83,11 +85,11 @@ done build_image # Ensure we pass all symlinked node_modules to the container -pushd node_modules || exit > /dev/null +pushd node_modules > /dev/null SYMLINKS=$(find . -maxdepth 2 -type l -not -path "./.bin/*") -popd || exit > /dev/null +popd > /dev/null for LINK in $SYMLINKS; do - TARGET=$(readlink -f "node_modules/$LINK") + TARGET=$(readlink -f "node_modules/$LINK") || true if [ -d "$TARGET" ]; then echo "mounting linked package ${LINK:2} in container" RUN_ARGS+=( "-v" "$TARGET:/work/node_modules/${LINK:2}" ) From 2af8e3c21f7fe1b265cc3009364897127e8204a5 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 4 Nov 2025 23:49:31 +0000 Subject: [PATCH 056/113] Warn about linked node modules under podman Thanks to a podman bug, the symlink workaround doesn't work there. Let's emit a noisy warning. --- .../playwright-screenshots.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index 6b927f7cf5..99d8d1fb4b 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -91,6 +91,18 @@ popd > /dev/null for LINK in $SYMLINKS; do TARGET=$(readlink -f "node_modules/$LINK") || true if [ -d "$TARGET" ]; then + if docker --version | grep -q podman; then + echo -e "\033[31m" >&2 + cat <<'EOF' >&2 +WARNING: `node_modules` contains symlinks, and the support for this in +`playwright-screenshots.sh` is broken under podman due to +https://github.com/containers/podman/issues/25947. + +If you get errors such as 'Error: crun: creating ``', then retry this +having `yarn unlink`ed the relevant node modules. +EOF + echo -e "\033[0m" >&2 + fi echo "mounting linked package ${LINK:2} in container" RUN_ARGS+=( "-v" "$TARGET:/work/node_modules/${LINK:2}" ) fi From 49ad9c1d9c0a0cce8cd3dd58d666b31a63810c5e Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 5 Nov 2025 16:22:24 +0000 Subject: [PATCH 057/113] Start Ryuk ourselves --- .../element-web-playwright-common/Dockerfile | 4 ---- .../playwright-screenshots.sh | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/element-web-playwright-common/Dockerfile b/packages/element-web-playwright-common/Dockerfile index e6ac702766..c8bf12e503 100644 --- a/packages/element-web-playwright-common/Dockerfile +++ b/packages/element-web-playwright-common/Dockerfile @@ -6,8 +6,4 @@ WORKDIR /work # fonts-dejavu is needed for the same RTL rendering as on CI RUN apt-get update && apt-get -y install docker.io fonts-dejavu -# Disable the ryuk resource reaper, because it doesn't work in environments -# that disallow starting privileged containers (such as rootless containers) -ENV TESTCONTAINERS_RYUK_DISABLED=true - ENTRYPOINT ["npx", "playwright", "test", "--update-snapshots", "--reporter", "line"] diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index 99d8d1fb4b..80de4fd146 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -108,4 +108,24 @@ EOF fi done +# Our Playwright fixtures use Testcontainers [1], which in uses a docker image +# called Ryuk [2], which will clean up any dangling containers/networks/etc +# after a timeout, if the parent process dies unexpectedly. +# +# To do this, Ryuk requires access to the docker socket, so Testcontainers +# starts the Ryuk container with a bind-mount of `/var/run/docker.sock`. +# However, we're going to be running Playwright (and hence Testcontainers) +# itself in a container, but talking to the Docker daemon on the *host*, which +# means that bind mounts will be relative to the *host* filesystem. In short, +# it will try to bind-mount the *host's* `/var/run/docker.sock` rather than +# that from inside the element-web-playwright-common container. +# +# To solve this problem, we start Ryuk ourselves (with the correct docker +# socket) rather than waiting for Testcontainers to do so. Testcontainers will +# find the running Ryuk instance and connect to it rather than start a new one. +# +# [1] https://testcontainers.com/ +# [2] https://github.com/testcontainers/moby-ryuk +docker run -d --rm --label org.testcontainers.ryuk=true -v "${docker_sock}":/var/run/docker.sock -p 8080 --name="playwright-ryuk" testcontainers/ryuk:0.14.0 + docker run "${RUN_ARGS[@]}" "$IMAGE_NAME" "${DEFAULT_ARGS[@]}" "$@" From e31efda9b4a45b8f8ba727c69470fd8f763341d1 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:45:58 +0000 Subject: [PATCH 058/113] Apply suggestion from @richvdh --- .../element-web-playwright-common/playwright-screenshots.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index 80de4fd146..e137017a32 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -108,7 +108,7 @@ EOF fi done -# Our Playwright fixtures use Testcontainers [1], which in uses a docker image +# Our Playwright fixtures use Testcontainers [1], which uses a docker image # called Ryuk [2], which will clean up any dangling containers/networks/etc # after a timeout, if the parent process dies unexpectedly. # From c2f45351536f2a17fc4547db6d994cc53db463c9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Nov 2025 12:16:31 +0000 Subject: [PATCH 059/113] Delint --- .../element-web-playwright-common/src/expect/index.ts | 6 +++--- .../src/expect/screenshot.ts | 2 +- .../element-web-playwright-common/src/fixtures/user.ts | 2 +- .../src/testcontainers/HomeserverContainer.ts | 4 ++-- .../src/testcontainers/synapse.ts | 2 +- .../src/utils/config_json.ts | 4 ++-- .../element-web-playwright-common/src/utils/context.ts | 8 ++++---- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/element-web-playwright-common/src/expect/index.ts b/packages/element-web-playwright-common/src/expect/index.ts index b62352d3ea..3ef2a00bf1 100644 --- a/packages/element-web-playwright-common/src/expect/index.ts +++ b/packages/element-web-playwright-common/src/expect/index.ts @@ -9,10 +9,10 @@ import { mergeExpects, type Expect } from "@playwright/test"; import { expect as screenshotExpectations, - Expectations as ScreenshotExpectations, - ToMatchScreenshotOptions, + type Expectations as ScreenshotExpectations, + type ToMatchScreenshotOptions, } from "./screenshot.js"; -import { expect as axeExpectations, Expectations as AxeExpectations } from "./axe.js"; +import { expect as axeExpectations, type Expectations as AxeExpectations } from "./axe.js"; export const expect = mergeExpects(screenshotExpectations, axeExpectations) as Expect< ScreenshotExpectations & AxeExpectations diff --git a/packages/element-web-playwright-common/src/expect/screenshot.ts b/packages/element-web-playwright-common/src/expect/screenshot.ts index 86687b28e4..9936ae982b 100644 --- a/packages/element-web-playwright-common/src/expect/screenshot.ts +++ b/packages/element-web-playwright-common/src/expect/screenshot.ts @@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details. import { test, expect as baseExpect, - ElementHandle, + type ElementHandle, type ExpectMatcherState, type Locator, type Page, diff --git a/packages/element-web-playwright-common/src/fixtures/user.ts b/packages/element-web-playwright-common/src/fixtures/user.ts index 96e358c458..a5fae14b9f 100644 --- a/packages/element-web-playwright-common/src/fixtures/user.ts +++ b/packages/element-web-playwright-common/src/fixtures/user.ts @@ -10,7 +10,7 @@ import { type Page } from "@playwright/test"; import { sample, uniqueId } from "lodash-es"; import { test as base } from "./services.js"; -import { Credentials } from "../utils/api.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. */ export async function populateLocalStorageWithCredentials(page: Page, credentials: Credentials) { diff --git a/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts b/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts index ccb242767b..57ceb653db 100644 --- a/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts +++ b/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts @@ -9,8 +9,8 @@ import { type AbstractStartedContainer, type GenericContainer } from "testcontai import { type APIRequestContext, type TestInfo } from "@playwright/test"; import { type StartedMatrixAuthenticationServiceContainer } from "./mas"; -import { ClientServerApi, Credentials } from "../utils/api"; -import { StartedMailpitContainer } from "./mailpit"; +import { type ClientServerApi, type Credentials } from "../utils/api"; +import { type StartedMailpitContainer } from "./mailpit"; export interface HomeserverInstance { readonly baseUrl: string; diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index 4662031473..dae17316b0 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -23,7 +23,7 @@ import { deepCopy } from "../utils/object.js"; import { type HomeserverContainer, type StartedHomeserverContainer } from "./HomeserverContainer.js"; import { type StartedMatrixAuthenticationServiceContainer } from "./mas.js"; import { Api, ClientServerApi, type Verb, type Credentials } from "../utils/api.js"; -import { StartedMailpitContainer } from "./mailpit.js"; +import { type StartedMailpitContainer } from "./mailpit.js"; const DEFAULT_CONFIG = { server_name: "localhost", diff --git a/packages/element-web-playwright-common/src/utils/config_json.ts b/packages/element-web-playwright-common/src/utils/config_json.ts index f2a98f47a4..cc97a07be5 100644 --- a/packages/element-web-playwright-common/src/utils/config_json.ts +++ b/packages/element-web-playwright-common/src/utils/config_json.ts @@ -5,9 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { BrowserContext, Page } from "@playwright/test"; +import { type BrowserContext, type Page } from "@playwright/test"; -import { Config, CONFIG_JSON } from "../index.js"; +import { type Config, CONFIG_JSON } from "../index.js"; /** Construct a suitable config.json for the given homeserver * diff --git a/packages/element-web-playwright-common/src/utils/context.ts b/packages/element-web-playwright-common/src/utils/context.ts index fdc45a6736..c11477fe9c 100644 --- a/packages/element-web-playwright-common/src/utils/context.ts +++ b/packages/element-web-playwright-common/src/utils/context.ts @@ -5,11 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { Browser } from "playwright-core"; -import { Page } from "@playwright/test"; +import { type Browser } from "playwright-core"; +import { type Page } from "@playwright/test"; -import { Credentials } from "./api.js"; -import { Config } from "../index.js"; +import { type Credentials } from "./api.js"; +import { type Config } from "../index.js"; import { routeConfigJson } from "./config_json.js"; import { populateLocalStorageWithCredentials } from "../fixtures/user.js"; From 0788c3a207aaf37d8defd0fba26f9272caf59237 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 07:36:22 +0000 Subject: [PATCH 060/113] Bump glob in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [glob](https://github.com/isaacs/node-glob). Updates `glob` from 11.0.1 to 11.1.0 - [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md) - [Commits](https://github.com/isaacs/node-glob/compare/v11.0.1...v11.1.0) --- updated-dependencies: - dependency-name: glob dependency-version: 11.1.0 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 1d07e7af9e..cdefa2e8af 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -31,7 +31,7 @@ "dependencies": { "@axe-core/playwright": "^4.10.1", "@testcontainers/postgresql": "^11.0.0", - "glob": "^11.0.3", + "glob": "^11.1.0", "lodash-es": "^4.17.21", "mailpit-api": "^1.2.0", "strip-ansi": "^7.1.0", From 76b07d736c3d9baca17171565bdaeebb92e54f9e Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 27 Nov 2025 13:23:16 +0530 Subject: [PATCH 061/113] Make script compatible with yarn berry --- .../playwright-screenshots.sh | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index e137017a32..409c5baa8a 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -11,18 +11,13 @@ IMAGE_NAME="element-web-playwright-common" build_image() { echo "Building $IMAGE_NAME image in $SCRIPT_DIR" - # Build image - PW_VERSION=$( - yarn list \ - --pattern @playwright/test \ - --depth=0 \ - --json \ - --non-interactive \ - --no-progress | \ - jq -r '.data.trees[].name | split("@")[2]' \ - ) + # Fetch the playwright version + # .data.version is for yarn classic + # .children.Version is for yarn berry + PW_VERSION=$(yarn info --manifest --json @playwright/test | jq -r '.data.version // .children.Version') echo "with Playwright version $PW_VERSION" + # Build image docker build -t "$IMAGE_NAME" --build-arg "PLAYWRIGHT_VERSION=$PW_VERSION" "$SCRIPT_DIR" } From 3b31bbf4a956a35a25d240090ebc688764973713 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 27 Nov 2025 17:16:56 +0530 Subject: [PATCH 062/113] Bump version to 2.1.0 --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index cdefa2e8af..c5abe36799 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "2.0.0", + "version": "2.1.0", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 4c928d28543b18eec14334e217fc24df8b598065 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Thu, 27 Nov 2025 17:13:22 +0000 Subject: [PATCH 063/113] Avoid `mergeTests` in test fixture declarations (#138) Normally, one can find the documentation on a playwright test fixture by finding its declaration (i.e., you can ctrl-click on the fixture name and find its documentaion). However, `mergeTests` re-declares the type, making it much harder to find the documentation on a given fixture. It's easy enough to avoid `mergeTests`: we just structure our `test.extend` calls as a strict hieirarchy. --- .../element-web-playwright-common/src/fixtures/axe.ts | 5 ++++- .../element-web-playwright-common/src/fixtures/index.ts | 9 +++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/element-web-playwright-common/src/fixtures/axe.ts b/packages/element-web-playwright-common/src/fixtures/axe.ts index eeeb523aed..2c0c97af45 100644 --- a/packages/element-web-playwright-common/src/fixtures/axe.ts +++ b/packages/element-web-playwright-common/src/fixtures/axe.ts @@ -6,9 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { test as base } from "@playwright/test"; import AxeBuilder from "@axe-core/playwright"; +import { test as base } from "./user"; + +// We want to avoid using `mergeTests` because it drops useful type information about the fixtures. Instead, we extend +// the definition of `test` from `user.ts`, so that there is a linear hierarchy. export const test = base.extend<{ /** * AxeBuilder instance for the current page diff --git a/packages/element-web-playwright-common/src/fixtures/index.ts b/packages/element-web-playwright-common/src/fixtures/index.ts index 04c8d120e3..58aebfa20e 100644 --- a/packages/element-web-playwright-common/src/fixtures/index.ts +++ b/packages/element-web-playwright-common/src/fixtures/index.ts @@ -5,11 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { mergeTests } from "@playwright/test"; - -import { test as axe } from "./axe.js"; -import { test as user } from "./user.js"; - export { type Services, type WorkerOptions } from "./services.js"; -export const test = mergeTests(axe, user); +// We avoid using `mergeTests` because it drops useful type information about the fixtures. +// `axe` is the top of our stack of extensions (it extends `user`, etc), so it's the one we want to use. +export { test } from "./axe"; From 0d46bc924c95a8f4a6fc683e6b7c00f846d2ff15 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Thu, 27 Nov 2025 19:40:29 +0000 Subject: [PATCH 064/113] Fix "Error: No request context set" in `credentials` fixture (#145) Make sure that we've run the `context` fixture first, so that the request API is configured --- packages/element-web-playwright-common/src/fixtures/user.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/src/fixtures/user.ts b/packages/element-web-playwright-common/src/fixtures/user.ts index a5fae14b9f..073b6149d8 100644 --- a/packages/element-web-playwright-common/src/fixtures/user.ts +++ b/packages/element-web-playwright-common/src/fixtures/user.ts @@ -70,7 +70,11 @@ export const test = base.extend<{ user: Credentials; }>({ displayName: undefined, - credentials: async ({ homeserver, displayName: testDisplayName }, use, testInfo) => { + + // 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)!; From a7583dbdbc795d2c8f31d2e5b2bf91fe6e84058f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:39:37 +0000 Subject: [PATCH 065/113] Update dependency glob to v13 --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index c5abe36799..d71ca41594 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -31,7 +31,7 @@ "dependencies": { "@axe-core/playwright": "^4.10.1", "@testcontainers/postgresql": "^11.0.0", - "glob": "^11.1.0", + "glob": "^13.0.0", "lodash-es": "^4.17.21", "mailpit-api": "^1.2.0", "strip-ansi": "^7.1.0", From fec7923af07f1eb82471221e2b005342b1dd84ac Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Dec 2025 17:06:45 +0000 Subject: [PATCH 066/113] Update index.ts --- .../element-web-playwright-common/src/testcontainers/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/index.ts b/packages/element-web-playwright-common/src/testcontainers/index.ts index bb34e4a832..42c73f4802 100644 --- a/packages/element-web-playwright-common/src/testcontainers/index.ts +++ b/packages/element-web-playwright-common/src/testcontainers/index.ts @@ -13,4 +13,4 @@ export { MatrixAuthenticationServiceContainer, StartedMatrixAuthenticationServiceContainer, } from "./mas.js"; -export { MailpitClient, MailpitContainer, StartedMailpitContainer } from "./mailpit.js"; +export { type MailpitClient, MailpitContainer, StartedMailpitContainer } from "./mailpit.js"; From b12b59a93290a66f495dbefac77404d633ac5e58 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 13:31:30 +0000 Subject: [PATCH 067/113] Allow configuring MatrixRTC endpoints. --- .../src/testcontainers/synapse.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index dae17316b0..66e02df08d 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -170,7 +170,12 @@ const DEFAULT_CONFIG = { room_name: string; }, allow_guest_access: false, - experimental_features: {}, + experimental_features: { + msc4143_enabled: false, + }, + matrix_rtc: { + transports: [] as { type: string; [field: string]: string }[], + }, oidc_providers: [], serve_server_wellknown: true, presence: { From 894e684dab4fbf8f51d01ef31e36a389fec5e158 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 13:33:08 +0000 Subject: [PATCH 068/113] match other syntax --- .../element-web-playwright-common/src/testcontainers/synapse.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index 66e02df08d..afe3c39ef3 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -174,7 +174,7 @@ const DEFAULT_CONFIG = { msc4143_enabled: false, }, matrix_rtc: { - transports: [] as { type: string; [field: string]: string }[], + transports: Array<{ type: string; [field: string]: string }>, }, oidc_providers: [], serve_server_wellknown: true, From a8523421dab98d547a7e1467d22c63b789f0d607 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 13:42:22 +0000 Subject: [PATCH 069/113] add docs --- .../src/testcontainers/synapse.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index afe3c39ef3..55ef890f7d 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -171,6 +171,9 @@ const DEFAULT_CONFIG = { }, allow_guest_access: false, experimental_features: { + /** + * MSC4143 Matrix RTC: Required for `matrix_rtc` configuration. + */ msc4143_enabled: false, }, matrix_rtc: { From 839a757d9430f97b83aafec5f722a2f24cbe03ec Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 13:57:23 +0000 Subject: [PATCH 070/113] Bump @element-hq/element-web-playwright-common to 2.2.0 --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index d71ca41594..f626573bfc 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "2.1.0", + "version": "2.2.0", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 1bf78ac7c468b9b104e776577a7137d7de23d27b Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 14:05:04 +0000 Subject: [PATCH 071/113] use as --- .../src/testcontainers/synapse.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index 55ef890f7d..efcae3ef88 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -176,9 +176,11 @@ const DEFAULT_CONFIG = { */ msc4143_enabled: false, }, - matrix_rtc: { - transports: Array<{ type: string; [field: string]: string }>, - }, + matrix_rtc: undefined as + | undefined + | { + transports: Array<{ type: string; [field: string]: string }>; + }, oidc_providers: [], serve_server_wellknown: true, presence: { From ea6017a26cd8f4c4be4822f60d0580150c886826 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 14:14:35 +0000 Subject: [PATCH 072/113] Bump @element-hq/element-web-playwright-common to 2.2.1 --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index f626573bfc..a332ed04be 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "2.2.0", + "version": "2.2.1", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From de205f991190943a333ecfebd7270564cdcc5326 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 14:46:50 +0000 Subject: [PATCH 073/113] Add msc2697_enabled --- .../src/testcontainers/synapse.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index efcae3ef88..8980eb1254 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -175,6 +175,11 @@ const DEFAULT_CONFIG = { * MSC4143 Matrix RTC: Required for `matrix_rtc` configuration. */ msc4143_enabled: false, + /** + * MSC2697: Device dehydration. + * This is ON by default in Synapse. + */ + msc2697_enabled: true, }, matrix_rtc: undefined as | undefined From b048f5323cce89327110e14635da8a8167399e85 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 15:27:06 +0000 Subject: [PATCH 074/113] Switch to MSC3814 --- .../src/testcontainers/synapse.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index 8980eb1254..106483b864 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -176,10 +176,9 @@ const DEFAULT_CONFIG = { */ msc4143_enabled: false, /** - * MSC2697: Device dehydration. - * This is ON by default in Synapse. + * MSC3814: Device dehydration SSSS. */ - msc2697_enabled: true, + msc3814_enabled: false, }, matrix_rtc: undefined as | undefined From 184c8dc3b3bd2838ded33ab5a676fff78e479c12 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 15:32:36 +0000 Subject: [PATCH 075/113] Just set as Record --- .../src/testcontainers/synapse.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index 106483b864..bec34aa433 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -170,16 +170,7 @@ const DEFAULT_CONFIG = { room_name: string; }, allow_guest_access: false, - experimental_features: { - /** - * MSC4143 Matrix RTC: Required for `matrix_rtc` configuration. - */ - msc4143_enabled: false, - /** - * MSC3814: Device dehydration SSSS. - */ - msc3814_enabled: false, - }, + experimental_features: {} as Record, matrix_rtc: undefined as | undefined | { From db11815589fe2afefdd83f495010ad29d28adfc5 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 15:47:10 +0000 Subject: [PATCH 076/113] Bump @element-hq/element-web-playwright-common to 2.2.2 --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index a332ed04be..1ef94d2f9d 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "2.2.1", + "version": "2.2.2", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From a303e13060f10622cd46d1c68b5f5cab81f3c821 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 17:14:50 +0000 Subject: [PATCH 077/113] Change module resolution to check for correct imports. --- packages/element-web-playwright-common/tsconfig.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/element-web-playwright-common/tsconfig.json b/packages/element-web-playwright-common/tsconfig.json index 04bbd964f0..bbbd26cdb4 100644 --- a/packages/element-web-playwright-common/tsconfig.json +++ b/packages/element-web-playwright-common/tsconfig.json @@ -4,6 +4,8 @@ "compilerOptions": { "outDir": "lib", "declarationMap": true, + "module": "node16", + "moduleResolution": "node16", "allowImportingTsExtensions": false }, "include": ["src"] From 23565d2d36a0d1356dd0dec83b670bba32395abb Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 6 Jan 2026 17:15:04 +0000 Subject: [PATCH 078/113] Fix imports to import with .js --- packages/element-web-playwright-common/src/fixtures/axe.ts | 2 +- .../element-web-playwright-common/src/fixtures/index.ts | 2 +- .../src/testcontainers/HomeserverContainer.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/element-web-playwright-common/src/fixtures/axe.ts b/packages/element-web-playwright-common/src/fixtures/axe.ts index 2c0c97af45..b7c03763cc 100644 --- a/packages/element-web-playwright-common/src/fixtures/axe.ts +++ b/packages/element-web-playwright-common/src/fixtures/axe.ts @@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details. import AxeBuilder from "@axe-core/playwright"; -import { test as base } from "./user"; +import { test as base } from "./user.js"; // We want to avoid using `mergeTests` because it drops useful type information about the fixtures. Instead, we extend // the definition of `test` from `user.ts`, so that there is a linear hierarchy. diff --git a/packages/element-web-playwright-common/src/fixtures/index.ts b/packages/element-web-playwright-common/src/fixtures/index.ts index 58aebfa20e..de7bce2f29 100644 --- a/packages/element-web-playwright-common/src/fixtures/index.ts +++ b/packages/element-web-playwright-common/src/fixtures/index.ts @@ -9,4 +9,4 @@ export { type Services, type WorkerOptions } from "./services.js"; // We avoid using `mergeTests` because it drops useful type information about the fixtures. // `axe` is the top of our stack of extensions (it extends `user`, etc), so it's the one we want to use. -export { test } from "./axe"; +export { test } from "./axe.js"; diff --git a/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts b/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts index 57ceb653db..dcb5efa386 100644 --- a/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts +++ b/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts @@ -8,9 +8,9 @@ Please see LICENSE files in the repository root for full details. import { type AbstractStartedContainer, type GenericContainer } from "testcontainers"; import { type APIRequestContext, type TestInfo } from "@playwright/test"; -import { type StartedMatrixAuthenticationServiceContainer } from "./mas"; -import { type ClientServerApi, type Credentials } from "../utils/api"; -import { type StartedMailpitContainer } from "./mailpit"; +import { type StartedMatrixAuthenticationServiceContainer } from "./mas.js"; +import { type ClientServerApi, type Credentials } from "../utils/api.js"; +import { type StartedMailpitContainer } from "./mailpit.js"; export interface HomeserverInstance { readonly baseUrl: string; From 0561301d982761e3a473f4ddb4aa485948c017f2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 7 Jan 2026 14:02:43 +0000 Subject: [PATCH 079/113] Bump version to 2.2.3 in package.json --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 1ef94d2f9d..81e18a73f5 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "2.2.2", + "version": "2.2.3", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 48e262d8f1ff9f395ef3d8052fbecccdfe7e1b8b Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 16 Jan 2026 12:05:41 +0100 Subject: [PATCH 080/113] Avoid starting a homeserver when using just the axe fixture Since 4c928d28543b18eec14334e217fc24df8b598065 importing the axe fixture would cause your test to pull in the entire world of Synapse and Postgres services. For Compound Web tests, we want to be able to use axe in isolation without all the extra fixtures. Inverting the fixture hierarchy ought to do the trick. --- packages/element-web-playwright-common/src/fixtures/axe.ts | 7 +++---- .../element-web-playwright-common/src/fixtures/index.ts | 4 ++-- .../element-web-playwright-common/src/fixtures/services.ts | 4 +++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/element-web-playwright-common/src/fixtures/axe.ts b/packages/element-web-playwright-common/src/fixtures/axe.ts index b7c03763cc..f438b56d1f 100644 --- a/packages/element-web-playwright-common/src/fixtures/axe.ts +++ b/packages/element-web-playwright-common/src/fixtures/axe.ts @@ -6,12 +6,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ +import { test as base } from "@playwright/test"; import AxeBuilder from "@axe-core/playwright"; -import { test as base } from "./user.js"; - -// We want to avoid using `mergeTests` because it drops useful type information about the fixtures. Instead, we extend -// the definition of `test` from `user.ts`, so that there is a linear hierarchy. +// This fixture is useful for simple component library tests that won't want any extra services like a homeserver, so we +// explicitly avoid pulling anything more than playwright's base fixtures in. export const test = base.extend<{ /** * AxeBuilder instance for the current page diff --git a/packages/element-web-playwright-common/src/fixtures/index.ts b/packages/element-web-playwright-common/src/fixtures/index.ts index de7bce2f29..04dfb50189 100644 --- a/packages/element-web-playwright-common/src/fixtures/index.ts +++ b/packages/element-web-playwright-common/src/fixtures/index.ts @@ -8,5 +8,5 @@ Please see LICENSE files in the repository root for full details. export { type Services, type WorkerOptions } from "./services.js"; // We avoid using `mergeTests` because it drops useful type information about the fixtures. -// `axe` is the top of our stack of extensions (it extends `user`, etc), so it's the one we want to use. -export { test } from "./axe.js"; +// `user` is the top of our stack of extensions (it extends services, axe, etc), so it includes everything. +export { test } from "./user.js"; diff --git a/packages/element-web-playwright-common/src/fixtures/services.ts b/packages/element-web-playwright-common/src/fixtures/services.ts index 2c91bae1a0..d4256c6213 100644 --- a/packages/element-web-playwright-common/src/fixtures/services.ts +++ b/packages/element-web-playwright-common/src/fixtures/services.ts @@ -5,7 +5,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { test as base } from "@playwright/test"; import { type MailpitClient } from "mailpit-api"; import { Network, type StartedNetwork } from "testcontainers"; import { PostgreSqlContainer, type StartedPostgreSqlContainer } from "@testcontainers/postgresql"; @@ -20,6 +19,9 @@ import { type StartedMailpitContainer, } from "../testcontainers/index.js"; import { Logger } from "../utils/logger.js"; +// We want to avoid using `mergeTests` in index.ts because it drops useful type information about the fixtures. Instead, +// we add `axe` into our fixture suite by using its `test` as a base, so that there is a linear hierarchy. +import { test as base } from "./axe.js"; /** * Test-scoped fixtures available in the test From edfc66fa8116da51b0912d4bc856339dd3092142 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 16 Jan 2026 15:35:24 +0000 Subject: [PATCH 081/113] Bump version from 2.2.3 to 2.2.4 --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 81e18a73f5..b5a2b0a759 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "2.2.3", + "version": "2.2.4", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 9233c0c0dbd685d4384307484534350c3d5ae8df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 02:02:39 +0000 Subject: [PATCH 082/113] build(deps): bump lodash-es in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [lodash-es](https://github.com/lodash/lodash). Updates `lodash-es` from 4.17.22 to 4.17.23 - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/commits/4.17.23) --- updated-dependencies: - dependency-name: lodash-es dependency-version: 4.17.23 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index b5a2b0a759..eca12de208 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -32,7 +32,7 @@ "@axe-core/playwright": "^4.10.1", "@testcontainers/postgresql": "^11.0.0", "glob": "^13.0.0", - "lodash-es": "^4.17.21", + "lodash-es": "^4.17.23", "mailpit-api": "^1.2.0", "strip-ansi": "^7.1.0", "testcontainers": "^11.0.0", From 33fbb47d93718df860ced507754d992b70b4610e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 23 Jan 2026 16:39:23 +0000 Subject: [PATCH 083/113] Fix Playwright version checking Updated method to fetch Playwright version using yarn list. `yarn info` was just querying what the latest available package is --- .../element-web-playwright-common/playwright-screenshots.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index 409c5baa8a..686807387c 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -11,10 +11,8 @@ IMAGE_NAME="element-web-playwright-common" build_image() { echo "Building $IMAGE_NAME image in $SCRIPT_DIR" - # Fetch the playwright version - # .data.version is for yarn classic - # .children.Version is for yarn berry - PW_VERSION=$(yarn info --manifest --json @playwright/test | jq -r '.data.version // .children.Version') + # Check the playwright version + PW_VERSION=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name | split("@") | last') echo "with Playwright version $PW_VERSION" # Build image From fba0da2eaae4c872b50437cdc457b6b0d50cc66a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 23 Jan 2026 16:57:21 +0000 Subject: [PATCH 084/113] Bump playwright-common version to 2.2.5 --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index eca12de208..f830a1873b 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "2.2.4", + "version": "2.2.5", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From c3966aea82184d9750e0baddf3e07e02225ec893 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 23 Jan 2026 17:15:49 +0000 Subject: [PATCH 085/113] Fix playwright-screenshots utility for native dependencies Without this, a developer would need to both specify --with-node-modules && --entrypoint and provide a custom entrypoint to do the `yarn install`. --- packages/element-web-playwright-common/Dockerfile | 4 +++- .../element-web-playwright-common/docker-entrypoint.sh | 9 +++++++++ .../playwright-screenshots.sh | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100755 packages/element-web-playwright-common/docker-entrypoint.sh diff --git a/packages/element-web-playwright-common/Dockerfile b/packages/element-web-playwright-common/Dockerfile index c8bf12e503..3be889bcd8 100644 --- a/packages/element-web-playwright-common/Dockerfile +++ b/packages/element-web-playwright-common/Dockerfile @@ -6,4 +6,6 @@ WORKDIR /work # fonts-dejavu is needed for the same RTL rendering as on CI RUN apt-get update && apt-get -y install docker.io fonts-dejavu -ENTRYPOINT ["npx", "playwright", "test", "--update-snapshots", "--reporter", "line"] +COPY docker-entrypoint.sh /docker-entrypoint.sh + +ENTRYPOINT ["docker-entrypoint.sh"] diff --git a/packages/element-web-playwright-common/docker-entrypoint.sh b/packages/element-web-playwright-common/docker-entrypoint.sh new file mode 100755 index 0000000000..500fb27c68 --- /dev/null +++ b/packages/element-web-playwright-common/docker-entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e + +if [[ "$YARN_INSTALL" == "true" ]]; then + yarn install --frozen-lockfile +fi + +npx playwright test --update-snapshots --reporter line "$@" \ No newline at end of file diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index 686807387c..04455ae4da 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -59,7 +59,7 @@ while [[ $# -gt 0 ]]; do # It's a volume rather than a directory because otherwise things tend to start picking up # files from it in the native environment and break. --with-node-modules) - RUN_ARGS+=(--mount "type=volume,src=ew-docker-node-modules,dst=/work/node_modules,volume-nocopy") + RUN_ARGS+=(--mount "type=volume,src=ew-docker-node-modules,dst=/work/node_modules,volume-nocopy" -e YARN_INSTALL=true) shift ;; # Sets a different entrypoint (in which case the default arguments to the script will be ignored) From ccadd73953f6657c9620db4ea1665027c4107965 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Mon, 2 Feb 2026 11:27:00 +0000 Subject: [PATCH 086/113] playwright-screenshots: fix incompatibility with podman (#189) Podman doesn't recognise the `volume-nocopy` option on mounts. It doesn't seem to be necessary so let's omit it. --- .../playwright-screenshots.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index 04455ae4da..36550c677a 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -8,6 +8,8 @@ SCRIPT_DIR=$(dirname "$SCRIPT_PATH") IMAGE_NAME="element-web-playwright-common" +if docker --version | grep -q podman; then docker_is_podman=1; fi + build_image() { echo "Building $IMAGE_NAME image in $SCRIPT_DIR" @@ -59,7 +61,10 @@ while [[ $# -gt 0 ]]; do # It's a volume rather than a directory because otherwise things tend to start picking up # files from it in the native environment and break. --with-node-modules) - RUN_ARGS+=(--mount "type=volume,src=ew-docker-node-modules,dst=/work/node_modules,volume-nocopy" -e YARN_INSTALL=true) + mount_param="type=volume,src=ew-docker-node-modules,dst=/work/node_modules" + # podman doesn't support `volume-nocopy` + if [ -z "$docker_is_podman" ]; then mount_param+=",volume-nocopy"; fi + RUN_ARGS+=(--mount "${mount_param}" -e YARN_INSTALL=true) shift ;; # Sets a different entrypoint (in which case the default arguments to the script will be ignored) @@ -84,7 +89,7 @@ popd > /dev/null for LINK in $SYMLINKS; do TARGET=$(readlink -f "node_modules/$LINK") || true if [ -d "$TARGET" ]; then - if docker --version | grep -q podman; then + if [ -n "$docker_is_podman" ]; then echo -e "\033[31m" >&2 cat <<'EOF' >&2 WARNING: `node_modules` contains symlinks, and the support for this in From cb27334d3d2312d14c4932a8823d7a8ee9815014 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:18:35 +0000 Subject: [PATCH 087/113] Fix entrypoint for element-web-playwright-common docker image (#190) --- packages/element-web-playwright-common/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/Dockerfile b/packages/element-web-playwright-common/Dockerfile index 3be889bcd8..5ae83e4bcf 100644 --- a/packages/element-web-playwright-common/Dockerfile +++ b/packages/element-web-playwright-common/Dockerfile @@ -8,4 +8,4 @@ RUN apt-get update && apt-get -y install docker.io fonts-dejavu COPY docker-entrypoint.sh /docker-entrypoint.sh -ENTRYPOINT ["docker-entrypoint.sh"] +ENTRYPOINT ["/docker-entrypoint.sh"] From 5f7b56a89f175c0d51e21289b8ebc0ecd34cf996 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 9 Feb 2026 09:34:54 +0000 Subject: [PATCH 088/113] Add support for pnpm in playwright-common screenshot script --- .../playwright-screenshots.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index 36550c677a..5a3a014e2f 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -14,7 +14,12 @@ build_image() { echo "Building $IMAGE_NAME image in $SCRIPT_DIR" # Check the playwright version - PW_VERSION=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name | split("@") | last') + PM=$(cat package.json | jq -r '.packageManager') + if [[ $PM == "pnpm@"* ]]; then + PW_VERSION=$(pnpm list @playwright/test --depth=0 --json | jq -r '.[].devDependencies["@playwright/test"].version') + else + PW_VERSION=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name | split("@") | last') + fi echo "with Playwright version $PW_VERSION" # Build image From 31d5d60b4c05bb22970b07b03a885e998b4578a4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 10 Feb 2026 10:31:06 +0000 Subject: [PATCH 089/113] Bump playwright-common version to 2.2.6 --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index f830a1873b..f9bb08c13a 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "2.2.5", + "version": "2.2.6", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 9c1f3ba9286d503aba85f3ed3b675403aa22795f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 10:32:16 +0000 Subject: [PATCH 090/113] build(deps): bump axios in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [axios](https://github.com/axios/axios). Updates `axios` from 1.13.2 to 1.13.5 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.13.2...v1.13.5) --- updated-dependencies: - dependency-name: axios dependency-version: 1.13.5 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index f9bb08c13a..ed6481c824 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -31,7 +31,7 @@ "dependencies": { "@axe-core/playwright": "^4.10.1", "@testcontainers/postgresql": "^11.0.0", - "glob": "^13.0.0", + "glob": "^13.0.1", "lodash-es": "^4.17.23", "mailpit-api": "^1.2.0", "strip-ansi": "^7.1.0", From 82f185700e63063e1875a816546a2ebf2893c327 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 12 Feb 2026 14:54:33 +0000 Subject: [PATCH 091/113] Add --no-link-modules to playwright-screenshots.sh script --- .../playwright-screenshots.sh | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/element-web-playwright-common/playwright-screenshots.sh index 5a3a014e2f..1fa0f537d9 100755 --- a/packages/element-web-playwright-common/playwright-screenshots.sh +++ b/packages/element-web-playwright-common/playwright-screenshots.sh @@ -56,6 +56,7 @@ RUN_ARGS=( ) DEFAULT_ARGS=(--grep @screenshot) +LINK_MODULES=true # Some arguments to customise behaviour so the same script / image can be # re-used for other screenshot generation. @@ -72,6 +73,11 @@ while [[ $# -gt 0 ]]; do RUN_ARGS+=(--mount "${mount_param}" -e YARN_INSTALL=true) shift ;; + # Disables the automatic detection & linking of node_modules which can clash with developer tooling e.g. pnpm-link + --no-link-modules) + LINK_MODULES=false + shift + ;; # Sets a different entrypoint (in which case the default arguments to the script will be ignored) --entrypoint) shift @@ -87,16 +93,17 @@ done build_image -# Ensure we pass all symlinked node_modules to the container -pushd node_modules > /dev/null -SYMLINKS=$(find . -maxdepth 2 -type l -not -path "./.bin/*") -popd > /dev/null -for LINK in $SYMLINKS; do - TARGET=$(readlink -f "node_modules/$LINK") || true - if [ -d "$TARGET" ]; then - if [ -n "$docker_is_podman" ]; then - echo -e "\033[31m" >&2 - cat <<'EOF' >&2 +if [[ $LINK_MODULES == true ]]; then + # Ensure we pass all symlinked node_modules to the container + pushd node_modules > /dev/null + SYMLINKS=$(find . -maxdepth 2 -type l -not -path "./.bin/*") + popd > /dev/null + for LINK in $SYMLINKS; do + TARGET=$(readlink -f "node_modules/$LINK") || true + if [ -d "$TARGET" ]; then + if [ -n "$docker_is_podman" ]; then + echo -e "\033[31m" >&2 + cat <<'EOF' >&2 WARNING: `node_modules` contains symlinks, and the support for this in `playwright-screenshots.sh` is broken under podman due to https://github.com/containers/podman/issues/25947. @@ -104,12 +111,13 @@ https://github.com/containers/podman/issues/25947. If you get errors such as 'Error: crun: creating ``', then retry this having `yarn unlink`ed the relevant node modules. EOF - echo -e "\033[0m" >&2 + echo -e "\033[0m" >&2 + fi + echo "mounting linked package ${LINK:2} in container" + RUN_ARGS+=( "-v" "$TARGET:/work/node_modules/${LINK:2}" ) fi - echo "mounting linked package ${LINK:2} in container" - RUN_ARGS+=( "-v" "$TARGET:/work/node_modules/${LINK:2}" ) - fi -done + done +fi # Our Playwright fixtures use Testcontainers [1], which uses a docker image # called Ryuk [2], which will clean up any dangling containers/networks/etc From f243b3dd5447722230e9bf6d965626b3ac28df08 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 12 Feb 2026 14:54:59 +0000 Subject: [PATCH 092/113] Enable corepack in playwright screenshots docker image --- packages/element-web-playwright-common/Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/element-web-playwright-common/Dockerfile b/packages/element-web-playwright-common/Dockerfile index 5ae83e4bcf..d718dc5c25 100644 --- a/packages/element-web-playwright-common/Dockerfile +++ b/packages/element-web-playwright-common/Dockerfile @@ -6,6 +6,10 @@ WORKDIR /work # fonts-dejavu is needed for the same RTL rendering as on CI RUN apt-get update && apt-get -y install docker.io fonts-dejavu +# Set up corepack +RUN corepack enable +ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 + COPY docker-entrypoint.sh /docker-entrypoint.sh ENTRYPOINT ["/docker-entrypoint.sh"] From 2c73e09907293fefa9b74e6ad2efbf284f4e0df0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 12 Feb 2026 14:55:12 +0000 Subject: [PATCH 093/113] Expose PLAYWRIGHT_COMMON_DOCKER envvar in playwright screenshots docker image --- packages/element-web-playwright-common/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/element-web-playwright-common/Dockerfile b/packages/element-web-playwright-common/Dockerfile index d718dc5c25..ef6bd176ed 100644 --- a/packages/element-web-playwright-common/Dockerfile +++ b/packages/element-web-playwright-common/Dockerfile @@ -10,6 +10,9 @@ RUN apt-get update && apt-get -y install docker.io fonts-dejavu RUN corepack enable ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +# Add environment variable so consumers can skip developer-centric scripts +ENV PLAYWRIGHT_COMMON_DOCKER=1 + COPY docker-entrypoint.sh /docker-entrypoint.sh ENTRYPOINT ["/docker-entrypoint.sh"] From a604153ece4faf0c2050e006c7fd65532e593fa2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 12 Feb 2026 14:55:30 +0000 Subject: [PATCH 094/113] Bump playwright-common version --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index ed6481c824..e6be5b6ea7 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "2.2.6", + "version": "2.2.7", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From dd71370223cf7d526aae2f9da2549eeb41451e80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 14 Feb 2026 12:59:13 +0000 Subject: [PATCH 095/113] build(deps): bump qs in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [qs](https://github.com/ljharb/qs). Updates `qs` from 6.14.1 to 6.14.2 - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2) --- updated-dependencies: - dependency-name: qs dependency-version: 6.14.2 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index e6be5b6ea7..a9881fed6a 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -31,7 +31,7 @@ "dependencies": { "@axe-core/playwright": "^4.10.1", "@testcontainers/postgresql": "^11.0.0", - "glob": "^13.0.1", + "glob": "^13.0.2", "lodash-es": "^4.17.23", "mailpit-api": "^1.2.0", "strip-ansi": "^7.1.0", From a9e76dbb8a2e3f857ba5155b757cafde8f3650f2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 19 Feb 2026 16:04:42 +0000 Subject: [PATCH 096/113] Update stale screenshots playwright reporter At some point it seems that playwright changed how reporters are passed flakes. https://github.com/microsoft/playwright/pull/39145 needs to land to fully fix the reporter though --- .../src/stale-screenshot-reporter.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts index a6ac4f5967..39c9a53fa5 100644 --- a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts +++ b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts @@ -25,7 +25,7 @@ export const ANNOTATION = "_screenshot"; class StaleScreenshotReporter implements Reporter { private readonly snapshotRoots = new Set(); private readonly screenshots = new Set(); - private failing = false; + private readonly failing = new Set(); private success = true; public onBegin(config: FullConfig): void { @@ -36,8 +36,11 @@ class StaleScreenshotReporter implements Reporter { public onTestEnd(test: TestCase): void { if (!test.ok()) { - this.failing = true; + this.failing.add(test.id); + return; } + this.failing.delete(test.id); // delete if passed on re-run + for (const annotation of test.annotations) { if (annotation.type === ANNOTATION && annotation.description) { this.screenshots.add(annotation.description); @@ -54,7 +57,10 @@ class StaleScreenshotReporter implements Reporter { } public async onExit(): Promise { - if (this.failing) return; + if (this.failing.size) { + console.error(`${this.failing.size} tests failed, skipping stale screenshot reporter.`); + } + if (!this.snapshotRoots.size) { this.error("No snapshot directories found, did you set the snapshotDir in your Playwright config?", ""); return; From e30d82550347c0ffda869f83b5f94101f08f10e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Feb 2026 17:45:03 +0000 Subject: [PATCH 097/113] build(deps): bump ajv in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [ajv](https://github.com/ajv-validator/ajv). Updates `ajv` from 6.12.6 to 6.14.0 - [Release notes](https://github.com/ajv-validator/ajv/releases) - [Commits](https://github.com/ajv-validator/ajv/compare/v6.12.6...v6.14.0) --- updated-dependencies: - dependency-name: ajv dependency-version: 6.14.0 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index a9881fed6a..7396026e6a 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -31,7 +31,7 @@ "dependencies": { "@axe-core/playwright": "^4.10.1", "@testcontainers/postgresql": "^11.0.0", - "glob": "^13.0.2", + "glob": "^13.0.3", "lodash-es": "^4.17.23", "mailpit-api": "^1.2.0", "strip-ansi": "^7.1.0", From a0d4df56c0691eb5030fa4412bb738885aac7b6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 22 Feb 2026 14:06:56 +0000 Subject: [PATCH 098/113] build(deps): bump bn.js in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [bn.js](https://github.com/indutny/bn.js). Updates `bn.js` from 4.12.1 to 4.12.3 - [Release notes](https://github.com/indutny/bn.js/releases) - [Changelog](https://github.com/indutny/bn.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/indutny/bn.js/commits/v4.12.3) --- updated-dependencies: - dependency-name: bn.js dependency-version: 4.12.3 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 7396026e6a..75c5278489 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -31,7 +31,7 @@ "dependencies": { "@axe-core/playwright": "^4.10.1", "@testcontainers/postgresql": "^11.0.0", - "glob": "^13.0.3", + "glob": "^13.0.4", "lodash-es": "^4.17.23", "mailpit-api": "^1.2.0", "strip-ansi": "^7.1.0", From 2b688528e360b0a318a06032711eb7fb9e0d1723 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 16:42:02 +0000 Subject: [PATCH 099/113] build(deps): bump rollup in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [rollup](https://github.com/rollup/rollup). Updates `rollup` from 4.52.0 to 4.59.0 - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v4.52.0...v4.59.0) --- updated-dependencies: - dependency-name: rollup dependency-version: 4.59.0 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 75c5278489..a31149a2c4 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -31,7 +31,7 @@ "dependencies": { "@axe-core/playwright": "^4.10.1", "@testcontainers/postgresql": "^11.0.0", - "glob": "^13.0.4", + "glob": "^13.0.5", "lodash-es": "^4.17.23", "mailpit-api": "^1.2.0", "strip-ansi": "^7.1.0", From fd4bb50a14c38592b58a05394fb3918281813bca Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 3 Mar 2026 18:25:22 +0000 Subject: [PATCH 100/113] Tests galore --- .../src/testcontainers/mas.ts | 3 +++ .../src/testcontainers/synapse.ts | 25 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts index 294afb7343..430875797b 100644 --- a/packages/element-web-playwright-common/src/testcontainers/mas.ts +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -156,6 +156,7 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { super(image); const initialConfig = deepCopy(DEFAULT_CONFIG); + initialConfig.database.host = db.getHostname(); initialConfig.database.username = db.getUsername(); initialConfig.database.password = db.getPassword(); @@ -205,6 +206,7 @@ export class MatrixAuthenticationServiceContainer extends GenericContainer { await super.start(), `http://localhost:${port}`, this.args, + this.config.matrix.secret, ); } } @@ -219,6 +221,7 @@ export class StartedMatrixAuthenticationServiceContainer extends AbstractStarted container: StartedTestContainer, public readonly baseUrl: string, private readonly args: string[], + public readonly sharedSecret: string, ) { super(container); } diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/element-web-playwright-common/src/testcontainers/synapse.ts index bec34aa433..65d92c0ab9 100644 --- a/packages/element-web-playwright-common/src/testcontainers/synapse.ts +++ b/packages/element-web-playwright-common/src/testcontainers/synapse.ts @@ -184,6 +184,14 @@ const DEFAULT_CONFIG = { }, room_list_publication_rules: [{ action: "allow" }], modules: [] as Array<{ module: string; config?: Record }>, + matrix_authentication_service: undefined as + | undefined + | { + enabled?: boolean; + endpoint?: string; + secret?: string | null; + secret_path?: string | null; + }, }; /** @@ -278,7 +286,22 @@ export class SynapseContainer extends GenericContainer implements HomeserverCont } public withMatrixAuthenticationService(mas?: StartedMatrixAuthenticationServiceContainer): this { - this.mas = mas; + if (mas) { + this.mas = mas; + this.withConfig({ + matrix_authentication_service: { + enabled: true, + endpoint: `http://${mas.getHostname()}:8080/`, + secret: mas.sharedSecret, + }, + // Must be disabled when using MAS. + password_config: { + enabled: false, + }, + // Must be disabled when using MAS. + enable_registration: false, + }); + } return this; } From 6d71191af973325b4889c1d62668a1e171da06d7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 3 Mar 2026 18:27:32 +0000 Subject: [PATCH 101/113] Fix axe imports --- packages/element-web-playwright-common/src/expect/axe.ts | 2 +- packages/element-web-playwright-common/src/fixtures/axe.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/element-web-playwright-common/src/expect/axe.ts b/packages/element-web-playwright-common/src/expect/axe.ts index 7a751f4fbe..ab798d9a96 100644 --- a/packages/element-web-playwright-common/src/expect/axe.ts +++ b/packages/element-web-playwright-common/src/expect/axe.ts @@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details. import { test, expect as baseExpect, type ExpectMatcherState, type MatcherReturnType } from "@playwright/test"; -import type AxeBuilder from "@axe-core/playwright"; +import type { AxeBuilder } from "@axe-core/playwright"; export type Expectations = { /** diff --git a/packages/element-web-playwright-common/src/fixtures/axe.ts b/packages/element-web-playwright-common/src/fixtures/axe.ts index f438b56d1f..6f99b489c0 100644 --- a/packages/element-web-playwright-common/src/fixtures/axe.ts +++ b/packages/element-web-playwright-common/src/fixtures/axe.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. */ import { test as base } from "@playwright/test"; -import AxeBuilder from "@axe-core/playwright"; +import { AxeBuilder } from "@axe-core/playwright"; // This fixture is useful for simple component library tests that won't want any extra services like a homeserver, so we // explicitly avoid pulling anything more than playwright's base fixtures in. From 225fe7c40669205f8fcca2457fcd409facd93967 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 4 Mar 2026 10:32:58 +0000 Subject: [PATCH 102/113] Tidy up --- .../src/fixtures/services.ts | 25 ++---------- .../src/testcontainers/mas.ts | 18 +++++++++ .../src/testcontainers/postgres.ts | 40 +++++++++++++++++++ 3 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 packages/element-web-playwright-common/src/testcontainers/postgres.ts diff --git a/packages/element-web-playwright-common/src/fixtures/services.ts b/packages/element-web-playwright-common/src/fixtures/services.ts index d4256c6213..de5e9af680 100644 --- a/packages/element-web-playwright-common/src/fixtures/services.ts +++ b/packages/element-web-playwright-common/src/fixtures/services.ts @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. import { type MailpitClient } from "mailpit-api"; import { Network, type StartedNetwork } from "testcontainers"; -import { PostgreSqlContainer, type StartedPostgreSqlContainer } from "@testcontainers/postgresql"; +import { type StartedPostgreSqlContainer } from "@testcontainers/postgresql"; import { type SynapseConfig, @@ -22,6 +22,7 @@ import { Logger } from "../utils/logger.js"; // We want to avoid using `mergeTests` in index.ts because it drops useful type information about the fixtures. Instead, // we add `axe` into our fixture suite by using its `test` as a base, so that there is a linear hierarchy. import { test as base } from "./axe.js"; +import { makePostgres } from "../testcontainers/postgres.js"; /** * Test-scoped fixtures available in the test @@ -101,27 +102,7 @@ export const test = base.extend({ ], postgres: [ async ({ logger, network }, use) => { - const container = await new PostgreSqlContainer("postgres:13.3-alpine") - .withNetwork(network) - .withNetworkAliases("postgres") - .withLogConsumer(logger.getConsumer("postgres")) - .withTmpFs({ - "/dev/shm/pgdata/data": "", - }) - .withEnvironment({ - PG_DATA: "/dev/shm/pgdata/data", - }) - .withCommand([ - "-c", - "shared_buffers=128MB", - "-c", - `fsync=off`, - "-c", - `synchronous_commit=off`, - "-c", - "full_page_writes=off", - ]) - .start(); + const container = await makePostgres(network, logger); await use(container); await container.stop(); }, diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/element-web-playwright-common/src/testcontainers/mas.ts index 430875797b..8e64db59f8 100644 --- a/packages/element-web-playwright-common/src/testcontainers/mas.ts +++ b/packages/element-web-playwright-common/src/testcontainers/mas.ts @@ -11,6 +11,7 @@ import { type StartedTestContainer, Wait, type ExecResult, + type StartedNetwork, } from "testcontainers"; import { type StartedPostgreSqlContainer } from "@testcontainers/postgresql"; import * as YAML from "yaml"; @@ -23,6 +24,7 @@ import { type Credentials } from "../utils/api.js"; // curl -sL https://element-hq.github.io/matrix-authentication-service/config.schema.json \ // | npx json-schema-to-typescript -o packages/element-web-playwright-common/src/testconainers/mas-config.ts import type { RootConfig as MasConfig } from "./mas-config.js"; +import type { Logger } from "../utils/logger.js"; export { type MasConfig }; @@ -349,3 +351,19 @@ export class StartedMatrixAuthenticationServiceContainer extends AbstractStarted await this.manage("add-email", username, address); } } + +export async function makeMas( + postgres: StartedPostgreSqlContainer, + network: StartedNetwork, + logger: Logger, + config: Partial, + name = "mas", +): Promise { + const container = await new MatrixAuthenticationServiceContainer(postgres) + .withNetwork(network) + .withNetworkAliases(name) + .withLogConsumer(logger.getConsumer(name)) + .withConfig(config) + .start(); + return container; +} diff --git a/packages/element-web-playwright-common/src/testcontainers/postgres.ts b/packages/element-web-playwright-common/src/testcontainers/postgres.ts new file mode 100644 index 0000000000..0dd0eab27d --- /dev/null +++ b/packages/element-web-playwright-common/src/testcontainers/postgres.ts @@ -0,0 +1,40 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { PostgreSqlContainer, type StartedPostgreSqlContainer } from "@testcontainers/postgresql"; +import { type StartedNetwork } from "testcontainers"; + +import { type Logger } from "../utils/logger.js"; + +export async function makePostgres( + network: StartedNetwork, + logger: Logger, + name = "postgres", +): Promise { + const container = await new PostgreSqlContainer("postgres:13.3-alpine") + .withNetwork(network) + .withNetworkAliases(name) + .withLogConsumer(logger.getConsumer(name)) + .withTmpFs({ + "/dev/shm/pgdata/data": "", + }) + .withEnvironment({ + PG_DATA: "/dev/shm/pgdata/data", + }) + .withCommand([ + "-c", + "shared_buffers=128MB", + "-c", + `fsync=off`, + "-c", + `synchronous_commit=off`, + "-c", + "full_page_writes=off", + ]) + .start(); + return container; +} From 0079270fa5644e95a1bab68a98719d31a82d4123 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 30 Mar 2026 18:16:44 +0530 Subject: [PATCH 103/113] Lock width when running most playwright tests --- .../src/index.ts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/src/index.ts b/packages/element-web-playwright-common/src/index.ts index 518def9eaa..afc92244e4 100644 --- a/packages/element-web-playwright-common/src/index.ts +++ b/packages/element-web-playwright-common/src/index.ts @@ -66,6 +66,16 @@ export interface TestFixtures { labsFlags: string[]; disablePresence: boolean; + /** + * Whether the left panel should have its width fixed. + * This is done because the library that we use for rendering collapsible + * panels uses math to calculate the width which can sometimes leads to +/-1px + * difference. While this does not matter to the user, it can lead to screenshot + * tests failing. + * Defaults to true, should be set to false via {@link base.use} when you want to test the collapse + * behaviour. + */ + lockLeftPanelWidth: boolean; } export const test = base.extend({ @@ -73,8 +83,18 @@ export const test = base.extend({ config: async ({}, use) => use({}), labsFlags: async ({}, use) => use([]), disablePresence: async ({}, use) => use(false), - page: async ({ homeserver, context, page, config, labsFlags, disablePresence }, use) => { + lockLeftPanelWidth: true, + page: async ({ homeserver, context, page, config, labsFlags, disablePresence, lockLeftPanelWidth }, use) => { await routeConfigJson(context, homeserver.baseUrl, config, labsFlags, disablePresence); + if (lockLeftPanelWidth) { + await page.addStyleTag({ + content: ` + #left-panel { + flex: 0 0 369.6875px !important; + } + `, + }); + } await use(page); }, }); From 72de8745e939434afdcf86a4c66d832a2e075d6b Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 30 Mar 2026 19:56:58 +0530 Subject: [PATCH 104/113] Bump package version --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index a31149a2c4..09480c689a 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "2.2.7", + "version": "2.3.0", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 1107ddf69577a716b21370cd2df6cc824a48cc76 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 7 Apr 2026 15:39:22 +0100 Subject: [PATCH 105/113] Bump version to 2.4.0 in playwright-common --- packages/element-web-playwright-common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/element-web-playwright-common/package.json b/packages/element-web-playwright-common/package.json index 09480c689a..a1bdc3391e 100644 --- a/packages/element-web-playwright-common/package.json +++ b/packages/element-web-playwright-common/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-playwright-common", "type": "module", - "version": "2.3.0", + "version": "2.4.0", "license": "SEE LICENSE IN README.md", "repository": { "type": "git", From 8cae8df17f146a7c928e5bf8f2ad91c120efd356 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 8 Apr 2026 11:43:50 +0100 Subject: [PATCH 106/113] Actually skip stale screenshot reporter on errors --- .../src/stale-screenshot-reporter.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts index 39c9a53fa5..2d7b1ec095 100644 --- a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts +++ b/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts @@ -56,11 +56,7 @@ class StaleScreenshotReporter implements Reporter { this.success = false; } - public async onExit(): Promise { - if (this.failing.size) { - console.error(`${this.failing.size} tests failed, skipping stale screenshot reporter.`); - } - + private async checkStaleScreenshots(): Promise { if (!this.snapshotRoots.size) { this.error("No snapshot directories found, did you set the snapshotDir in your Playwright config?", ""); return; @@ -90,6 +86,14 @@ class StaleScreenshotReporter implements Reporter { this.error("Stale screenshot file", screenshot); } } + } + + public async onExit(): Promise { + if (this.failing.size) { + this.error(`${this.failing.size} tests failed, skipping stale screenshot reporter.`, ""); + } else { + await this.checkStaleScreenshots(); + } if (!this.success) { process.exit(1); From 16f17d9127d7354c3b4bf345c30f0b44f920d399 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 9 Apr 2026 14:07:24 +0100 Subject: [PATCH 107/113] Generalise npm publishing workflow to work for more than just shared-components --- ...component-publish.yaml => npm-publish.yaml} | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) rename .github/workflows/{shared-component-publish.yaml => npm-publish.yaml} (69%) diff --git a/.github/workflows/shared-component-publish.yaml b/.github/workflows/npm-publish.yaml similarity index 69% rename from .github/workflows/shared-component-publish.yaml rename to .github/workflows/npm-publish.yaml index c728c303d5..708d233d26 100644 --- a/.github/workflows/shared-component-publish.yaml +++ b/.github/workflows/npm-publish.yaml @@ -1,6 +1,15 @@ -name: Publish shared component npm package +name: Publish npm package +run-name: Publish ${{ inputs.package }} on: - workflow_dispatch: {} + workflow_dispatch: + inputs: + package: + description: Which package to release + required: true + type: choice + options: + - playwright-common + - shared-components concurrency: release jobs: @@ -29,10 +38,9 @@ jobs: - name: Update npm run: npm install -g npm@latest - # Need to setup element web too as it needs the translations - - name: 🛠️ Setup EW + - name: 🛠️ Install dependencies run: pnpm install --frozen-lockfile - name: 🚀 Publish to npm - working-directory: packages/shared-components + working-directory: packages/${{ inputs.package }} run: npm publish --access public --provenance From 04dff2d2b634225db9272cd26262a6dba8724d77 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 9 Apr 2026 14:12:28 +0100 Subject: [PATCH 108/113] Update doc --- packages/shared-components/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared-components/README.md b/packages/shared-components/README.md index 8af1621ff2..138a458197 100644 --- a/packages/shared-components/README.md +++ b/packages/shared-components/README.md @@ -365,4 +365,4 @@ pnpm i18n Two steps are required to publish a new version of this package: 1. Bump the version in `package.json` following semver rules and open a PR. -2. Once merged run the [github workflow](https://github.com/element-hq/element-web/actions/workflows/shared-component-publish.yaml) +2. Once merged run the [github workflow](https://github.com/element-hq/element-web/actions/workflows/npm-publish.yaml) From 5e80431399de643fcbe448abe38d6cddaf4a4cb0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 9 Apr 2026 14:35:32 +0100 Subject: [PATCH 109/113] Shorten path for playwright-commmon --- .../Dockerfile | 0 .../README.md | 0 .../docker-entrypoint.sh | 0 .../package.json | 0 .../playwright-screenshots.sh | 0 .../src/@types/playwright-core.d.ts | 0 .../src/expect/axe.ts | 0 .../src/expect/index.ts | 0 .../src/expect/screenshot.ts | 0 .../src/fixtures/axe.ts | 0 .../src/fixtures/index.ts | 0 .../src/fixtures/services.ts | 0 .../src/fixtures/user.ts | 0 .../src/index.ts | 0 .../src/stale-screenshot-reporter.ts | 0 .../src/testcontainers/HomeserverContainer.ts | 0 .../src/testcontainers/index.ts | 0 .../src/testcontainers/mailpit.ts | 0 .../src/testcontainers/mas-config.ts | 0 .../src/testcontainers/mas.ts | 0 .../src/testcontainers/postgres.ts | 0 .../src/testcontainers/synapse.ts | 0 .../src/utils/api.ts | 0 .../src/utils/config_json.ts | 0 .../src/utils/context.ts | 0 .../src/utils/logger.ts | 0 .../src/utils/object.ts | 0 .../src/utils/port.ts | 0 .../src/utils/rand.ts | 0 .../tsconfig.json | 0 30 files changed, 0 insertions(+), 0 deletions(-) rename packages/{element-web-playwright-common => playwright-common}/Dockerfile (100%) rename packages/{element-web-playwright-common => playwright-common}/README.md (100%) rename packages/{element-web-playwright-common => playwright-common}/docker-entrypoint.sh (100%) rename packages/{element-web-playwright-common => playwright-common}/package.json (100%) rename packages/{element-web-playwright-common => playwright-common}/playwright-screenshots.sh (100%) rename packages/{element-web-playwright-common => playwright-common}/src/@types/playwright-core.d.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/expect/axe.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/expect/index.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/expect/screenshot.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/fixtures/axe.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/fixtures/index.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/fixtures/services.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/fixtures/user.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/index.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/stale-screenshot-reporter.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/testcontainers/HomeserverContainer.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/testcontainers/index.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/testcontainers/mailpit.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/testcontainers/mas-config.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/testcontainers/mas.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/testcontainers/postgres.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/testcontainers/synapse.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/utils/api.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/utils/config_json.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/utils/context.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/utils/logger.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/utils/object.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/utils/port.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/src/utils/rand.ts (100%) rename packages/{element-web-playwright-common => playwright-common}/tsconfig.json (100%) diff --git a/packages/element-web-playwright-common/Dockerfile b/packages/playwright-common/Dockerfile similarity index 100% rename from packages/element-web-playwright-common/Dockerfile rename to packages/playwright-common/Dockerfile diff --git a/packages/element-web-playwright-common/README.md b/packages/playwright-common/README.md similarity index 100% rename from packages/element-web-playwright-common/README.md rename to packages/playwright-common/README.md diff --git a/packages/element-web-playwright-common/docker-entrypoint.sh b/packages/playwright-common/docker-entrypoint.sh similarity index 100% rename from packages/element-web-playwright-common/docker-entrypoint.sh rename to packages/playwright-common/docker-entrypoint.sh diff --git a/packages/element-web-playwright-common/package.json b/packages/playwright-common/package.json similarity index 100% rename from packages/element-web-playwright-common/package.json rename to packages/playwright-common/package.json diff --git a/packages/element-web-playwright-common/playwright-screenshots.sh b/packages/playwright-common/playwright-screenshots.sh similarity index 100% rename from packages/element-web-playwright-common/playwright-screenshots.sh rename to packages/playwright-common/playwright-screenshots.sh diff --git a/packages/element-web-playwright-common/src/@types/playwright-core.d.ts b/packages/playwright-common/src/@types/playwright-core.d.ts similarity index 100% rename from packages/element-web-playwright-common/src/@types/playwright-core.d.ts rename to packages/playwright-common/src/@types/playwright-core.d.ts diff --git a/packages/element-web-playwright-common/src/expect/axe.ts b/packages/playwright-common/src/expect/axe.ts similarity index 100% rename from packages/element-web-playwright-common/src/expect/axe.ts rename to packages/playwright-common/src/expect/axe.ts diff --git a/packages/element-web-playwright-common/src/expect/index.ts b/packages/playwright-common/src/expect/index.ts similarity index 100% rename from packages/element-web-playwright-common/src/expect/index.ts rename to packages/playwright-common/src/expect/index.ts diff --git a/packages/element-web-playwright-common/src/expect/screenshot.ts b/packages/playwright-common/src/expect/screenshot.ts similarity index 100% rename from packages/element-web-playwright-common/src/expect/screenshot.ts rename to packages/playwright-common/src/expect/screenshot.ts diff --git a/packages/element-web-playwright-common/src/fixtures/axe.ts b/packages/playwright-common/src/fixtures/axe.ts similarity index 100% rename from packages/element-web-playwright-common/src/fixtures/axe.ts rename to packages/playwright-common/src/fixtures/axe.ts diff --git a/packages/element-web-playwright-common/src/fixtures/index.ts b/packages/playwright-common/src/fixtures/index.ts similarity index 100% rename from packages/element-web-playwright-common/src/fixtures/index.ts rename to packages/playwright-common/src/fixtures/index.ts diff --git a/packages/element-web-playwright-common/src/fixtures/services.ts b/packages/playwright-common/src/fixtures/services.ts similarity index 100% rename from packages/element-web-playwright-common/src/fixtures/services.ts rename to packages/playwright-common/src/fixtures/services.ts diff --git a/packages/element-web-playwright-common/src/fixtures/user.ts b/packages/playwright-common/src/fixtures/user.ts similarity index 100% rename from packages/element-web-playwright-common/src/fixtures/user.ts rename to packages/playwright-common/src/fixtures/user.ts diff --git a/packages/element-web-playwright-common/src/index.ts b/packages/playwright-common/src/index.ts similarity index 100% rename from packages/element-web-playwright-common/src/index.ts rename to packages/playwright-common/src/index.ts diff --git a/packages/element-web-playwright-common/src/stale-screenshot-reporter.ts b/packages/playwright-common/src/stale-screenshot-reporter.ts similarity index 100% rename from packages/element-web-playwright-common/src/stale-screenshot-reporter.ts rename to packages/playwright-common/src/stale-screenshot-reporter.ts diff --git a/packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts b/packages/playwright-common/src/testcontainers/HomeserverContainer.ts similarity index 100% rename from packages/element-web-playwright-common/src/testcontainers/HomeserverContainer.ts rename to packages/playwright-common/src/testcontainers/HomeserverContainer.ts diff --git a/packages/element-web-playwright-common/src/testcontainers/index.ts b/packages/playwright-common/src/testcontainers/index.ts similarity index 100% rename from packages/element-web-playwright-common/src/testcontainers/index.ts rename to packages/playwright-common/src/testcontainers/index.ts diff --git a/packages/element-web-playwright-common/src/testcontainers/mailpit.ts b/packages/playwright-common/src/testcontainers/mailpit.ts similarity index 100% rename from packages/element-web-playwright-common/src/testcontainers/mailpit.ts rename to packages/playwright-common/src/testcontainers/mailpit.ts diff --git a/packages/element-web-playwright-common/src/testcontainers/mas-config.ts b/packages/playwright-common/src/testcontainers/mas-config.ts similarity index 100% rename from packages/element-web-playwright-common/src/testcontainers/mas-config.ts rename to packages/playwright-common/src/testcontainers/mas-config.ts diff --git a/packages/element-web-playwright-common/src/testcontainers/mas.ts b/packages/playwright-common/src/testcontainers/mas.ts similarity index 100% rename from packages/element-web-playwright-common/src/testcontainers/mas.ts rename to packages/playwright-common/src/testcontainers/mas.ts diff --git a/packages/element-web-playwright-common/src/testcontainers/postgres.ts b/packages/playwright-common/src/testcontainers/postgres.ts similarity index 100% rename from packages/element-web-playwright-common/src/testcontainers/postgres.ts rename to packages/playwright-common/src/testcontainers/postgres.ts diff --git a/packages/element-web-playwright-common/src/testcontainers/synapse.ts b/packages/playwright-common/src/testcontainers/synapse.ts similarity index 100% rename from packages/element-web-playwright-common/src/testcontainers/synapse.ts rename to packages/playwright-common/src/testcontainers/synapse.ts diff --git a/packages/element-web-playwright-common/src/utils/api.ts b/packages/playwright-common/src/utils/api.ts similarity index 100% rename from packages/element-web-playwright-common/src/utils/api.ts rename to packages/playwright-common/src/utils/api.ts diff --git a/packages/element-web-playwright-common/src/utils/config_json.ts b/packages/playwright-common/src/utils/config_json.ts similarity index 100% rename from packages/element-web-playwright-common/src/utils/config_json.ts rename to packages/playwright-common/src/utils/config_json.ts diff --git a/packages/element-web-playwright-common/src/utils/context.ts b/packages/playwright-common/src/utils/context.ts similarity index 100% rename from packages/element-web-playwright-common/src/utils/context.ts rename to packages/playwright-common/src/utils/context.ts diff --git a/packages/element-web-playwright-common/src/utils/logger.ts b/packages/playwright-common/src/utils/logger.ts similarity index 100% rename from packages/element-web-playwright-common/src/utils/logger.ts rename to packages/playwright-common/src/utils/logger.ts diff --git a/packages/element-web-playwright-common/src/utils/object.ts b/packages/playwright-common/src/utils/object.ts similarity index 100% rename from packages/element-web-playwright-common/src/utils/object.ts rename to packages/playwright-common/src/utils/object.ts diff --git a/packages/element-web-playwright-common/src/utils/port.ts b/packages/playwright-common/src/utils/port.ts similarity index 100% rename from packages/element-web-playwright-common/src/utils/port.ts rename to packages/playwright-common/src/utils/port.ts diff --git a/packages/element-web-playwright-common/src/utils/rand.ts b/packages/playwright-common/src/utils/rand.ts similarity index 100% rename from packages/element-web-playwright-common/src/utils/rand.ts rename to packages/playwright-common/src/utils/rand.ts diff --git a/packages/element-web-playwright-common/tsconfig.json b/packages/playwright-common/tsconfig.json similarity index 100% rename from packages/element-web-playwright-common/tsconfig.json rename to packages/playwright-common/tsconfig.json From 540e620f40c7c598600c3beea92864ebbfb1314b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 9 Apr 2026 15:22:56 +0100 Subject: [PATCH 110/113] Adjust consumers of playwright-common --- apps/web/package.json | 9 +- apps/web/project.json | 10 ++ package.json | 2 +- packages/playwright-common/project.json | 7 ++ packages/shared-components/package.json | 2 +- packages/shared-components/project.json | 6 +- pnpm-lock.yaml | 117 +++++++++++++----------- pnpm-workspace.yaml | 1 - 8 files changed, 89 insertions(+), 65 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 98b6f8c876..87f78828c9 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -30,9 +30,9 @@ "lint:types": "nx lint:types", "lint:style": "stylelint \"res/css/**/*.pcss\"", "test": "nx test:unit", - "test:playwright": "playwright test", - "test:playwright:open": "pnpm test:playwright --ui", - "test:playwright:screenshots": "playwright-screenshots-experimental pnpm playwright test --update-snapshots --project=Chrome --grep @screenshot", + "test:playwright": "nx test:playwright", + "test:playwright:open": "nx test:playwright --ui", + "test:playwright:screenshots": "nx test:playwright:screenshots", "coverage": "pnpm test --coverage", "analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp" }, @@ -127,8 +127,7 @@ "@babel/preset-typescript": "^7.12.7", "@casualbot/jest-sonar-reporter": "2.5.0", "@element-hq/element-call-embedded": "0.18.0", - "@element-hq/element-web-playwright-common": "catalog:", - "@element-hq/element-web-playwright-common-local": "workspace:*", + "@element-hq/element-web-playwright-common": "workspace:*", "@fetch-mock/jest": "^0.2.20", "@jest/globals": "^30.2.0", "@peculiar/webcrypto": "^1.4.3", diff --git a/apps/web/project.json b/apps/web/project.json index 95f9a6828d..8a434bc9aa 100644 --- a/apps/web/project.json +++ b/apps/web/project.json @@ -53,6 +53,16 @@ "cwd": "apps/web" }, "dependsOn": ["^build"] + }, + "test:playwright": { + "command": "playwright test", + "options": { "cwd": "apps/web" }, + "dependsOn": ["^build:playwright"] + }, + "test:playwright:screenshots": { + "command": "playwright-screenshots nx test:playwright --update-snapshots --project=Chrome --grep @screenshot", + "options": { "cwd": "apps/web" }, + "dependsOn": ["^build:playwright"] } } } diff --git a/package.json b/package.json index e3d619d109..cb48fcfa1b 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "devDependencies": { "@action-validator/cli": "^0.6.0", "@action-validator/core": "^0.6.0", - "@element-hq/element-web-playwright-common": "catalog:", + "@element-hq/element-web-playwright-common": "workspace:*", "@nx-tools/nx-container": "^7.2.1", "@nx/jest": "^22.5.0", "@playwright/test": "catalog:", diff --git a/packages/playwright-common/project.json b/packages/playwright-common/project.json index 8bb084571d..b3e491b9d6 100644 --- a/packages/playwright-common/project.json +++ b/packages/playwright-common/project.json @@ -3,6 +3,13 @@ "projectType": "library", "root": "packages/playwright-common", "targets": { + "build:playwright": { + "cache": true, + "command": "tsc", + "inputs": ["src"], + "outputs": ["{projectRoot}/lib"], + "options": { "cwd": "packages/playwright-common" } + }, "docker:prebuild": { "cache": true, "command": "echo PLAYWRIGHT_VERSION=$(pnpm --silent -- playwright --version | awk '{print $2}') > .env.docker:build", diff --git a/packages/shared-components/package.json b/packages/shared-components/package.json index 390010a2a6..29a3ded4d6 100644 --- a/packages/shared-components/package.json +++ b/packages/shared-components/package.json @@ -69,7 +69,7 @@ "temporal-polyfill": "^0.3.0" }, "devDependencies": { - "@element-hq/element-web-playwright-common-local": "workspace:*", + "@element-hq/element-web-playwright-common": "workspace:*", "@fetch-mock/vitest": "^0.2.18", "@fontsource/inter": "catalog:", "@matrix-org/react-sdk-module-api": "^2.5.0", diff --git a/packages/shared-components/project.json b/packages/shared-components/project.json index 13d072b225..0c9dc50527 100644 --- a/packages/shared-components/project.json +++ b/packages/shared-components/project.json @@ -40,17 +40,17 @@ "test:storybook": { "command": "vitest --project=storybook", "options": { "cwd": "packages/shared-components" }, - "dependsOn": ["typedoc"] + "dependsOn": ["typedoc", "^build:playwright"] }, "test:storybook:update": { - "command": "playwright-screenshots-experimental nx test:storybook --run --update", + "command": "playwright-screenshots nx test:storybook --run --update", "options": { "env": { "CI": "1" }, "cwd": "packages/shared-components" }, - "dependsOn": ["typedoc"] + "dependsOn": ["typedoc", "^build:playwright"] } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7feb5f30f4..81ecbf1c67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,9 +9,6 @@ catalogs: '@element-hq/element-web-module-api': specifier: 1.12.0 version: 1.12.0 - '@element-hq/element-web-playwright-common': - specifier: 2.4.0 - version: 2.4.0 '@fontsource/inter': specifier: 5.2.8 version: 5.2.8 @@ -139,8 +136,8 @@ importers: specifier: ^0.6.0 version: 0.6.0 '@element-hq/element-web-playwright-common': - specifier: 'catalog:' - version: 2.4.0(@element-hq/element-web-module-api@1.12.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.59.1)(playwright-core@1.59.1) + specifier: workspace:* + version: link:packages/playwright-common '@nx-tools/nx-container': specifier: ^7.2.1 version: 7.2.1(@nx/devkit@22.5.3(nx@22.5.4))(@nx/js@22.5.3(@babel/traverse@7.29.0)(nx@22.5.4))(dotenv@17.4.0)(nx@22.5.4)(tslib@2.8.1) @@ -188,10 +185,10 @@ importers: version: 5.9.3 vitepress: specifier: ^1.6.4 - version: 1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(react@19.2.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.1)(typescript@5.9.3) + version: 1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.1)(typescript@5.9.3) vitepress-plugin-mermaid: specifier: ^2.0.17 - version: 2.0.17(mermaid@11.14.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(react@19.2.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.1)(typescript@5.9.3)) + version: 2.0.17(mermaid@11.14.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.1)(typescript@5.9.3)) yaml: specifier: 2.8.3 version: 2.8.3 @@ -465,7 +462,7 @@ importers: version: 1.0.3 matrix-js-sdk: specifier: github:matrix-org/matrix-js-sdk#develop - version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6371e4b25206dcd459d19375f5d046993ce11b1b + version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/f17f013f1e0d68622e9dd2f863689b7cd0ae09d1 matrix-widget-api: specifier: ^1.17.0 version: 1.17.0 @@ -597,9 +594,6 @@ importers: specifier: 0.18.0 version: 0.18.0 '@element-hq/element-web-playwright-common': - specifier: 'catalog:' - version: 2.4.0(@element-hq/element-web-module-api@1.12.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.59.1)(playwright-core@1.59.1) - '@element-hq/element-web-playwright-common-local': specifier: workspace:* version: link:../../packages/playwright-common '@fetch-mock/jest': @@ -928,7 +922,47 @@ importers: version: 2.8.3 packages/playwright-common: + dependencies: + '@axe-core/playwright': + specifier: ^4.10.1 + version: 4.11.1(playwright-core@1.59.1) + '@playwright/test': + specifier: 'catalog:' + version: 1.59.1 + '@testcontainers/postgresql': + specifier: ^11.0.0 + version: 11.11.0 + glob: + specifier: ^13.0.5 + version: 13.0.6 + lodash-es: + specifier: ^4.17.23 + version: 4.18.1 + mailpit-api: + specifier: ^1.2.0 + version: 1.7.0 + playwright-core: + specifier: 'catalog:' + version: 1.59.1 + strip-ansi: + specifier: ^7.1.0 + version: 7.2.0 + testcontainers: + specifier: ^11.0.0 + version: 11.12.0 + yaml: + specifier: 2.8.3 + version: 2.8.3 devDependencies: + '@element-hq/element-web-module-api': + specifier: '*' + version: 1.12.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4) + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 + typescript: + specifier: ^5.8.2 + version: 5.9.3 wait-on: specifier: ^9.0.4 version: 9.0.4 @@ -984,7 +1018,7 @@ importers: specifier: ^0.3.0 version: 0.3.2 devDependencies: - '@element-hq/element-web-playwright-common-local': + '@element-hq/element-web-playwright-common': specifier: workspace:* version: link:../playwright-common '@fetch-mock/vitest': @@ -2410,15 +2444,6 @@ packages: matrix-web-i18n: optional: true - '@element-hq/element-web-playwright-common@2.4.0': - resolution: {integrity: sha512-6zXRkeTlCEEBfUKgE42+5+RYGQloQB4p/HBdTBxjnMArAnZY/+8GE8pY1CeoJrmRISxGdDbdWkMEyT7bwIBx/g==} - engines: {node: '>=20.0.0'} - hasBin: true - peerDependencies: - '@element-hq/element-web-module-api': '*' - '@playwright/test': ^1.52.0 - playwright-core: ^1.52.0 - '@emnapi/core@1.8.1': resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} @@ -5405,6 +5430,9 @@ packages: '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + '@types/lodash@4.17.24': resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} @@ -9845,8 +9873,8 @@ packages: matrix-events-sdk@0.0.1: resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==} - matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6371e4b25206dcd459d19375f5d046993ce11b1b: - resolution: {tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6371e4b25206dcd459d19375f5d046993ce11b1b} + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/f17f013f1e0d68622e9dd2f863689b7cd0ae09d1: + resolution: {tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/f17f013f1e0d68622e9dd2f863689b7cd0ae09d1} version: 41.3.0 engines: {node: '>=22.0.0'} @@ -14774,9 +14802,9 @@ snapshots: '@docsearch/css@3.8.2': {} - '@docsearch/js@3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(react@19.2.4)(search-insights@2.17.3)': + '@docsearch/js@3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(search-insights@2.17.3)': dependencies: - '@docsearch/react': 3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(react@19.2.4)(search-insights@2.17.3) + '@docsearch/react': 3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(search-insights@2.17.3) preact: 10.28.3 transitivePeerDependencies: - '@algolia/client-search' @@ -14785,7 +14813,7 @@ snapshots: - react-dom - search-insights - '@docsearch/react@3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(react@19.2.4)(search-insights@2.17.3)': + '@docsearch/react@3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(search-insights@2.17.3)': dependencies: '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.50.0)(algoliasearch@5.50.0)(search-insights@2.17.3) '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.50.0)(algoliasearch@5.50.0) @@ -14793,7 +14821,6 @@ snapshots: algoliasearch: 5.50.0 optionalDependencies: '@types/react': 19.2.10 - react: 19.2.4 search-insights: 2.17.3 transitivePeerDependencies: - '@algolia/client-search' @@ -14916,28 +14943,6 @@ snapshots: '@matrix-org/react-sdk-module-api': 2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4) matrix-web-i18n: 3.6.0 - '@element-hq/element-web-playwright-common@2.4.0(@element-hq/element-web-module-api@1.12.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.59.1)(playwright-core@1.59.1)': - dependencies: - '@axe-core/playwright': 4.11.1(playwright-core@1.59.1) - '@element-hq/element-web-module-api': 1.12.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4) - '@playwright/test': 1.59.1 - '@testcontainers/postgresql': 11.11.0 - glob: 13.0.6 - lodash-es: 4.18.1 - mailpit-api: 1.7.0 - playwright-core: 1.59.1 - strip-ansi: 7.2.0 - testcontainers: 11.12.0 - yaml: 2.8.3 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - bufferutil - - debug - - react-native-b4a - - supports-color - - utf-8-validate - '@emnapi/core@1.8.1': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -18158,6 +18163,10 @@ snapshots: '@types/linkify-it@5.0.0': {} + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.24 + '@types/lodash@4.17.24': {} '@types/markdown-it@14.1.2': @@ -23464,7 +23473,7 @@ snapshots: matrix-events-sdk@0.0.1: {} - matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6371e4b25206dcd459d19375f5d046993ce11b1b: + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/f17f013f1e0d68622e9dd2f863689b7cd0ae09d1: dependencies: '@babel/runtime': 7.28.6 '@matrix-org/matrix-sdk-crypto-wasm': 18.0.0 @@ -27112,17 +27121,17 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - vitepress-plugin-mermaid@2.0.17(mermaid@11.14.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(react@19.2.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.1)(typescript@5.9.3)): + vitepress-plugin-mermaid@2.0.17(mermaid@11.14.0)(vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.1)(typescript@5.9.3)): dependencies: mermaid: 11.14.0 - vitepress: 1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(react@19.2.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.1)(typescript@5.9.3) + vitepress: 1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.1)(typescript@5.9.3) optionalDependencies: '@mermaid-js/mermaid-mindmap': 9.3.0 - vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(react@19.2.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.1)(typescript@5.9.3): + vitepress@1.6.4(@algolia/client-search@5.50.0)(@types/node@18.19.130)(@types/react@19.2.10)(axios@1.13.5)(jwt-decode@4.0.0)(lightningcss@1.32.0)(postcss@8.5.8)(qrcode@1.5.4)(search-insights@2.17.3)(sugarss@5.0.1(postcss@8.5.8))(terser@5.46.1)(typescript@5.9.3): dependencies: '@docsearch/css': 3.8.2 - '@docsearch/js': 3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(react@19.2.4)(search-insights@2.17.3) + '@docsearch/js': 3.8.2(@algolia/client-search@5.50.0)(@types/react@19.2.10)(search-insights@2.17.3) '@iconify-json/simple-icons': 1.2.75 '@shikijs/core': 2.5.0 '@shikijs/transformers': 2.5.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7251cf86ce..a49b8531d4 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -13,7 +13,6 @@ catalog: "@types/react": ^19.2.10 "@types/react-dom": ^19.2.3 # playwright - "@element-hq/element-web-playwright-common": 2.4.0 "@playwright/test": 1.59.1 "playwright-core": 1.59.1 # Module API From 561dbd32be43bfd90290e53e77ab292be9521666 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 9 Apr 2026 15:31:34 +0100 Subject: [PATCH 111/113] Iterate --- .github/workflows/build-and-test.yaml | 2 +- apps/web/project.json | 2 +- knip.ts | 8 ++------ packages/playwright-common/src/testcontainers/index.ts | 2 ++ 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 19943434e6..0182b45351 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -165,7 +165,7 @@ jobs: - name: Run Playwright tests working-directory: apps/web run: | - pnpm playwright test \ + pnpm test:playwright \ --shard "$SHARD" \ --project="${{ matrix.project }}" \ ${{ (github.event_name == 'pull_request' && matrix.runAllTests == false ) && '--grep-invert @mergequeue' || '' }} diff --git a/apps/web/project.json b/apps/web/project.json index 8a434bc9aa..59fa9f3a66 100644 --- a/apps/web/project.json +++ b/apps/web/project.json @@ -44,7 +44,7 @@ "parallel": false, "cwd": "apps/web" }, - "dependsOn": ["^build"] + "dependsOn": ["^build", "^build:playwright"] }, "test:unit": { "executor": "@nx/jest:jest", diff --git a/knip.ts b/knip.ts index 43ba17061b..97d3332861 100644 --- a/knip.ts +++ b/knip.ts @@ -5,13 +5,9 @@ process.env.GITHUB_ACTIONS = "1"; export default { workspaces: { - "packages/shared-components": { - ignoreDependencies: [ - // Used for vitest browser tests - "@playwright/test", - ], - }, + "packages/shared-components": {}, "packages/playwright-common": { + entry: ["src/stale-screenshot-reporter.ts", "src/fixtures/index.ts", "src/testcontainers/index.ts"], ignoreDependencies: [ // Used in playwright-screenshots.sh "wait-on", diff --git a/packages/playwright-common/src/testcontainers/index.ts b/packages/playwright-common/src/testcontainers/index.ts index 42c73f4802..c7dbb4fd68 100644 --- a/packages/playwright-common/src/testcontainers/index.ts +++ b/packages/playwright-common/src/testcontainers/index.ts @@ -6,11 +6,13 @@ Please see LICENSE files in the repository root for full details. */ export { PostgreSqlContainer, StartedPostgreSqlContainer } from "@testcontainers/postgresql"; +export { makePostgres } from "./postgres.js"; export type { HomeserverInstance, HomeserverContainer, StartedHomeserverContainer } from "./HomeserverContainer.js"; export { type SynapseConfig, SynapseContainer, StartedSynapseContainer } from "./synapse.js"; export { type MasConfig, MatrixAuthenticationServiceContainer, StartedMatrixAuthenticationServiceContainer, + makeMas, } from "./mas.js"; export { type MailpitClient, MailpitContainer, StartedMailpitContainer } from "./mailpit.js"; From 917237fc3c004456749b53e21c59dcd28eb32757 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 9 Apr 2026 15:50:51 +0100 Subject: [PATCH 112/113] Pass args thru nx --- apps/web/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 87f78828c9..6640de4ff9 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -30,9 +30,9 @@ "lint:types": "nx lint:types", "lint:style": "stylelint \"res/css/**/*.pcss\"", "test": "nx test:unit", - "test:playwright": "nx test:playwright", - "test:playwright:open": "nx test:playwright --ui", - "test:playwright:screenshots": "nx test:playwright:screenshots", + "test:playwright": "nx test:playwright --", + "test:playwright:open": "nx test:playwright -- --ui", + "test:playwright:screenshots": "nx test:playwright:screenshots --", "coverage": "pnpm test --coverage", "analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp" }, From 1096ca206653359efa67adf40f9aa93d2acb4433 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 10 Apr 2026 17:39:46 +0100 Subject: [PATCH 113/113] Iterate --- knip.ts | 2 +- package.json | 1 - packages/playwright-common/{ => src}/flaky-reporter.ts | 0 playwright-merge.config.ts | 4 ++-- pnpm-lock.yaml | 3 --- 5 files changed, 3 insertions(+), 7 deletions(-) rename packages/playwright-common/{ => src}/flaky-reporter.ts (100%) diff --git a/knip.ts b/knip.ts index 97d3332861..6efd0ee3f2 100644 --- a/knip.ts +++ b/knip.ts @@ -7,7 +7,7 @@ export default { workspaces: { "packages/shared-components": {}, "packages/playwright-common": { - entry: ["src/stale-screenshot-reporter.ts", "src/fixtures/index.ts", "src/testcontainers/index.ts"], + entry: ["src/fixtures/index.ts", "src/testcontainers/index.ts"], ignoreDependencies: [ // Used in playwright-screenshots.sh "wait-on", diff --git a/package.json b/package.json index cb48fcfa1b..19497482b8 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,6 @@ "devDependencies": { "@action-validator/cli": "^0.6.0", "@action-validator/core": "^0.6.0", - "@element-hq/element-web-playwright-common": "workspace:*", "@nx-tools/nx-container": "^7.2.1", "@nx/jest": "^22.5.0", "@playwright/test": "catalog:", diff --git a/packages/playwright-common/flaky-reporter.ts b/packages/playwright-common/src/flaky-reporter.ts similarity index 100% rename from packages/playwright-common/flaky-reporter.ts rename to packages/playwright-common/src/flaky-reporter.ts diff --git a/playwright-merge.config.ts b/playwright-merge.config.ts index c4498527a3..3a30236e14 100644 --- a/playwright-merge.config.ts +++ b/playwright-merge.config.ts @@ -14,7 +14,7 @@ export default defineConfig({ testDir: "apps/web/playwright/e2e", reporter: [ ["html", { open: "never" }], - ["./packages/playwright-common/flaky-reporter.ts"], - ["@element-hq/element-web-playwright-common/lib/stale-screenshot-reporter.js"], + ["./packages/playwright-common/src/flaky-reporter.ts"], + ["./packages/playwright-common/src/stale-screenshot-reporter.ts"], ], }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e909a52b60..0bf4434d24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,9 +135,6 @@ importers: '@action-validator/core': specifier: ^0.6.0 version: 0.6.0 - '@element-hq/element-web-playwright-common': - specifier: workspace:* - version: link:packages/playwright-common '@nx-tools/nx-container': specifier: ^7.2.1 version: 7.2.1(@nx/devkit@22.5.3(nx@22.5.4))(@nx/js@22.5.3(@babel/traverse@7.29.0)(nx@22.5.4))(dotenv@17.4.0)(nx@22.5.4)(tslib@2.8.1)