@element-hq/element-web-playwright-common 3.1.0 → 4.0.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 CHANGED
@@ -6,10 +6,17 @@ ARG PLAYWRIGHT_VERSION
6
6
  WORKDIR /work
7
7
 
8
8
  # fonts-dejavu is needed for the same RTL rendering as on CI
9
- RUN apt-get update && apt-get -y install docker.io fonts-dejavu
9
+ RUN apt-get update && \
10
+ apt-get -y install docker.io fonts-dejavu && \
11
+ apt-get purge -y --auto-remove && \
12
+ rm -rf /var/lib/apt/lists/*
13
+
10
14
  # Install the matching playwright runtime, the docker image only includes browsers
11
15
  RUN npm i -g playwright@${PLAYWRIGHT_VERSION}
12
16
 
17
+ # switch to pwuser
18
+ USER 1001:1001
19
+
13
20
  COPY docker-entrypoint.sh /docker-entrypoint.sh
14
21
 
15
22
  # We use `docker-init` as PID 1, which means that the container shuts down correctly on SIGTERM.
@@ -11,52 +11,61 @@ declare class Toasts {
11
11
  readonly page: Page;
12
12
  constructor(page: Page);
13
13
  /**
14
- * Assert that no toasts exist
14
+ * Assert that no toasts exist.
15
15
  */
16
16
  assertNoToasts(): Promise<void>;
17
17
  /**
18
- * Assert that a toast with the given title exists, and return it
18
+ * Return the toast with the supplied title. Fail or return null if it does
19
+ * not exist.
19
20
  *
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.
21
+ * If `required` is false, you should supply a relatively short `timeout`
22
+ * (e.g. 2000, meaning 2 seconds) to prevent your test taking too long.
28
23
  *
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.
24
+ * @param title - Expected title of the toast.
25
+ * @param timeout - Time in ms before we give up and decide the toast does
26
+ * not exist. If `required` is true, defaults to `timeout`
27
+ * in `TestConfig.expect`. Otherwise, defaults to 2000 (2
28
+ * seconds).
29
+ * @param required - If true, fail the test (throw an exception) if the
30
+ * toast is not visible. Otherwise, just return null if
31
+ * the toast is not visible.
32
+ * @returns the Locator for the matching toast, or null if it is not
33
+ * visible. (null will only be returned if `required` is false.)
32
34
  */
33
- getToastIfExists(title: string): Locator;
35
+ getToast(title: string, timeout?: number, required?: true): Promise<Locator>;
36
+ getToast(title: string, timeout: number | undefined, required: false): Promise<Locator | null>;
34
37
  /**
35
- * Accept a toast with the given title. Only works for the first toast in
36
- * the stack.
38
+ * Accept the toast with the supplied title, or fail if it does not exist.
39
+ *
40
+ * Only works if this toast is at the top of the stack of toasts.
37
41
  *
38
- * @param title - Expected title of the toast
42
+ * @param title - Expected title of the toast.
39
43
  */
40
44
  acceptToast(title: string): Promise<void>;
41
45
  /**
42
- * Accept a toast with the given title, if it exists. Only works for the
43
- * first toast in the stack.
46
+ * Accept the toast with the supplied title, if it exists, or return after 2
47
+ * seconds if it is not found.
44
48
  *
45
- * @param title - Title of the toast
49
+ * Only works if this toast is at the top of the stack of toasts.
50
+ *
51
+ * @param title - Expected title of the toast.
46
52
  */
47
53
  acceptToastIfExists(title: string): Promise<void>;
48
54
  /**
49
- * Reject a toast with the given title. Only works for the first toast in
50
- * the stack.
55
+ * Reject the toast with the supplied title, or fail if it does not exist.
56
+ *
57
+ * Only works if this toast is at the top of the stack of toasts.
51
58
  *
52
- * @param title - Expected title of the toast
59
+ * @param title - Expected title of the toast.
53
60
  */
54
61
  rejectToast(title: string): Promise<void>;
55
62
  /**
56
- * Reject a toast with the given title, if it exists. Only works for the
57
- * first toast in the stack.
63
+ * Reject the toast with the supplied title, if it exists, or return after 2
64
+ * seconds if it is not found.
65
+ *
66
+ * Only works if this toast is at the top of the stack of toasts.
58
67
  *
59
- * @param title - Title of the toast
68
+ * @param title - Expected title of the toast.
60
69
  */
61
70
  rejectToastIfExists(title: string): Promise<void>;
