From 8953936d3d399bae76bed7abf1f1a627f806532a Mon Sep 17 00:00:00 2001 From: fkwp Date: Tue, 27 May 2025 17:53:06 +0200 Subject: [PATCH 01/11] fix docker compose playwright override --- .github/workflows/test.yaml | 2 +- package.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 2ef4dc04..30934f71 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -46,7 +46,7 @@ jobs: run: yarn playwright install --with-deps - name: Run backend components run: | - docker compose -f playwright-backend-docker-compose.yml up -d + docker compose -f playwright-backend-docker-compose.yml -f playwright-backend-docker-compose.override.yml up -d docker ps - name: Copy config file run: cp config/config.devenv.json public/config.json diff --git a/package.json b/package.json index 97c4f737..e8ccdd95 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "test": "vitest", "test:coverage": "vitest --coverage", "backend": "docker-compose -f dev-backend-docker-compose.yml up", + "backend-playwright": "docker-compose -f playwright-backend-docker-compose.yml -f playwright-backend-docker-compose.override.yml up", "test:playwright": "playwright test", "test:playwright:open": "yarn test:playwright --ui", "links:enable": "mv .links.disabled.yaml .links.yaml & touch .links.yaml", From 99fc7162e207abc73ba6f4107d2759bffcbda4fd Mon Sep 17 00:00:00 2001 From: fkwp Date: Tue, 27 May 2025 18:04:34 +0200 Subject: [PATCH 02/11] fix widget fixture --- playwright/fixtures/widget-user.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playwright/fixtures/widget-user.ts b/playwright/fixtures/widget-user.ts index d1412bd8..b25602eb 100644 --- a/playwright/fixtures/widget-user.ts +++ b/playwright/fixtures/widget-user.ts @@ -30,8 +30,8 @@ const PASSWORD = "foobarbaz1!"; const CONFIG_JSON = { default_server_config: { "m.homeserver": { - base_url: "http://synapse.localhost:8008", - server_name: "synapse.localhost", + base_url: "https://synapse.m.localhost", + server_name: "synapse.m.localhost", }, }, From e4fd630f376faa9cd80698f491614ad236c53d23 Mon Sep 17 00:00:00 2001 From: fkwp Date: Tue, 27 May 2025 19:03:53 +0200 Subject: [PATCH 03/11] fix element web fixture --- playwright/fixtures/widget-user.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/playwright/fixtures/widget-user.ts b/playwright/fixtures/widget-user.ts index b25602eb..46973f83 100644 --- a/playwright/fixtures/widget-user.ts +++ b/playwright/fixtures/widget-user.ts @@ -99,6 +99,13 @@ export const widgetTest = test.extend({ .getByRole("textbox", { name: "Confirm password" }) .fill(PASSWORD); await ewPage1.getByRole("button", { name: "Register" }).click(); + await expect( + ewPage1.getByRole("button", { name: "Continue" }), + ).toBeVisible(); + await ewPage1 + .getByRole("textbox", { name: "Password", exact: true }) + .fill(PASSWORD); + await ewPage1.getByRole("button", { name: "Continue" }).click(); await expect( ewPage1.getByRole("heading", { name: `Welcome ${userA}` }), ).toBeVisible(); @@ -127,6 +134,13 @@ export const widgetTest = test.extend({ .getByRole("textbox", { name: "Confirm password" }) .fill(PASSWORD); await ewPage2.getByRole("button", { name: "Register" }).click(); + await expect( + ewPage2.getByRole("button", { name: "Continue" }), + ).toBeVisible(); + await ewPage2 + .getByRole("textbox", { name: "Password", exact: true }) + .fill(PASSWORD); + await ewPage2.getByRole("button", { name: "Continue" }).click(); await expect( ewPage2.getByRole("heading", { name: `Welcome ${userB}` }), ).toBeVisible(); From ebc714b73ff0813f134808e7d75305c5aea49fc7 Mon Sep 17 00:00:00 2001 From: fkwp Date: Tue, 27 May 2025 20:06:28 +0200 Subject: [PATCH 04/11] force to pull the latest docker images --- .github/workflows/test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 30934f71..518226fb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -46,6 +46,7 @@ jobs: run: yarn playwright install --with-deps - name: Run backend components run: | + docker compose -f playwright-backend-docker-compose.yml -f playwright-backend-docker-compose.override.yml pull docker compose -f playwright-backend-docker-compose.yml -f playwright-backend-docker-compose.override.yml up -d docker ps - name: Copy config file From d5729779222b65cce41542e785b3e70edc40f7ae Mon Sep 17 00:00:00 2001 From: fkwp Date: Fri, 30 May 2025 13:38:45 +0200 Subject: [PATCH 05/11] refactor: splitout registerUser into its own function. Make the'Confirm your identity by entering your account password below.' flow optional --- playwright/fixtures/widget-user.ts | 108 +++++++++++------------------ 1 file changed, 39 insertions(+), 69 deletions(-) diff --git a/playwright/fixtures/widget-user.ts b/playwright/fixtures/widget-user.ts index 46973f83..39c23bbd 100644 --- a/playwright/fixtures/widget-user.ts +++ b/playwright/fixtures/widget-user.ts @@ -74,6 +74,40 @@ async function setDevToolElementCallDevUrl(page: Page): Promise { }); } +/** + * Registers a new user and returns page, clientHandle and mxId. + */ +async function registerUser( + browser: typeof test["browser"], + username: string, +): Promise<{ page: Page; clientHandle: JSHandle; mxId: string }> { + const userContext = await browser.newContext({ + reducedMotion: "reduce", + }); + const page = await userContext.newPage(); + await page.goto("http://localhost:8081/#/welcome"); + await page.getByRole("link", { name: "Create Account" }).click(); + await page.getByRole("textbox", { name: "Username" }).fill(username); + await page.getByRole("textbox", { name: "Password", exact: true }).fill(PASSWORD); + await page.getByRole("textbox", { name: "Confirm password" }).click(); + await page.getByRole("textbox", { name: "Confirm password" }).fill(PASSWORD); + await page.getByRole("button", { name: "Register" }).click(); + const continueButton = page.getByRole("button", { name: "Continue" }); + if (await continueButton.isVisible().catch(() => false)) { + await page.getByRole("textbox", { name: "Password", exact: true }).fill(PASSWORD); + await continueButton.click(); + } + await expect( + page.getByRole("heading", { name: `Welcome ${username}` }) + ).toBeVisible(); + await setDevToolElementCallDevUrl(page); + + const clientHandle = await page.evaluateHandle(() => window.mxMatrixClientPeg.get()); + const mxId = (await clientHandle.evaluate((cli) => cli.getUserId(), clientHandle))!; + + return { page, clientHandle, mxId }; +} + export const widgetTest = test.extend({ asWidget: async ({ browser, context }, pUse) => { await context.route(`http://localhost:8081/config.json*`, async (route) => { @@ -83,75 +117,11 @@ export const widgetTest = test.extend({ const userA = `brooks_${Date.now()}`; const userB = `whistler_${Date.now()}`; - const user1Context = await browser.newContext({ - reducedMotion: "reduce", - }); - const ewPage1 = await user1Context.newPage(); - // Register the first user - await ewPage1.goto("http://localhost:8081/#/welcome"); - await ewPage1.getByRole("link", { name: "Create Account" }).click(); - await ewPage1.getByRole("textbox", { name: "Username" }).fill(userA); - await ewPage1 - .getByRole("textbox", { name: "Password", exact: true }) - .fill(PASSWORD); - await ewPage1.getByRole("textbox", { name: "Confirm password" }).click(); - await ewPage1 - .getByRole("textbox", { name: "Confirm password" }) - .fill(PASSWORD); - await ewPage1.getByRole("button", { name: "Register" }).click(); - await expect( - ewPage1.getByRole("button", { name: "Continue" }), - ).toBeVisible(); - await ewPage1 - .getByRole("textbox", { name: "Password", exact: true }) - .fill(PASSWORD); - await ewPage1.getByRole("button", { name: "Continue" }).click(); - await expect( - ewPage1.getByRole("heading", { name: `Welcome ${userA}` }), - ).toBeVisible(); - await setDevToolElementCallDevUrl(ewPage1); - - const brooksClientHandle = await ewPage1.evaluateHandle(() => - window.mxMatrixClientPeg.get(), - ); - const brooksMxId = (await brooksClientHandle.evaluate((cli) => { - return cli.getUserId(); - }, brooksClientHandle))!; - - const user2Context = await browser.newContext({ - reducedMotion: "reduce", - }); - const ewPage2 = await user2Context.newPage(); - // Register the second user - await ewPage2.goto("http://localhost:8081/#/welcome"); - await ewPage2.getByRole("link", { name: "Create Account" }).click(); - await ewPage2.getByRole("textbox", { name: "Username" }).fill(userB); - await ewPage2 - .getByRole("textbox", { name: "Password", exact: true }) - .fill(PASSWORD); - await ewPage2.getByRole("textbox", { name: "Confirm password" }).click(); - await ewPage2 - .getByRole("textbox", { name: "Confirm password" }) - .fill(PASSWORD); - await ewPage2.getByRole("button", { name: "Register" }).click(); - await expect( - ewPage2.getByRole("button", { name: "Continue" }), - ).toBeVisible(); - await ewPage2 - .getByRole("textbox", { name: "Password", exact: true }) - .fill(PASSWORD); - await ewPage2.getByRole("button", { name: "Continue" }).click(); - await expect( - ewPage2.getByRole("heading", { name: `Welcome ${userB}` }), - ).toBeVisible(); - await setDevToolElementCallDevUrl(ewPage2); - - const whistlerClientHandle = await ewPage2.evaluateHandle(() => - window.mxMatrixClientPeg.get(), - ); - const whistlerMxId = (await whistlerClientHandle.evaluate((cli) => { - return cli.getUserId(); - }, whistlerClientHandle))!; + // Register users + const { page: ewPage1, clientHandle: brooksClientHandle, mxId: brooksMxId } = + await registerUser(browser, userA); + const { page: ewPage2, clientHandle: whistlerClientHandle, mxId: whistlerMxId } = + await registerUser(browser, userB); // Invite the second user await ewPage1.getByRole("button", { name: "Add room" }).click(); From aabfe1602185a1db0ca47c937e30d2bae2ab0ea9 Mon Sep 17 00:00:00 2001 From: fkwp Date: Fri, 30 May 2025 13:51:55 +0200 Subject: [PATCH 06/11] prettier --- playwright/fixtures/widget-user.ts | 35 +++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/playwright/fixtures/widget-user.ts b/playwright/fixtures/widget-user.ts index 39c23bbd..50949292 100644 --- a/playwright/fixtures/widget-user.ts +++ b/playwright/fixtures/widget-user.ts @@ -78,7 +78,7 @@ async function setDevToolElementCallDevUrl(page: Page): Promise { * Registers a new user and returns page, clientHandle and mxId. */ async function registerUser( - browser: typeof test["browser"], + browser: (typeof test)["browser"], username: string, ): Promise<{ page: Page; clientHandle: JSHandle; mxId: string }> { const userContext = await browser.newContext({ @@ -88,22 +88,31 @@ async function registerUser( await page.goto("http://localhost:8081/#/welcome"); await page.getByRole("link", { name: "Create Account" }).click(); await page.getByRole("textbox", { name: "Username" }).fill(username); - await page.getByRole("textbox", { name: "Password", exact: true }).fill(PASSWORD); + await page + .getByRole("textbox", { name: "Password", exact: true }) + .fill(PASSWORD); await page.getByRole("textbox", { name: "Confirm password" }).click(); await page.getByRole("textbox", { name: "Confirm password" }).fill(PASSWORD); await page.getByRole("button", { name: "Register" }).click(); const continueButton = page.getByRole("button", { name: "Continue" }); if (await continueButton.isVisible().catch(() => false)) { - await page.getByRole("textbox", { name: "Password", exact: true }).fill(PASSWORD); + await page + .getByRole("textbox", { name: "Password", exact: true }) + .fill(PASSWORD); await continueButton.click(); } await expect( - page.getByRole("heading", { name: `Welcome ${username}` }) + page.getByRole("heading", { name: `Welcome ${username}` }), ).toBeVisible(); await setDevToolElementCallDevUrl(page); - const clientHandle = await page.evaluateHandle(() => window.mxMatrixClientPeg.get()); - const mxId = (await clientHandle.evaluate((cli) => cli.getUserId(), clientHandle))!; + const clientHandle = await page.evaluateHandle(() => + window.mxMatrixClientPeg.get(), + ); + const mxId = (await clientHandle.evaluate( + (cli) => cli.getUserId(), + clientHandle, + ))!; return { page, clientHandle, mxId }; } @@ -118,10 +127,16 @@ export const widgetTest = test.extend({ const userB = `whistler_${Date.now()}`; // Register users - const { page: ewPage1, clientHandle: brooksClientHandle, mxId: brooksMxId } = - await registerUser(browser, userA); - const { page: ewPage2, clientHandle: whistlerClientHandle, mxId: whistlerMxId } = - await registerUser(browser, userB); + const { + page: ewPage1, + clientHandle: brooksClientHandle, + mxId: brooksMxId, + } = await registerUser(browser, userA); + const { + page: ewPage2, + clientHandle: whistlerClientHandle, + mxId: whistlerMxId, + } = await registerUser(browser, userB); // Invite the second user await ewPage1.getByRole("button", { name: "Add room" }).click(); From e56fb7cfc80decc1fa07b30864d33d0cbfd834cd Mon Sep 17 00:00:00 2001 From: fkwp Date: Fri, 30 May 2025 14:10:57 +0200 Subject: [PATCH 07/11] use .toBeVisible instead of continueButton.isVisible --- playwright/fixtures/widget-user.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/playwright/fixtures/widget-user.ts b/playwright/fixtures/widget-user.ts index 50949292..49aaecf5 100644 --- a/playwright/fixtures/widget-user.ts +++ b/playwright/fixtures/widget-user.ts @@ -95,11 +95,14 @@ async function registerUser( await page.getByRole("textbox", { name: "Confirm password" }).fill(PASSWORD); await page.getByRole("button", { name: "Register" }).click(); const continueButton = page.getByRole("button", { name: "Continue" }); - if (await continueButton.isVisible().catch(() => false)) { + try { + await expect(continueButton).toBeVisible({ timeout: 5000 }); await page .getByRole("textbox", { name: "Password", exact: true }) .fill(PASSWORD); await continueButton.click(); + } catch { + // continueButton not visible, continue as normal } await expect( page.getByRole("heading", { name: `Welcome ${username}` }), From 7e19b5897609ee50eba5b06c8361e47bcc1f2c83 Mon Sep 17 00:00:00 2001 From: fkwp Date: Fri, 30 May 2025 14:29:17 +0200 Subject: [PATCH 08/11] define the widget test as a slow test --- playwright/widget/simple-create.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/playwright/widget/simple-create.spec.ts b/playwright/widget/simple-create.spec.ts index 3712f5c4..c8b97029 100644 --- a/playwright/widget/simple-create.spec.ts +++ b/playwright/widget/simple-create.spec.ts @@ -15,6 +15,8 @@ widgetTest("Start a new call as widget", async ({ asWidget, browserName }) => { "This test is not working on firefox, after hangup brooks is locked in a strange state with a blank widget", ); + test.slow(); // Triples the timeout + const { brooks, whistler } = asWidget; await expect( From a732fcc6045537da95d05d5cc7d53791773497b3 Mon Sep 17 00:00:00 2001 From: fkwp Date: Fri, 30 May 2025 14:46:35 +0200 Subject: [PATCH 09/11] linting --- playwright/fixtures/widget-user.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/playwright/fixtures/widget-user.ts b/playwright/fixtures/widget-user.ts index 49aaecf5..a55166ee 100644 --- a/playwright/fixtures/widget-user.ts +++ b/playwright/fixtures/widget-user.ts @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -import { type Page, test, expect, type JSHandle } from "@playwright/test"; +import { type Browser, type Page, test, expect, type JSHandle } from "@playwright/test"; import type { MatrixClient } from "matrix-js-sdk"; @@ -78,7 +78,7 @@ async function setDevToolElementCallDevUrl(page: Page): Promise { * Registers a new user and returns page, clientHandle and mxId. */ async function registerUser( - browser: (typeof test)["browser"], + browser: Browser, username: string, ): Promise<{ page: Page; clientHandle: JSHandle; mxId: string }> { const userContext = await browser.newContext({ @@ -113,7 +113,7 @@ async function registerUser( window.mxMatrixClientPeg.get(), ); const mxId = (await clientHandle.evaluate( - (cli) => cli.getUserId(), + (cli: MatrixClient) => cli.getUserId(), clientHandle, ))!; From 08ab38280ad382a05b6c8b186663ecfca49f823d Mon Sep 17 00:00:00 2001 From: fkwp Date: Fri, 30 May 2025 14:49:29 +0200 Subject: [PATCH 10/11] prettier --- playwright/fixtures/widget-user.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/playwright/fixtures/widget-user.ts b/playwright/fixtures/widget-user.ts index a55166ee..9caef91d 100644 --- a/playwright/fixtures/widget-user.ts +++ b/playwright/fixtures/widget-user.ts @@ -5,7 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -import { type Browser, type Page, test, expect, type JSHandle } from "@playwright/test"; +import { + type Browser, + type Page, + test, + expect, + type JSHandle, +} from "@playwright/test"; import type { MatrixClient } from "matrix-js-sdk"; From a548c5d59251d5625c1eb022855d56460e5789a4 Mon Sep 17 00:00:00 2001 From: fkwp Date: Fri, 30 May 2025 15:03:43 +0200 Subject: [PATCH 11/11] test.skip() does only skip the test call back but not the fiturex. Hence, disabling the whole test --- playwright/widget/simple-create.spec.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/playwright/widget/simple-create.spec.ts b/playwright/widget/simple-create.spec.ts index c8b97029..00d5c658 100644 --- a/playwright/widget/simple-create.spec.ts +++ b/playwright/widget/simple-create.spec.ts @@ -9,12 +9,13 @@ import { expect, test } from "@playwright/test"; import { widgetTest } from "../fixtures/widget-user.ts"; -widgetTest("Start a new call as widget", async ({ asWidget, browserName }) => { - test.skip( - browserName === "firefox", - "This test is not working on firefox, after hangup brooks is locked in a strange state with a blank widget", - ); +// Skip test, including Fixtures +widgetTest.skip( + ({ browserName }) => browserName === "firefox", + "This test is not working on firefox, after hangup brooks is locked in a strange state with a blank widget", +); +widgetTest("Start a new call as widget", async ({ asWidget, browserName }) => { test.slow(); // Triples the timeout const { brooks, whistler } = asWidget;