@element-hq/element-web-playwright-common 2.4.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +14 -8
- package/README.md +2 -2
- package/docker-entrypoint.sh +1 -8
- package/lib/expect/axe.d.ts +1 -1
- package/lib/expect/axe.d.ts.map +1 -1
- package/lib/expect/screenshot.d.ts +1 -1
- package/lib/expect/screenshot.d.ts.map +1 -1
- package/lib/fixtures/axe.d.ts +2 -2
- package/lib/fixtures/axe.d.ts.map +1 -1
- package/lib/fixtures/services.d.ts.map +1 -1
- package/lib/fixtures/services.js +2 -22
- package/lib/fixtures/toasts.d.ts +64 -0
- package/lib/fixtures/toasts.d.ts.map +1 -0
- package/lib/fixtures/toasts.js +97 -0
- package/lib/fixtures/user.d.ts +13 -2
- package/lib/fixtures/user.d.ts.map +1 -1
- package/lib/fixtures/user.js +4 -1
- package/lib/flaky-reporter.d.ts +24 -0
- package/lib/flaky-reporter.d.ts.map +1 -0
- package/lib/flaky-reporter.js +153 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/stale-screenshot-reporter.d.ts +1 -0
- package/lib/stale-screenshot-reporter.d.ts.map +1 -1
- package/lib/stale-screenshot-reporter.js +9 -4
- package/lib/testcontainers/index.d.ts +2 -1
- package/lib/testcontainers/index.d.ts.map +1 -1
- package/lib/testcontainers/index.js +2 -1
- package/lib/testcontainers/mas.d.ts +5 -2
- package/lib/testcontainers/mas.d.ts.map +1 -1
- package/lib/testcontainers/mas.js +14 -2
- package/lib/testcontainers/postgres.d.ts +5 -0
- package/lib/testcontainers/postgres.d.ts.map +1 -0
- package/lib/testcontainers/postgres.js +31 -0
- package/lib/testcontainers/synapse.d.ts +6 -0
- package/lib/testcontainers/synapse.d.ts.map +1 -1
- package/lib/testcontainers/synapse.js +17 -1
- package/package.json +10 -11
- package/playwright-screenshots.sh +33 -126
- package/project.json +44 -0
- package/src/fixtures/services.ts +3 -22
- package/src/fixtures/toasts.ts +109 -0
- package/src/fixtures/user.ts +4 -1
- package/src/flaky-reporter.ts +188 -0
- package/src/stale-screenshot-reporter.ts +9 -5
- package/src/testcontainers/index.ts +2 -0
- package/src/testcontainers/mas.ts +21 -0
- package/src/testcontainers/postgres.ts +40 -0
- package/src/testcontainers/synapse.ts +24 -1
- package/tsconfig.json +8 -3
package/Dockerfile
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
ARG PLAYWRIGHT_VERSION
|
|
2
2
|
FROM mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-noble
|
|
3
|
+
# Expose the argument to this build stage
|
|
4
|
+
ARG PLAYWRIGHT_VERSION
|
|
3
5
|
|
|
4
6
|
WORKDIR /work
|
|
5
7
|
|
|
6
8
|
# fonts-dejavu is needed for the same RTL rendering as on CI
|
|
7
9
|
RUN apt-get update && apt-get -y install docker.io fonts-dejavu
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
RUN corepack enable
|
|
11
|
-
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
|
12
|
-
|
|
13
|
-
# Add environment variable so consumers can skip developer-centric scripts
|
|
14
|
-
ENV PLAYWRIGHT_COMMON_DOCKER=1
|
|
10
|
+
# Install the matching playwright runtime, the docker image only includes browsers
|
|
11
|
+
RUN npm i -g playwright@${PLAYWRIGHT_VERSION}
|
|
15
12
|
|
|
16
13
|
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
# We use `docker-init` as PID 1, which means that the container shuts down correctly on SIGTERM.
|
|
16
|
+
#
|
|
17
|
+
# (The problem is that PID 1 doesn't get default signal handlers, and
|
|
18
|
+
# playwright server doesn't register a SIGTERM handler, so if that ends up as
|
|
19
|
+
# PID 1, then it ignores SIGTERM. Likewise bash doesn't set a SIGTERM handler by default.
|
|
20
|
+
#
|
|
21
|
+
# The easiest solution is to use docker-init, which is in fact `tini` (https://github.com/krallin/tini).
|
|
22
|
+
#
|
|
23
|
+
# See https://github.com/krallin/tini/issues/8#issuecomment-146135930 for a good explanation of all this.)
|
|
24
|
+
ENTRYPOINT ["/usr/bin/docker-init", "/docker-entrypoint.sh"]
|
package/README.md
CHANGED
|
@@ -18,9 +18,9 @@ The API is versioned using semver, with the major version incremented for breaki
|
|
|
18
18
|
|
|
19
19
|
## Copyright & License
|
|
20
20
|
|
|
21
|
-
Copyright (c)
|
|
21
|
+
Copyright (c) 2026 Element Creations Ltd
|
|
22
22
|
|
|
23
|
-
This software is multi licensed by
|
|
23
|
+
This software is multi licensed by Element Creations Ltd (Element). It can be used either:
|
|
24
24
|
|
|
25
25
|
(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
|
|
26
26
|
|
package/docker-entrypoint.sh
CHANGED
package/lib/expect/axe.d.ts
CHANGED
|
@@ -7,5 +7,5 @@ export type Expectations = {
|
|
|
7
7
|
*/
|
|
8
8
|
toHaveNoViolations: (this: ExpectMatcherState, receiver: AxeBuilder) => Promise<MatcherReturnType>;
|
|
9
9
|
};
|
|
10
|
-
export declare const expect: import("
|
|
10
|
+
export declare const expect: import("playwright/test").Expect<Expectations>;
|
|
11
11
|
//# sourceMappingURL=axe.d.ts.map
|
package/lib/expect/axe.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"axe.d.ts","sourceRoot":"","sources":["../../src/expect/axe.ts"],"names":[],"mappings":"AAQA,OAAO,EAA8B,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE/G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,MAAM,YAAY,GAAG;IACvB;;;OAGG;IACH,kBAAkB,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACtG,CAAC;AAEF,eAAO,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"axe.d.ts","sourceRoot":"","sources":["../../src/expect/axe.ts"],"names":[],"mappings":"AAQA,OAAO,EAA8B,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE/G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,MAAM,YAAY,GAAG;IACvB;;;OAGG;IACH,kBAAkB,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,UAAU,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACtG,CAAC;AAEF,eAAO,MAAM,MAAM,gDAgBjB,CAAC"}
|
|
@@ -9,5 +9,5 @@ export type Expectations = {
|
|
|
9
9
|
* Provides an upgrade to the `toHaveScreenshot` expectation.
|
|
10
10
|
* Unfortunately, we can't just extend the existing `toHaveScreenshot` expectation
|
|
11
11
|
*/
|
|
12
|
-
export declare const expect: import("
|
|
12
|
+
export declare const expect: import("playwright/test").Expect<Expectations>;
|
|
13
13
|
//# sourceMappingURL=screenshot.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/expect/screenshot.ts"],"names":[],"mappings":"AAQA,OAAO,EAIH,KAAK,kBAAkB,EACvB,KAAK,OAAO,EACZ,KAAK,IAAI,EACT,KAAK,qCAAqC,EAC1C,KAAK,iBAAiB,EACzB,MAAM,kBAAkB,CAAC;AAa1B,MAAM,WAAW,wBAAyB,SAAQ,qCAAqC;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,YAAY,GAAG;IACvB,iBAAiB,EAAE,CACf,IAAI,EAAE,kBAAkB,EACxB,QAAQ,EAAE,IAAI,GAAG,OAAO,EACxB,IAAI,EAAE,GAAG,MAAM,MAAM,EACrB,OAAO,CAAC,EAAE,wBAAwB,KACjC,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACnC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/expect/screenshot.ts"],"names":[],"mappings":"AAQA,OAAO,EAIH,KAAK,kBAAkB,EACvB,KAAK,OAAO,EACZ,KAAK,IAAI,EACT,KAAK,qCAAqC,EAC1C,KAAK,iBAAiB,EACzB,MAAM,kBAAkB,CAAC;AAa1B,MAAM,WAAW,wBAAyB,SAAQ,qCAAqC;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,YAAY,GAAG;IACvB,iBAAiB,EAAE,CACf,IAAI,EAAE,kBAAkB,EACxB,QAAQ,EAAE,IAAI,GAAG,OAAO,EACxB,IAAI,EAAE,GAAG,MAAM,MAAM,EACrB,OAAO,CAAC,EAAE,wBAAwB,KACjC,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACnC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,gDA+BjB,CAAC"}
|
package/lib/fixtures/axe.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AxeBuilder } from "@axe-core/playwright";
|
|
2
|
-
export declare const test: import("
|
|
2
|
+
export declare const test: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions & {
|
|
3
3
|
/**
|
|
4
4
|
* AxeBuilder instance for the current page
|
|
5
5
|
*/
|
|
6
6
|
axe: AxeBuilder;
|
|
7
|
-
}, import("
|
|
7
|
+
}, import("playwright/test").PlaywrightWorkerArgs & import("playwright/test").PlaywrightWorkerOptions>;
|
|
8
8
|
//# sourceMappingURL=axe.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"axe.d.ts","sourceRoot":"","sources":["../../src/fixtures/axe.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIlD,eAAO,MAAM,IAAI;IACb;;OAEG;SACE,UAAU;
|
|
1
|
+
{"version":3,"file":"axe.d.ts","sourceRoot":"","sources":["../../src/fixtures/axe.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIlD,eAAO,MAAM,IAAI;IACb;;OAEG;SACE,UAAU;sGAMjB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/fixtures/services.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/fixtures/services.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,KAAK,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAE7E,OAAO,EACH,KAAK,aAAa,EAElB,KAAK,2CAA2C,EAChD,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,EAE/B,KAAK,uBAAuB,EAC/B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAM5C;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,aAAa,EAAE,aAAa,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC1B;;OAEG;IACH,aAAa,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACrB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,EAAE,cAAc,CAAC;IAExB;;OAEG;IACH,QAAQ,EAAE,0BAA0B,CAAC;IAErC;;OAEG;IACH,OAAO,EAAE,uBAAuB,CAAC;IAEjC;;OAEG;IACH,WAAW,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC1C;;OAEG;IACH,UAAU,EAAE,0BAA0B,CAAC;IAEvC;;;OAGG;IACH,GAAG,CAAC,EAAE,2CAA2C,CAAC;CACrD;AAED,eAAO,MAAM,IAAI;;gJAsFf,CAAC"}
|
package/lib/fixtures/services.js
CHANGED
|
@@ -5,12 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
|
5
5
|
Please see LICENSE files in the repository root for full details.
|
|
6
6
|
*/
|
|
7
7
|
import { Network } from "testcontainers";
|
|
8
|
-
import { PostgreSqlContainer } from "@testcontainers/postgresql";
|
|
9
8
|
import { SynapseContainer, MailpitContainer, } from "../testcontainers/index.js";
|
|
10
9
|
import { Logger } from "../utils/logger.js";
|
|
11
10
|
// We want to avoid using `mergeTests` in index.ts because it drops useful type information about the fixtures. Instead,
|
|
12
11
|
// we add `axe` into our fixture suite by using its `test` as a base, so that there is a linear hierarchy.
|
|
13
12
|
import { test as base } from "./axe.js";
|
|
13
|
+
import { makePostgres } from "../testcontainers/postgres.js";
|
|
14
14
|
export const test = base.extend({
|
|
15
15
|
logger: [
|
|
16
16
|
// eslint-disable-next-line no-empty-pattern
|
|
@@ -31,27 +31,7 @@ export const test = base.extend({
|
|
|
31
31
|
],
|
|
32
32
|
postgres: [
|
|
33
33
|
async ({ logger, network }, use) => {
|
|
34
|
-
const container = await
|
|
35
|
-
.withNetwork(network)
|
|
36
|
-
.withNetworkAliases("postgres")
|
|
37
|
-
.withLogConsumer(logger.getConsumer("postgres"))
|
|
38
|
-
.withTmpFs({
|
|
39
|
-
"/dev/shm/pgdata/data": "",
|
|
40
|
-
})
|
|
41
|
-
.withEnvironment({
|
|
42
|
-
PG_DATA: "/dev/shm/pgdata/data",
|
|
43
|
-
})
|
|
44
|
-
.withCommand([
|
|
45
|
-
"-c",
|
|
46
|
-
"shared_buffers=128MB",
|
|
47
|
-
"-c",
|
|
48
|
-
`fsync=off`,
|
|
49
|
-
"-c",
|
|
50
|
-
`synchronous_commit=off`,
|
|
51
|
-
"-c",
|
|
52
|
-
"full_page_writes=off",
|
|
53
|
-
])
|
|
54
|
-
.start();
|
|
34
|
+
const container = await makePostgres(network, logger);
|
|
55
35
|
await use(container);
|
|
56
36
|
await container.stop();
|
|
57
37
|
},
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type Locator, type Page } from "@playwright/test";
|
|
2
|
+
export declare const test: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions & {
|
|
3
|
+
axe: import("@axe-core/playwright").AxeBuilder;
|
|
4
|
+
} & import("./services.js").TestFixtures & {
|
|
5
|
+
/**
|
|
6
|
+
* Convenience functions for handling toasts.
|
|
7
|
+
*/
|
|
8
|
+
toasts: Toasts;
|
|
9
|
+
}, import("playwright/test").PlaywrightWorkerArgs & import("playwright/test").PlaywrightWorkerOptions & import("./services.js").WorkerOptions & import("./services.js").Services>;
|
|
10
|
+
declare class Toasts {
|
|
11
|
+
readonly page: Page;
|
|
12
|
+
constructor(page: Page);
|
|
13
|
+
/**
|
|
14
|
+
* Assert that no toasts exist
|
|
15
|
+
*/
|
|
16
|
+
assertNoToasts(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Assert that a toast with the given title exists, and return it
|
|
19
|
+
*
|
|
20
|
+
* @param title - Expected title of the toast
|
|
21
|
+
* @param timeout - Time to retry the assertion for in milliseconds.
|
|
22
|
+
* Defaults to `timeout` in `TestConfig.expect`.
|
|
23
|
+
* @returns the Locator for the matching toast
|
|
24
|
+
*/
|
|
25
|
+
getToast(title: string, timeout?: number): Promise<Locator>;
|
|
26
|
+
/**
|
|
27
|
+
* Find a toast with the given title, if it exists.
|
|
28
|
+
*
|
|
29
|
+
* @param title - Title of the toast.
|
|
30
|
+
* @returns the Locator for the matching toast, or an empty locator if it
|
|
31
|
+
* doesn't exist.
|
|
32
|
+
*/
|
|
33
|
+
getToastIfExists(title: string): Locator;
|
|
34
|
+
/**
|
|
35
|
+
* Accept a toast with the given title. Only works for the first toast in
|
|
36
|
+
* the stack.
|
|
37
|
+
*
|
|
38
|
+
* @param title - Expected title of the toast
|
|
39
|
+
*/
|
|
40
|
+
acceptToast(title: string): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Accept a toast with the given title, if it exists. Only works for the
|
|
43
|
+
* first toast in the stack.
|
|
44
|
+
*
|
|
45
|
+
* @param title - Title of the toast
|
|
46
|
+
*/
|
|
47
|
+
acceptToastIfExists(title: string): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Reject a toast with the given title. Only works for the first toast in
|
|
50
|
+
* the stack.
|
|
51
|
+
*
|
|
52
|
+
* @param title - Expected title of the toast
|
|
53
|
+
*/
|
|
54
|
+
rejectToast(title: string): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Reject a toast with the given title, if it exists. Only works for the
|
|
57
|
+
* first toast in the stack.
|
|
58
|
+
*
|
|
59
|
+
* @param title - Title of the toast
|
|
60
|
+
*/
|
|
61
|
+
rejectToastIfExists(title: string): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=toasts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toasts.d.ts","sourceRoot":"","sources":["../../src/fixtures/toasts.ts"],"names":[],"mappings":"AAOA,OAAO,EAAU,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAQnE,eAAO,MAAM,IAAI;;;IACb;;OAEG;YACK,MAAM;iLAMhB,CAAC;AAEH,cAAM,MAAM;aAC2B,IAAI,EAAE,IAAI;gBAAV,IAAI,EAAE,IAAI;IAE7C;;OAEG;IACU,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5C;;;;;;;OAOG;IACU,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMxE;;;;;;OAMG;IACI,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI/C;;;;;OAKG;IACU,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD;;;;;OAKG;IACU,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9D;;;;;OAKG;IACU,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtD;;;;;OAKG;IACU,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMjE"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Element Creations Ltd.
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
5
|
+
* Please see LICENSE files in the repository root for full details.
|
|
6
|
+
*/
|
|
7
|
+
import { expect } from "@playwright/test";
|
|
8
|
+
// We want to avoid using `mergeTests` in index.ts because it drops useful type
|
|
9
|
+
// information about the fixtures. Instead, we add `services` into our fixture
|
|
10
|
+
// suite by using its `test` as a base, so that there is a linear hierarchy.
|
|
11
|
+
import { test as base } from "./services.js";
|
|
12
|
+
// This fixture provides convenient handling of Element Web's toasts.
|
|
13
|
+
export const test = base.extend({
|
|
14
|
+
toasts: async ({ page }, use) => {
|
|
15
|
+
const toasts = new Toasts(page);
|
|
16
|
+
await use(toasts);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
class Toasts {
|
|
20
|
+
page;
|
|
21
|
+
constructor(page) {
|
|
22
|
+
this.page = page;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Assert that no toasts exist
|
|
26
|
+
*/
|
|
27
|
+
async assertNoToasts() {
|
|
28
|
+
await expect(this.page.locator(".mx_Toast_toast")).not.toBeVisible();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Assert that a toast with the given title exists, and return it
|
|
32
|
+
*
|
|
33
|
+
* @param title - Expected title of the toast
|
|
34
|
+
* @param timeout - Time to retry the assertion for in milliseconds.
|
|
35
|
+
* Defaults to `timeout` in `TestConfig.expect`.
|
|
36
|
+
* @returns the Locator for the matching toast
|
|
37
|
+
*/
|
|
38
|
+
async getToast(title, timeout) {
|
|
39
|
+
const toast = this.getToastIfExists(title);
|
|
40
|
+
await expect(toast).toBeVisible({ timeout });
|
|
41
|
+
return toast;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Find a toast with the given title, if it exists.
|
|
45
|
+
*
|
|
46
|
+
* @param title - Title of the toast.
|
|
47
|
+
* @returns the Locator for the matching toast, or an empty locator if it
|
|
48
|
+
* doesn't exist.
|
|
49
|
+
*/
|
|
50
|
+
getToastIfExists(title) {
|
|
51
|
+
return this.page.locator(".mx_Toast_toast", { hasText: title }).first();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Accept a toast with the given title. Only works for the first toast in
|
|
55
|
+
* the stack.
|
|
56
|
+
*
|
|
57
|
+
* @param title - Expected title of the toast
|
|
58
|
+
*/
|
|
59
|
+
async acceptToast(title) {
|
|
60
|
+
const toast = await this.getToast(title);
|
|
61
|
+
await toast.locator('.mx_Toast_buttons button[data-kind="primary"]').click();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Accept a toast with the given title, if it exists. Only works for the
|
|
65
|
+
* first toast in the stack.
|
|
66
|
+
*
|
|
67
|
+
* @param title - Title of the toast
|
|
68
|
+
*/
|
|
69
|
+
async acceptToastIfExists(title) {
|
|
70
|
+
const toast = this.getToastIfExists(title).locator('.mx_Toast_buttons button[data-kind="primary"]');
|
|
71
|
+
if ((await toast.count()) > 0) {
|
|
72
|
+
await toast.click();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Reject a toast with the given title. Only works for the first toast in
|
|
77
|
+
* the stack.
|
|
78
|
+
*
|
|
79
|
+
* @param title - Expected title of the toast
|
|
80
|
+
*/
|
|
81
|
+
async rejectToast(title) {
|
|
82
|
+
const toast = await this.getToast(title);
|
|
83
|
+
await toast.locator('.mx_Toast_buttons button[data-kind="secondary"]').click();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Reject a toast with the given title, if it exists. Only works for the
|
|
87
|
+
* first toast in the stack.
|
|
88
|
+
*
|
|
89
|
+
* @param title - Title of the toast
|
|
90
|
+
*/
|
|
91
|
+
async rejectToastIfExists(title) {
|
|
92
|
+
const toast = this.getToastIfExists(title).locator('.mx_Toast_buttons button[data-kind="secondary"]');
|
|
93
|
+
if ((await toast.count()) > 0) {
|
|
94
|
+
await toast.click();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
package/lib/fixtures/user.d.ts
CHANGED
|
@@ -2,9 +2,20 @@ import { type Page } from "@playwright/test";
|
|
|
2
2
|
import { type Credentials } from "../utils/api.js";
|
|
3
3
|
/** Adds an initScript to the given page which will populate localStorage appropriately so that Element will use the given credentials. */
|
|
4
4
|
export declare function populateLocalStorageWithCredentials(page: Page, credentials: Credentials): Promise<void>;
|
|
5
|
-
export declare const test: import("
|
|
5
|
+
export declare const test: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions & {
|
|
6
6
|
axe: import("@axe-core/playwright").AxeBuilder;
|
|
7
7
|
} & import("./services.js").TestFixtures & {
|
|
8
|
+
toasts: {
|
|
9
|
+
readonly page: Page;
|
|
10
|
+
assertNoToasts(): Promise<void>;
|
|
11
|
+
getToast(title: string, timeout?: number): Promise<import("playwright-core").Locator>;
|
|
12
|
+
getToastIfExists(title: string): import("playwright-core").Locator;
|
|
13
|
+
acceptToast(title: string): Promise<void>;
|
|
14
|
+
acceptToastIfExists(title: string): Promise<void>;
|
|
15
|
+
rejectToast(title: string): Promise<void>;
|
|
16
|
+
rejectToastIfExists(title: string): Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
} & {
|
|
8
19
|
/**
|
|
9
20
|
* The displayname to use for the user registered in {@link #credentials}.
|
|
10
21
|
*
|
|
@@ -31,5 +42,5 @@ export declare const test: import("@playwright/test").TestType<import("@playwrig
|
|
|
31
42
|
* app.
|
|
32
43
|
*/
|
|
33
44
|
user: Credentials;
|
|
34
|
-
}, import("
|
|
45
|
+
}, import("playwright/test").PlaywrightWorkerArgs & import("playwright/test").PlaywrightWorkerOptions & import("./services.js").WorkerOptions & import("./services.js").Services>;
|
|
35
46
|
//# sourceMappingURL=user.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/fixtures/user.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/fixtures/user.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAO7C,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,0IAA0I;AAC1I,wBAAsB,mCAAmC,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,iBAuB7F;AAED,eAAO,MAAM,IAAI;;;;;;;;;;;;;;IACb;;;;;OAKG;kBACW,MAAM;IAEpB;;;OAGG;iBACU,WAAW;IAExB;;;;;;OAMG;yBACkB,IAAI;IAEzB;;;;OAIG;UACG,WAAW;iLA+BnB,CAAC"}
|
package/lib/fixtures/user.js
CHANGED
|
@@ -6,7 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
|
6
6
|
Please see LICENSE files in the repository root for full details.
|
|
7
7
|
*/
|
|
8
8
|
import { sample, uniqueId } from "lodash-es";
|
|
9
|
-
|
|
9
|
+
// We want to avoid using `mergeTests` in index.ts because it drops useful type
|
|
10
|
+
// information about the fixtures. Instead, we add `toasts` into our fixture
|
|
11
|
+
// suite by using its `test` as a base, so that there is a linear hierarchy.
|
|
12
|
+
import { test as base } from "./toasts.js";
|
|
10
13
|
/** Adds an initScript to the given page which will populate localStorage appropriately so that Element will use the given credentials. */
|
|
11
14
|
export async function populateLocalStorageWithCredentials(page, credentials) {
|
|
12
15
|
await page.addInitScript(({ credentials }) => {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flaky test reporter, creating & updating GitHub issues
|
|
3
|
+
* Only intended to run from within GitHub Actions
|
|
4
|
+
*/
|
|
5
|
+
import type { Reporter, TestCase } from "@playwright/test/reporter";
|
|
6
|
+
declare class FlakyReporter implements Reporter {
|
|
7
|
+
private flakes;
|
|
8
|
+
onTestEnd(test: TestCase): void;
|
|
9
|
+
/**
|
|
10
|
+
* Parse link header to retrieve pagination links
|
|
11
|
+
* @see https://docs.github.com/en/rest/using-the-rest-api/using-pagination-in-the-rest-api?apiVersion=2022-11-28#using-link-headers
|
|
12
|
+
* @param link link header from response or undefined
|
|
13
|
+
* @returns an empty object if link is undefined otherwise returns a map from type to link
|
|
14
|
+
*/
|
|
15
|
+
private parseLinkHeader;
|
|
16
|
+
/**
|
|
17
|
+
* Fetch all flaky test issues that were updated since Jan-1-2024
|
|
18
|
+
* @returns A promise that resolves to a list of issues
|
|
19
|
+
*/
|
|
20
|
+
getAllIssues(): Promise<any[]>;
|
|
21
|
+
onExit(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
export default FlakyReporter;
|
|
24
|
+
//# sourceMappingURL=flaky-reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flaky-reporter.d.ts","sourceRoot":"","sources":["../src/flaky-reporter.ts"],"names":[],"mappings":"AAQA;;;GAGG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAuBpE,cAAM,aAAc,YAAW,QAAQ;IACnC,OAAO,CAAC,MAAM,CAAiC;IAExC,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IA2CtC;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAevB;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAuBvB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAuDvC;AAED,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2024 New Vector Ltd.
|
|
3
|
+
Copyright 2024 The Matrix.org Foundation C.I.C.
|
|
4
|
+
|
|
5
|
+
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
6
|
+
Please see LICENSE files in the repository root for full details.
|
|
7
|
+
*/
|
|
8
|
+
const REPO = "element-hq/element-web";
|
|
9
|
+
const LABEL = "Z-Flaky-Test";
|
|
10
|
+
const ISSUE_TITLE_PREFIX = "Flaky playwright test: ";
|
|
11
|
+
const ANSI_COLOUR_REGEX = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
|
12
|
+
// We see quite a few test flakes which are caused by the app exploding
|
|
13
|
+
// so we have some magic strings we check the logs for to better track the flake with its cause
|
|
14
|
+
const SPECIAL_CASES = {
|
|
15
|
+
"ChunkLoadError": "ChunkLoadError",
|
|
16
|
+
"Unreachable code should not be executed": "Rust crypto panic",
|
|
17
|
+
"Out of bounds memory access": "Rust crypto memory error",
|
|
18
|
+
};
|
|
19
|
+
class FlakyReporter {
|
|
20
|
+
flakes = new Map();
|
|
21
|
+
onTestEnd(test) {
|
|
22
|
+
// Ignores flakes on Dendrite and Pinecone as they have their own flakes we do not track
|
|
23
|
+
if (["Dendrite", "Pinecone"].includes(test.parent.project().name))
|
|
24
|
+
return;
|
|
25
|
+
if (test.outcome() === "flaky") {
|
|
26
|
+
const failures = [];
|
|
27
|
+
const timedOutRuns = test.results.filter((result) => result.status === "timedOut");
|
|
28
|
+
const pageLogs = timedOutRuns.flatMap((result) => result.attachments.filter((attachment) => attachment.name.startsWith("page-")));
|
|
29
|
+
// If a test failed due to a systemic fault then the test is not flaky, the app is, record it as such.
|
|
30
|
+
const specialCases = Object.keys(SPECIAL_CASES).filter((log) => pageLogs.some((attachment) => attachment.name.startsWith("page-") && attachment.body?.includes(log)));
|
|
31
|
+
if (specialCases.length > 0) {
|
|
32
|
+
failures.push(...specialCases.map((specialCase) => SPECIAL_CASES[specialCase]));
|
|
33
|
+
}
|
|
34
|
+
// Check for fixtures failing to set up
|
|
35
|
+
const errorMessages = timedOutRuns
|
|
36
|
+
.map((r) => r.error?.message?.replace(ANSI_COLOUR_REGEX, ""))
|
|
37
|
+
.filter(Boolean);
|
|
38
|
+
for (const error of errorMessages) {
|
|
39
|
+
if (error.startsWith("Fixture") && error.endsWith("exceeded during setup.")) {
|
|
40
|
+
failures.push(error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (failures.length < 1) {
|
|
44
|
+
failures.push(`${test.location.file.split("playwright/e2e/")[1]}: ${test.title}`);
|
|
45
|
+
}
|
|
46
|
+
for (const title of failures) {
|
|
47
|
+
if (!this.flakes.has(title)) {
|
|
48
|
+
this.flakes.set(title, []);
|
|
49
|
+
}
|
|
50
|
+
this.flakes.get(title).push(test);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Parse link header to retrieve pagination links
|
|
56
|
+
* @see https://docs.github.com/en/rest/using-the-rest-api/using-pagination-in-the-rest-api?apiVersion=2022-11-28#using-link-headers
|
|
57
|
+
* @param link link header from response or undefined
|
|
58
|
+
* @returns an empty object if link is undefined otherwise returns a map from type to link
|
|
59
|
+
*/
|
|
60
|
+
parseLinkHeader(link) {
|
|
61
|
+
/**
|
|
62
|
+
* link looks like:
|
|
63
|
+
* <https://api.github.com/repositories/1300192/issues?page=2>; rel="prev", <https://api.github.com/repositories/1300192/issues?page=4>;
|
|
64
|
+
*/
|
|
65
|
+
const map = {};
|
|
66
|
+
if (!link)
|
|
67
|
+
return map;
|
|
68
|
+
const matches = link.matchAll(/(<(?<link>.+?)>; rel="(?<type>.+?)")/g);
|
|
69
|
+
for (const match of matches) {
|
|
70
|
+
const { link, type } = match.groups;
|
|
71
|
+
map[type] = link;
|
|
72
|
+
}
|
|
73
|
+
return map;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Fetch all flaky test issues that were updated since Jan-1-2024
|
|
77
|
+
* @returns A promise that resolves to a list of issues
|
|
78
|
+
*/
|
|
79
|
+
async getAllIssues() {
|
|
80
|
+
const issues = [];
|
|
81
|
+
const { GITHUB_TOKEN, GITHUB_API_URL } = process.env;
|
|
82
|
+
// See https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues
|
|
83
|
+
let url = `${GITHUB_API_URL}/repos/${REPO}/issues?labels=${LABEL}&state=all&per_page=100&sort=updated&since=2024-01-01`;
|
|
84
|
+
const headers = {
|
|
85
|
+
Authorization: `Bearer ${GITHUB_TOKEN}`,
|
|
86
|
+
Accept: "application / vnd.github + json",
|
|
87
|
+
};
|
|
88
|
+
while (url) {
|
|
89
|
+
// Fetch issues and add to list
|
|
90
|
+
const issuesResponse = await fetch(url, { headers });
|
|
91
|
+
const fetchedIssues = await issuesResponse.json();
|
|
92
|
+
issues.push(...fetchedIssues);
|
|
93
|
+
// Get the next link for fetching more results
|
|
94
|
+
const linkHeader = issuesResponse.headers.get("Link");
|
|
95
|
+
const parsed = this.parseLinkHeader(linkHeader);
|
|
96
|
+
url = parsed.next;
|
|
97
|
+
}
|
|
98
|
+
return issues;
|
|
99
|
+
}
|
|
100
|
+
async onExit() {
|
|
101
|
+
if (this.flakes.size === 0) {
|
|
102
|
+
console.log("No flakes found");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
console.log("Found flakes: ");
|
|
106
|
+
for (const flake of this.flakes) {
|
|
107
|
+
console.log(flake);
|
|
108
|
+
}
|
|
109
|
+
const { GITHUB_TOKEN, GITHUB_API_URL, GITHUB_SERVER_URL, GITHUB_REPOSITORY, GITHUB_RUN_ID } = process.env;
|
|
110
|
+
if (!GITHUB_TOKEN)
|
|
111
|
+
return;
|
|
112
|
+
const issues = await this.getAllIssues();
|
|
113
|
+
for (const [flake, results] of this.flakes) {
|
|
114
|
+
const title = ISSUE_TITLE_PREFIX + "`" + flake + "`";
|
|
115
|
+
const existingIssue = issues.find((issue) => issue.title === title);
|
|
116
|
+
const headers = { Authorization: `Bearer ${GITHUB_TOKEN}` };
|
|
117
|
+
const body = `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}`;
|
|
118
|
+
const labels = [LABEL, ...results.map((test) => `${LABEL}-${test.parent.project()?.name}`)];
|
|
119
|
+
if (existingIssue) {
|
|
120
|
+
console.log(`Found issue ${existingIssue.number} for ${flake}, adding comment...`);
|
|
121
|
+
// Ensure that the test is open
|
|
122
|
+
await fetch(existingIssue.url, {
|
|
123
|
+
method: "PATCH",
|
|
124
|
+
headers,
|
|
125
|
+
body: JSON.stringify({ state: "open" }),
|
|
126
|
+
});
|
|
127
|
+
await fetch(`${existingIssue.url}/labels`, {
|
|
128
|
+
method: "POST",
|
|
129
|
+
headers,
|
|
130
|
+
body: JSON.stringify({ labels }),
|
|
131
|
+
});
|
|
132
|
+
await fetch(`${existingIssue.url}/comments`, {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers,
|
|
135
|
+
body: JSON.stringify({ body }),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
console.log(`Creating new issue for ${flake}...`);
|
|
140
|
+
await fetch(`${GITHUB_API_URL}/repos/${REPO}/issues`, {
|
|
141
|
+
method: "POST",
|
|
142
|
+
headers,
|
|
143
|
+
body: JSON.stringify({
|
|
144
|
+
title,
|
|
145
|
+
body,
|
|
146
|
+
labels: [...labels],
|
|
147
|
+
}),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export default FlakyReporter;
|
package/lib/index.d.ts
CHANGED
|
@@ -41,6 +41,17 @@ export interface TestFixtures {
|
|
|
41
41
|
export declare const test: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions & {
|
|
42
42
|
axe: import("@axe-core/playwright").AxeBuilder;
|
|
43
43
|
} & import("./fixtures/services.js").TestFixtures & {
|
|
44
|
+
toasts: {
|
|
45
|
+
readonly page: import("playwright-core").Page;
|
|
46
|
+
assertNoToasts(): Promise<void>;
|
|
47
|
+
getToast(title: string, timeout?: number): Promise<import("playwright-core").Locator>;
|
|
48
|
+
getToastIfExists(title: string): import("playwright-core").Locator;
|
|
49
|
+
acceptToast(title: string): Promise<void>;
|
|
50
|
+
acceptToastIfExists(title: string): Promise<void>;
|
|
51
|
+
rejectToast(title: string): Promise<void>;
|
|
52
|
+
rejectToastIfExists(title: string): Promise<void>;
|
|
53
|
+
};
|
|
54
|
+
} & {
|
|
44
55
|
displayName?: string;
|
|
45
56
|
credentials: import("./utils/api.js").Credentials;
|
|
46
57
|
pageWithCredentials: import("playwright-core").Page;
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,MAAM,IAAI,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAK/E,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAEnC,OAAO,EAAE,mCAAmC,EAAE,MAAM,oBAAoB,CAAC;AAQzE,MAAM,WAAW,MAAO,SAAQ,UAAU;IACtC,qBAAqB,EAAE;QACnB,cAAc,CAAC,EAAE;YACb,QAAQ,EAAE,MAAM,CAAC;YACjB,WAAW,CAAC,EAAE,MAAM,CAAC;SACxB,CAAC;QACF,mBAAmB,CAAC,EAAE;YAClB,QAAQ,EAAE,MAAM,CAAC;YACjB,WAAW,CAAC,EAAE,MAAM,CAAC;SACxB,CAAC;KACL,CAAC;IACF,yBAAyB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpD,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAGD,eAAO,MAAM,WAAW,EAAE,OAAO,CAAC,MAAM,CAevC,CAAC;AAEF,MAAM,WAAW,YAAY;IACzB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC;IAEpC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;IACzB;;;;;;;;OAQG;IACH,kBAAkB,EAAE,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,IAAI
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,MAAM,IAAI,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAK/E,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAEnC,OAAO,EAAE,mCAAmC,EAAE,MAAM,oBAAoB,CAAC;AAQzE,MAAM,WAAW,MAAO,SAAQ,UAAU;IACtC,qBAAqB,EAAE;QACnB,cAAc,CAAC,EAAE;YACb,QAAQ,EAAE,MAAM,CAAC;YACjB,WAAW,CAAC,EAAE,MAAM,CAAC;SACxB,CAAC;QACF,mBAAmB,CAAC,EAAE;YAClB,QAAQ,EAAE,MAAM,CAAC;YACjB,WAAW,CAAC,EAAE,MAAM,CAAC;SACxB,CAAC;KACL,CAAC;IACF,yBAAyB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpD,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAGD,eAAO,MAAM,WAAW,EAAE,OAAO,CAAC,MAAM,CAevC,CAAC;AAEF,MAAM,WAAW,YAAY;IACzB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC;IAEpC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;IACzB;;;;;;;;OAQG;IACH,kBAAkB,EAAE,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;kNAmBf,CAAC;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,wBAAwB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stale-screenshot-reporter.d.ts","sourceRoot":"","sources":["../src/stale-screenshot-reporter.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,UAAU,gBAAgB,CAAC;AAExC,cAAM,uBAAwB,YAAW,QAAQ;IAC7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,OAAO,CAAQ;IAEhB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAMjC,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IActC,OAAO,CAAC,KAAK;
|
|
1
|
+
{"version":3,"file":"stale-screenshot-reporter.d.ts","sourceRoot":"","sources":["../src/stale-screenshot-reporter.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,UAAU,gBAAgB,CAAC;AAExC,cAAM,uBAAwB,YAAW,QAAQ;IAC7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,OAAO,CAAQ;IAEhB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAMjC,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IActC,OAAO,CAAC,KAAK;YAQC,qBAAqB;IAgCtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAWvC;AAED,eAAe,uBAAuB,CAAC"}
|
|
@@ -45,10 +45,7 @@ class StaleScreenshotReporter {
|
|
|
45
45
|
console.error(msg, file);
|
|
46
46
|
this.success = false;
|
|
47
47
|
}
|
|
48
|
-
async
|
|
49
|
-
if (this.failing.size) {
|
|
50
|
-
console.error(`${this.failing.size} tests failed, skipping stale screenshot reporter.`);
|
|
51
|
-
}
|
|
48
|
+
async checkStaleScreenshots() {
|
|
52
49
|
if (!this.snapshotRoots.size) {
|
|
53
50
|
this.error("No snapshot directories found, did you set the snapshotDir in your Playwright config?", "");
|
|
54
51
|
return;
|
|
@@ -73,6 +70,14 @@ class StaleScreenshotReporter {
|
|
|
73
70
|
this.error("Stale screenshot file", screenshot);
|
|
74
71
|
}
|
|
75
72
|
}
|
|
73
|
+
}
|
|
74
|
+
async onExit() {
|
|
75
|
+
if (this.failing.size) {
|
|
76
|
+
this.error(`${this.failing.size} tests failed, skipping stale screenshot reporter.`, "");
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
await this.checkStaleScreenshots();
|
|
80
|
+
}
|
|
76
81
|
if (!this.success) {
|
|
77
82
|
process.exit(1);
|
|
78
83
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { PostgreSqlContainer, StartedPostgreSqlContainer } from "@testcontainers/postgresql";
|
|
2
|
+
export { makePostgres } from "./postgres.js";
|
|
2
3
|
export type { HomeserverInstance, HomeserverContainer, StartedHomeserverContainer } from "./HomeserverContainer.js";
|
|
3
4
|
export { type SynapseConfig, SynapseContainer, StartedSynapseContainer } from "./synapse.js";
|
|
4
|
-
export { type MasConfig, MatrixAuthenticationServiceContainer, StartedMatrixAuthenticationServiceContainer, } from "./mas.js";
|
|
5
|
+
export { type MasConfig, MatrixAuthenticationServiceContainer, StartedMatrixAuthenticationServiceContainer, makeMas, } from "./mas.js";
|
|
5
6
|
export { type MailpitClient, MailpitContainer, StartedMailpitContainer } from "./mailpit.js";
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testcontainers/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7F,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,EAAE,KAAK,aAAa,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC7F,OAAO,EACH,KAAK,SAAS,EACd,oCAAoC,EACpC,2CAA2C,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testcontainers/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,EAAE,KAAK,aAAa,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC7F,OAAO,EACH,KAAK,SAAS,EACd,oCAAoC,EACpC,2CAA2C,EAC3C,OAAO,GACV,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,KAAK,aAAa,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC"}
|