62
71
  }
@@ -1 +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"}
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;;;;;;;;;;;;;;;;;OAiBG;IACU,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAC5E,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,KAAK,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAqB3G;;;;;;OAMG;IACU,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD;;;;;;;OAOG;IACU,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9D;;;;;;OAMG;IACU,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD;;;;;;;OAOG;IACU,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGjE"}
@@ -22,76 +22,103 @@ class Toasts {
22
22
  this.page = page;
23
23
  }
24
24
  /**
25
- * Assert that no toasts exist
25
+ * Assert that no toasts exist.
26
26
  */
27
27
  async assertNoToasts() {
28
28
  await expect(this.page.locator(".mx_Toast_toast")).not.toBeVisible();
29
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;
30
+ async getToast(title, timeout, required = true) {
31
+ const toast = this.page.locator(".mx_Toast_toast", { hasText: title }).first();
32
+ if (required) {
33
+ await expect(toast).toBeVisible({ timeout });
34
+ return toast;
35
+ }
36
+ else {
37
+ // If we don't set a timeout, waitFor will wait forever. Since
38
+ // required is false, we definitely don't want to wait forever.
39
+ timeout = timeout ?? 2000;
40
+ try {
41
+ await toast.waitFor({ state: "visible", timeout });
42
+ return toast;
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
42
48
  }
43
49
  /**
44
- * Find a toast with the given title, if it exists.
50
+ * Accept the toast with the supplied title, or fail if it does not exist.
45
51
  *
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.
52
+ * Only works if this toast is at the top of the stack of toasts.
56
53
  *
57
- * @param title - Expected title of the toast
54
+ * @param title - Expected title of the toast.
58
55
  */
59
56
  async acceptToast(title) {
60
- const toast = await this.getToast(title);
61
- await toast.locator('.mx_Toast_buttons button[data-kind="primary"]').click();
57
+ return await clickToastButton(this, title, "primary");
62
58
  }
63
59
  /**
64
- * Accept a toast with the given title, if it exists. Only works for the
65
- * first toast in the stack.
60
+ * Accept the toast with the supplied title, if it exists, or return after 2
61
+ * seconds if it is not found.
62
+ *
63
+ * Only works if this toast is at the top of the stack of toasts.
66
64
  *
67
- * @param title - Title of the toast
65
+ * @param title - Expected title of the toast.
68
66
  */
69
67
  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
- }
68
+ return await clickToastButton(this, title, "primary", 2000, false);
74
69
  }
75
70
  /**
76
- * Reject a toast with the given title. Only works for the first toast in
77
- * the stack.
71
+ * Reject the toast with the supplied title, or fail if it does not exist.
72
+ *
73
+ * Only works if this toast is at the top of the stack of toasts.
78
74
  *
79
- * @param title - Expected title of the toast
75
+ * @param title - Expected title of the toast.
80
76
  */
81
77
  async rejectToast(title) {
82
- const toast = await this.getToast(title);
83
- await toast.locator('.mx_Toast_buttons button[data-kind="secondary"]').click();
78
+ return await clickToastButton(this, title, "secondary");
84
79
  }
85
80
  /**
86
- * Reject a toast with the given title, if it exists. Only works for the
87
- * first toast in the stack.
81
+ * Reject the toast with the supplied title, if it exists, or return after 2
82
+ * seconds if it is not found.
88
83
  *
89
- * @param title - Title of the toast
84
+ * Only works if this toast is at the top of the stack of toasts.
85
+ *
86
+ * @param title - Expected title of the toast.
90
87
  */
91
88
  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
- }
89
+ return await clickToastButton(this, title, "secondary", 2000, false);
90
+ }
91
+ }
92
+ /**
93
+ * Find the toast with the supplied title and click a button on it.
94
+ *
95
+ * Only works if this toast is at the top of the stack of toasts.
96
+ *
97
+ * If `required` is false, you should supply a relatively short `timeout`
98
+ * (e.g. 2000, meaning 2 seconds) to prevent your test taking too long.
99
+ *
100
+ * @param toasts - A Toasts instance.
101
+ * @param title - Expected title of the toast.
102
+ * @param button - Which button to click on the toast. Allowed values are
103
+ * "primary", which will accept the toast, or "secondary",
104
+ * which will reject it.
105
+ * @param timeout - Time in ms before we give up and decide the toast does
106
+ * not exist. If `required` is true, defaults to `timeout`
107
+ * in `TestConfig.expect`. Otherwise, defaults to 2000 (2
108
+ * seconds).
109
+ * @param required - If true, fail the test (throw an exception) if the
110
+ * toast is not visible. Otherwise, just return after
111
+ * `timeout` if the toast is not visible.
112
+ */
113
+ async function clickToastButton(toasts, title, button, timeout, required = true) {
114
+ let toast;
115
+ if (required) {
116
+ toast = await toasts.getToast(title, timeout, true);
117
+ }
118
+ else {
119
+ toast = await toasts.getToast(title, timeout, false);
120
+ }
121
+ if (toast) {
122
+ await toast.locator(`.mx_Toast_buttons button[data-kind="${button}"]`).click();
96
123
  }
97
124
  }
@@ -8,8 +8,8 @@ export declare const test: import("playwright/test").TestType<import("playwright
8
8
  toasts: {
9
9
  readonly page: Page;
10
10
  assertNoToasts(): Promise<void>;
11
- getToast(title: string, timeout?: number): Promise<import("playwright-core").Locator>;
12
- getToastIfExists(title: string): import("playwright-core").Locator;
11
+ getToast(title: string, timeout?: number, required?: true): Promise<import("playwright-core").Locator>;
12
+ getToast(title: string, timeout: number | undefined, required: false): Promise<import("playwright-core").Locator | null>;
13
13
  acceptToast(title: string): Promise<void>;
14
14
  acceptToastIfExists(title: string): Promise<void>;
15
15
  rejectToast(title: string): Promise<void>;
package/lib/index.d.ts CHANGED
@@ -44,8 +44,8 @@ export declare const test: import("playwright/test").TestType<import("playwright
44
44
  toasts: {
45
45
  readonly page: import("playwright-core").Page;
46
46
  assertNoToasts(): Promise<void>;
47
- getToast(title: string, timeout?: number): Promise<import("playwright-core").Locator>;
48
- getToastIfExists(title: string): import("playwright-core").Locator;
47
+ getToast(title: string, timeout?: number, required?: true): Promise<import("playwright-core").Locator>;
48
+ getToast(title: string, timeout: number | undefined, required: false): Promise<import("playwright-core").Locator | null>;
49
49
  acceptToast(title: string): Promise<void>;
50
50
  acceptToastIfExists(title: string): Promise<void>;
51
51
  rejectToast(title: string): Promise<void>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@element-hq/element-web-playwright-common",
3
3
  "type": "module",
4
- "version": "3.1.0",
4
+ "version": "4.0.0",
5
5
  "license": "SEE LICENSE IN README.md",
6
6
  "repository": {
7
7
  "type": "git",
@@ -24,8 +24,7 @@
24
24
  "devDependencies": {
25
25
  "@element-hq/element-web-module-api": "workspace:*",
26
26
  "@types/lodash-es": "^4.17.12",
27
- "typescript": "^5.8.2",
28
- "wait-on": "^9.0.4"
27
+ "typescript": "^6.0.0"
29
28
  },
30
29
  "dependencies": {
31
30
  "@axe-core/playwright": "^4.10.1",
@@ -35,6 +34,7 @@
35
34
  "mailpit-api": "^1.2.0",
36
35
  "strip-ansi": "^7.1.0",
37
36
  "testcontainers": "^11.0.0",
37
+ "wait-on": "^9.0.4",
38
38
  "yaml": "^2.7.0"
39
39
  },
40
40
  "peerDependencies": {
@@ -9,7 +9,7 @@ SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
9
9
  function build_image() {
10
10
  local IMAGE_NAME="$1"
11
11
 
12
- echo "Building $IMAGE_NAME image in $SCRIPT_DIR"
12
+ echo "playwright-screenshots: Building $IMAGE_NAME image in $SCRIPT_DIR"
13
13
  docker build -t "$IMAGE_NAME" --build-arg "PLAYWRIGHT_VERSION=${IMAGE_NAME#*:}" "$SCRIPT_DIR"
14
14
  }
15
15
 
@@ -34,16 +34,19 @@ CONTAINER=$(docker run --network=host -v /tmp:/tmp --rm -d -e PORT="$WS_PORT" "$
34
34
  # Set up an exit trap to clean up the docker container
35
35
  clean_up() {
36
36
  ARG=$?
37
- echo "Stopping playwright-server"
37
+ echo "playwright-screenshots: Stopping playwright-server"
38
38
  docker stop "$CONTAINER" > /dev/null
39
39
  exit $ARG
40
40
  }
41
41
  trap clean_up EXIT
42
42
 
43
43
  # Wait for playwright-server to be ready
44
- echo "Waiting for playwright-server"
44
+ echo "playwright-screenshots: Waiting for playwright-server"
45
45
  pnpm --dir "$SCRIPT_DIR" exec wait-on "tcp:$WS_PORT"
46
46
 
47
+ # Playwright seems to overwrite the last line from the console, so add an
48
+ # extra newline to make sure this doesn't get lost.
49
+ echo -e "playwright-screenshots: Running '$@'\n"
50
+
47
51
  # Run the test we were given, setting PW_TEST_CONNECT_WS_ENDPOINT accordingly
48
- echo "Running '$@'"
49
52
  PW_TEST_CONNECT_WS_ENDPOINT="http://localhost:$WS_PORT" "$@"
@@ -29,81 +29,135 @@ class Toasts {
29
29
  public constructor(public readonly page: Page) {}
30
30
 
31
31
  /**
32
- * Assert that no toasts exist
32
+ * Assert that no toasts exist.
33
33
  */
34
34
  public async assertNoToasts(): Promise<void> {
35
35
  await expect(this.page.locator(".mx_Toast_toast")).not.toBeVisible();
36
36
  }
37
37
 
38
38
  /**
39
- * Assert that a toast with the given title exists, and return it
39
+ * Return the toast with the supplied title. Fail or return null if it does
40
+ * not exist.
40
41
  *
41
- * @param title - Expected title of the toast
42
- * @param timeout - Time to retry the assertion for in milliseconds.
43
- * Defaults to `timeout` in `TestConfig.expect`.
44
- * @returns the Locator for the matching toast
45
- */
46
- public async getToast(title: string, timeout?: number): Promise<Locator> {
47
- const toast = this.getToastIfExists(title);
48
- await expect(toast).toBeVisible({ timeout });
49
- return toast;
50
- }
51
-
52
- /**
53
- * Find a toast with the given title, if it exists.
42
+ * If `required` is false, you should supply a relatively short `timeout`
43
+ * (e.g. 2000, meaning 2 seconds) to prevent your test taking too long.
54
44
  *
55
- * @param title - Title of the toast.
56
- * @returns the Locator for the matching toast, or an empty locator if it
57
- * doesn't exist.
45
+ * @param title - Expected title of the toast.
46
+ * @param timeout - Time in ms before we give up and decide the toast does
47
+ * not exist. If `required` is true, defaults to `timeout`
48
+ * in `TestConfig.expect`. Otherwise, defaults to 2000 (2
49
+ * seconds).
50
+ * @param required - If true, fail the test (throw an exception) if the
51
+ * toast is not visible. Otherwise, just return null if
52
+ * the toast is not visible.
53
+ * @returns the Locator for the matching toast, or null if it is not
54
+ * visible. (null will only be returned if `required` is false.)
58
55
  */
59
- public getToastIfExists(title: string): Locator {
60
- return this.page.locator(".mx_Toast_toast", { hasText: title }).first();
56
+ public async getToast(title: string, timeout?: number, required?: true): Promise<Locator>;
57
+ public async getToast(title: string, timeout: number | undefined, required: false): Promise<Locator | null>;
58
+ public async getToast(title: string, timeout?: number, required = true): Promise<Locator | null> {
59
+ const toast = this.page.locator(".mx_Toast_toast", { hasText: title }).first();
60
+
61
+ if (required) {
62
+ await expect(toast).toBeVisible({ timeout });
63
+ return toast;
64
+ } else {
65
+ // If we don't set a timeout, waitFor will wait forever. Since
66
+ // required is false, we definitely don't want to wait forever.
67
+ timeout = timeout ?? 2000;
68
+
69
+ try {
70
+ await toast.waitFor({ state: "visible", timeout });
71
+ return toast;
72
+ } catch {
73
+ return null;
74
+ }
75
+ }
61
76
  }
62
77
 
63
78
  /**
64
- * Accept a toast with the given title. Only works for the first toast in
65
- * the stack.
79
+ * Accept the toast with the supplied title, or fail if it does not exist.
80
+ *
81
+ * Only works if this toast is at the top of the stack of toasts.
66
82
  *
67
- * @param title - Expected title of the toast
83
+ * @param title - Expected title of the toast.
68
84
  */
69
85
  public async acceptToast(title: string): Promise<void> {
70
- const toast = await this.getToast(title);
71
- await toast.locator('.mx_Toast_buttons button[data-kind="primary"]').click();
86
+ return await clickToastButton(this, title, "primary");
72
87
  }
88
+
73
89
  /**
74
- * Accept a toast with the given title, if it exists. Only works for the
75
- * first toast in the stack.
90
+ * Accept the toast with the supplied title, if it exists, or return after 2
91
+ * seconds if it is not found.
76
92
  *
77
- * @param title - Title of the toast
93
+ * Only works if this toast is at the top of the stack of toasts.
94
+ *
95
+ * @param title - Expected title of the toast.
78
96
  */
79
97
  public async acceptToastIfExists(title: string): Promise<void> {
80
- const toast = this.getToastIfExists(title).locator('.mx_Toast_buttons button[data-kind="primary"]');
81
- if ((await toast.count()) > 0) {
82
- await toast.click();
83
- }
98
+ return await clickToastButton(this, title, "primary", 2000, false);
84
99
  }
85
100
 
86
101
  /**
87
- * Reject a toast with the given title. Only works for the first toast in
88
- * the stack.
102
+ * Reject the toast with the supplied title, or fail if it does not exist.
103
+ *
104
+ * Only works if this toast is at the top of the stack of toasts.
89
105
  *
90
- * @param title - Expected title of the toast
106
+ * @param title - Expected title of the toast.
91
107
  */
92
108
  public async rejectToast(title: string): Promise<void> {
93
- const toast = await this.getToast(title);
94
- await toast.locator('.mx_Toast_buttons button[data-kind="secondary"]').click();
109
+ return await clickToastButton(this, title, "secondary");
95
110
  }
96
111
 
97
112
  /**
98
- * Reject a toast with the given title, if it exists. Only works for the
99
- * first toast in the stack.
113
+ * Reject the toast with the supplied title, if it exists, or return after 2
114
+ * seconds if it is not found.
100
115
  *
101
- * @param title - Title of the toast
116
+ * Only works if this toast is at the top of the stack of toasts.
117
+ *
118
+ * @param title - Expected title of the toast.
102
119
  */
103
120
  public async rejectToastIfExists(title: string): Promise<void> {
104
- const toast = this.getToastIfExists(title).locator('.mx_Toast_buttons button[data-kind="secondary"]');
105
- if ((await toast.count()) > 0) {
106
- await toast.click();
107
- }
121
+ return await clickToastButton(this, title, "secondary", 2000, false);
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Find the toast with the supplied title and click a button on it.
127
+ *
128
+ * Only works if this toast is at the top of the stack of toasts.
129
+ *
130
+ * If `required` is false, you should supply a relatively short `timeout`
131
+ * (e.g. 2000, meaning 2 seconds) to prevent your test taking too long.
132
+ *
133
+ * @param toasts - A Toasts instance.
134
+ * @param title - Expected title of the toast.
135
+ * @param button - Which button to click on the toast. Allowed values are
136
+ * "primary", which will accept the toast, or "secondary",
137
+ * which will reject it.
138
+ * @param timeout - Time in ms before we give up and decide the toast does
139
+ * not exist. If `required` is true, defaults to `timeout`
140
+ * in `TestConfig.expect`. Otherwise, defaults to 2000 (2
141
+ * seconds).
142
+ * @param required - If true, fail the test (throw an exception) if the
143
+ * toast is not visible. Otherwise, just return after
144
+ * `timeout` if the toast is not visible.
145
+ */
146
+ async function clickToastButton(
147
+ toasts: Toasts,
148
+ title: string,
149
+ button: "primary" | "secondary",
150
+ timeout?: number,
151
+ required = true,
152
+ ): Promise<void> {
153
+ let toast: Locator | null;
154
+ if (required) {
155
+ toast = await toasts.getToast(title, timeout, true);
156
+ } else {
157
+ toast = await toasts.getToast(title, timeout, false);
158
+ }
159
+
160
+ if (toast) {
161
+ await toast.locator(`.mx_Toast_buttons button[data-kind="${button}"]`).click();
108
162
  }
109
163
  }
package/tsconfig.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "$schema": "http://json.schemastore.org/tsconfig",
3
3
  "compilerOptions": {
4
+ "rootDir": "./src",
4
5
  "target": "esnext",
5
6
  "lib": ["dom", "es2022", "esnext"],
6
7
  "esModuleInterop": true,