playwright-clipboard-testing 0.1.0 → 0.3.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/README.md CHANGED
@@ -2,11 +2,23 @@
2
2
 
3
3
  # Playwright Clipboard Testing
4
4
 
5
- Custom Playwright fixture and matchers for testing clipboard functionality in your web applications.
5
+ Effortless clipboard testing for Playwright. Features custom fixtures, smart polling matchers, and auto-managed permissions.
6
6
 
7
+ [![Tests](https://github.com/sergoleksenko/playwright-clipboard-testing/actions/workflows/tests.yml/badge.svg)](https://github.com/sergoleksenko/playwright-clipboard-testing/actions/workflows/tests.yml)
8
+ [![npm version](https://img.shields.io/npm/v/playwright-clipboard-testing.svg?style=flat&color=blue)](https://www.npmjs.com/package/playwright-clipboard-testing)
9
+ [![npm downloads](https://img.shields.io/npm/dw/playwright-clipboard-testing.svg?color=blue)](https://www.npmjs.com/package/playwright-clipboard-testing)
7
10
  [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg?style=flat&color=blue)](LICENSE)
8
11
 
12
+ ## Why?
13
+ Testing the Clipboard API in Playwright usually requires boilerplate code to manually grant `clipboard-read` and `clipboard-write` permissions for every context, write custom page functions, or deal with tricky async polling issues.
14
+
15
+ **playwright-clipboard-testing** simplifies this with:
16
+ - 🔌 **Zero configuration** — Chromium permissions are granted automatically under the hood (Firefox requires a 1-line config setup).
17
+ - 🔄 **Built-in Auto-retries & Polling** — Uses Playwright's native `expect` polling to wait until the clipboard updates asynchronously.
18
+ - 📦 **TypeScript Ready** — Full type safety for JSON objects with `readJSON<T>()`.
19
+
9
20
  ## Table of Contents
21
+ - [Why?](#why)
10
22
  - [Installation](#installation)
11
23
  - [Browser Support](#browser-support)
12
24
  - [Usage](#usage)
@@ -14,6 +26,8 @@ Custom Playwright fixture and matchers for testing clipboard functionality in yo
14
26
  - [Extended Usage](#extended-usage)
15
27
  - [API](#api)
16
28
  - [Clipboard Fixture](#clipboard-fixture)
29
+ - [toHaveText Matcher](#tohavetext-matcher)
30
+ - [toHaveJSON Matcher](#tohavejson-matcher)
17
31
  - [toHaveData Matcher](#tohavedata-matcher)
18
32
  - [Author](#author)
19
33
  - [License](#license)
@@ -25,17 +39,39 @@ npm install --save-dev playwright-clipboard-testing
25
39
  ```
26
40
 
27
41
  ## Browser Support
28
- ![NOTE](https://img.shields.io/badge/NOTE-Only%20supported%20in%20Chromium%20browsers-yellow)
42
+ ![NOTE](https://img.shields.io/badge/NOTE-WebKit%20browser%20does%20not%20support%20the%20Clipboard%20API-yellow)
43
+
44
+ Clipboard API testing is currently supported in Chromium-based and Firefox browsers.
45
+
46
+ > **Note:** Permissions are auto-managed out of the box for Chromium. Firefox requires a one-time preference setup in your config.
29
47
 
30
- The Web Clipboard Permissions API is currently only supported in Chromium-based browsers.
31
- If your Playwright setup runs tests across multiple browsers, skip non-Chromium runs in tests that use the clipboard:
48
+ If your Playwright setup runs tests in WebKit, you can skip clipboard tests for that browser:
32
49
  ```ts
33
50
  test('should copy text to clipboard', async ({ page, clipboard, browserName }) => {
34
- test.skip(browserName !== 'chromium', 'Clipboard API is only supported in Chromium');
51
+ test.skip(browserName === 'webkit', 'Clipboard API is only supported in Chromium and Firefox');
35
52
 
36
53
  // test logic...
37
54
  });
38
55
  ```
56
+ If your Playwright setup runs tests in Firefox, manually configure `firefoxUserPrefs` in your Playwright config:
57
+ ```ts
58
+ import { defineConfig, devices } from '@playwright/test';
59
+ import { firefoxClipboardPrefs } from 'playwright-clipboard-testing';
60
+
61
+ export default defineConfig({
62
+ projects: [
63
+ {
64
+ name: 'firefox',
65
+ use: {
66
+ ...devices['Desktop Firefox'],
67
+ launchOptions: {
68
+ firefoxUserPrefs: firefoxClipboardPrefs,
69
+ },
70
+ },
71
+ },
72
+ ],
73
+ });
74
+ ```
39
75
 
40
76
  ## Usage
41
77
  ### Direct Usage
@@ -46,7 +82,7 @@ import { test, expect } from 'playwright-clipboard-testing';
46
82
 
47
83
  test('should copy text to clipboard', async ({ page, clipboard }) => {
48
84
  await page.goto('https://example.com');
49
- await page.click('#copy-button');
85
+ await page.locator('#copy-button').click();
50
86
 
51
87
  await expect(clipboard).toHaveData('Hello, World!');
52
88
  });
@@ -57,13 +93,18 @@ test('should copy text to clipboard', async ({ page, clipboard }) => {
57
93
  If you already have a custom test fixture file, extend Playwright's `test` and `expect` with `clipboardFixture` and `clipboardMatchers`:
58
94
  ```ts
59
95
  import { expect as baseExpect, test as baseTest } from '@playwright/test';
60
- import { type ClipboardHandler, clipboardFixture, clipboardMatchers } from 'playwright-clipboard-testing';
96
+ import {
97
+ type ClipboardHandler,
98
+ clipboardFixture,
99
+ clipboardMatchers,
100
+ } from 'playwright-clipboard-testing';
61
101
 
62
102
  export const test = baseTest.extend<{ clipboard: ClipboardHandler }>({
63
103
  clipboard: clipboardFixture,
64
104
  });
65
105
 
66
106
  export const expect = baseExpect.extend(clipboardMatchers);
107
+
67
108
  ```
68
109
 
69
110
  ## API
@@ -72,9 +113,40 @@ The `clipboard` fixture provides direct access to the browser clipboard during t
72
113
  - `clipboard.read(): Promise<string>` - reads the current plain text content from the clipboard.
73
114
  - `clipboard.readJSON<T>(): Promise<T>` - reads the current clipboard content and parses it as a JSON object of type `T`. Throws an error if the content is not valid JSON.
74
115
 
75
- ![NOTE](https://img.shields.io/badge/NOTE-For%20your%20tests%20we%20recommend%20using%20the%20toHaveData%20matcher-yellow)
116
+ ![NOTE](https://img.shields.io/badge/NOTE-For%20your%20tests%20we%20recommend%20using%20existing%20matchers%20to%20assert%20clipboard%20content-yellow)
117
+
118
+ ### toHaveText Matcher
119
+ `expect(clipboard).toHaveText(expected, options?)`
120
+
121
+ Asserts that the clipboard content matches the expected string. Uses Playwright's smart polling mechanism to wait for the clipboard to update.
122
+ - `expected: string` — Expected text to compare against.
123
+ - `options.timeout: number (optional, default: 10000ms)` — Time in milliseconds to wait for the clipboard content to match.
124
+ ```ts
125
+ // assert that the clipboard contains the expected text
126
+ await expect(clipboard).toHaveText('Hello, World!');
127
+ ```
128
+ ```ts
129
+ // Custom timeout
130
+ await expect(clipboard).toHaveText('Async copied value', { timeout: 5000 });
131
+ ```
132
+
133
+ ### toHaveJSON Matcher
134
+ `expect(clipboard).toHaveJSON(expected, options?)`
135
+ Asserts that the clipboard content matches the expected JSON value. Uses Playwright's smart polling mechanism to wait for the clipboard to update.
136
+ - `expected: unknown` — Expected JSON value to compare against.
137
+ - `options.timeout: number (optional, default: 10000ms)` — Time in milliseconds to wait for the clipboard content to match.
138
+ ```ts
139
+ // assert that the clipboard contains the expected JSON data
140
+ await expect(clipboard).toHaveJSON({ message: 'Hello, World!' });
141
+ ```
142
+ ```ts
143
+ // Custom timeout
144
+ await expect(clipboard).toHaveJSON({ message: 'Async copied value' }, { timeout: 5000 });
145
+ ```
76
146
 
77
147
  ### toHaveData Matcher
148
+ ![NOTE](https://img.shields.io/badge/NOTE-Matcher%20is%20deprecated%20in%20favor%20of%20toHaveText%20and%20toHaveJSON-yellow)
149
+
78
150
  `expect(clipboard).toHaveData(expected, options?)`
79
151
 
80
152
  Asserts that the clipboard content matches the expected string or JSON object. Uses Playwright's smart polling mechanism to wait for the clipboard to update.
@@ -98,3 +170,6 @@ Sergii Oleksenko <serg.oleksenko@gmail.com>
98
170
 
99
171
  ## License
100
172
  [MIT](LICENSE)
173
+
174
+ ---
175
+ If you find this project useful, give it a ⭐️ on [GitHub](https://github.com/sergoleksenko/playwright-clipboard-testing)!
package/dist/index.cjs CHANGED
@@ -23,30 +23,43 @@ __export(index_exports, {
23
23
  ClipboardHandler: () => ClipboardHandler,
24
24
  clipboardFixture: () => clipboardFixture,
25
25
  clipboardMatchers: () => clipboardMatchers,
26
- expect: () => expect2,
26
+ expect: () => expect3,
27
+ firefoxClipboardPrefs: () => firefoxClipboardPrefs,
27
28
  test: () => test
28
29
  });
29
30
  module.exports = __toCommonJS(index_exports);
30
31
 
32
+ // src/constants.ts
33
+ var firefoxClipboardPrefs = {
34
+ "dom.events.testing.asyncClipboard": true,
35
+ "dom.events.asyncClipboard.readText": true,
36
+ "dom.events.asyncClipboard.writeText": true,
37
+ "permissions.default.clipboard-read": 1,
38
+ "permissions.default.clipboard-write": 1
39
+ };
40
+
31
41
  // src/utils/clipboardHandler.ts
32
42
  var ClipboardHandler = class {
33
- constructor(page, context) {
43
+ constructor(page, context, browserName = "chromium") {
34
44
  this.page = page;
35
45
  this.context = context;
46
+ this.browserName = browserName;
36
47
  this.isPermissionGranted = false;
37
48
  }
38
49
  page;
39
50
  context;
51
+ browserName;
40
52
  isPermissionGranted;
41
53
  /**
42
- * Grants the necessary clipboard permissions ('clipboard-read' and 'clipboard-write') to the browser context.
43
- * This method is called internally before any clipboard read operation to ensure permissions are set.
54
+ * Grants clipboard permissions to the Chromium browser context if not already granted.
44
55
  * @private
45
56
  */
46
57
  async grantPermissions() {
47
- if (!this.isPermissionGranted) {
48
- await this.context.grantPermissions(["clipboard-read", "clipboard-write"]);
49
- this.isPermissionGranted = true;
58
+ if (this.browserName === "chromium") {
59
+ if (!this.isPermissionGranted) {
60
+ await this.context.grantPermissions(["clipboard-read", "clipboard-write"]);
61
+ this.isPermissionGranted = true;
62
+ }
50
63
  }
51
64
  }
52
65
  /**
@@ -80,20 +93,20 @@ var ClipboardHandler = class {
80
93
 
81
94
  // src/fixtures/clipboardFixture.ts
82
95
  var clipboardFixture = async ({ page, context, browserName }, use) => {
83
- if (browserName !== "chromium") {
96
+ if (browserName === "webkit") {
84
97
  throw new Error(
85
- `[playwright-clipboard] Browser '${browserName}' is not supported. Clipboard Permissions API is currently only supported in Chromium-based browsers.
86
- Use test.skip(browserName !== 'chromium') in your tests to skip non-Chromium runs.`
98
+ `[playwright-clipboard] Browser '${browserName}' is not supported. Clipboard API testing is currently supported only in Chromium-based and Firefox browsers.
99
+ Add test.skip(browserName === 'webkit', 'Clipboard API is only supported in Chromium and Firefox'); to your test body.`
87
100
  );
88
101
  }
89
- const handler = new ClipboardHandler(page, context);
102
+ const handler = new ClipboardHandler(page, context, browserName);
90
103
  await use(handler);
91
104
  };
92
105
 
93
106
  // src/fixtures.ts
94
- var import_test2 = require("@playwright/test");
107
+ var import_test3 = require("@playwright/test");
95
108
 
96
- // src/matchers/toHaveData.ts
109
+ // src/matchers/toHaveJSON.ts
97
110
  var import_test = require("@playwright/test");
98
111
 
99
112
  // src/utils/matcherUtils.ts
@@ -112,23 +125,68 @@ ${error.message}`;
112
125
  Received: ${this.utils.printReceived(actual)}`;
113
126
  }
114
127
 
115
- // src/matchers/toHaveData.ts
116
- async function toHaveData(clipboard, expected, options = {}) {
117
- const name = "toHaveData";
128
+ // src/matchers/toHaveJSON.ts
129
+ async function toHaveJSON(clipboard, expected, options = {}) {
130
+ const name = "toHaveJSON";
118
131
  let pass;
119
132
  let actual;
133
+ let errorReason = null;
120
134
  const { timeout = 1e4 } = options;
121
135
  const poll = import_test.expect.poll(
122
136
  async () => {
123
137
  try {
124
138
  actual = await clipboard.readJSON();
125
- } catch {
126
- actual = await clipboard.read();
139
+ errorReason = null;
140
+ return actual;
141
+ } catch (error) {
142
+ errorReason = error instanceof Error ? error : new Error(String(error));
143
+ try {
144
+ actual = await clipboard.read();
145
+ } catch {
146
+ actual = void 0;
147
+ }
148
+ throw errorReason;
127
149
  }
128
- if (typeof expected === "string" && actual !== null && actual !== void 0) {
129
- return String(actual);
150
+ },
151
+ { timeout }
152
+ );
153
+ try {
154
+ const expectation = this.isNot ? poll.not : poll;
155
+ await expectation.toEqual(expected);
156
+ pass = true;
157
+ } catch {
158
+ pass = false;
159
+ }
160
+ if (this.isNot) pass = !pass;
161
+ const matcherReturn = {
162
+ message: getErrorMessage.call(this, name, expected, actual, errorReason),
163
+ pass,
164
+ name,
165
+ expected,
166
+ actual
167
+ };
168
+ return matcherReturn;
169
+ }
170
+
171
+ // src/matchers/toHaveText.ts
172
+ var import_test2 = require("@playwright/test");
173
+ async function toHaveText(clipboard, expected, options = {}) {
174
+ const name = "toHaveText";
175
+ let pass;
176
+ let actual;
177
+ let errorReason = null;
178
+ const { timeout = 1e4 } = options;
179
+ const poll = import_test2.expect.poll(
180
+ async () => {
181
+ try {
182
+ actual = await clipboard.read();
183
+ errorReason = null;
184
+ return actual;
185
+ } catch (error) {
186
+ errorReason = error instanceof Error ? error : new Error(String(error));
187
+ actual = void 0;
188
+ throw errorReason;
130
189
  }
131
- return actual;
132
190
  },
133
191
  { timeout }
134
192
  );
@@ -141,7 +199,7 @@ async function toHaveData(clipboard, expected, options = {}) {
141
199
  }
142
200
  if (this.isNot) pass = !pass;
143
201
  const matcherReturn = {
144
- message: getErrorMessage.call(this, name, expected, actual),
202
+ message: getErrorMessage.call(this, name, expected, actual, errorReason),
145
203
  pass,
146
204
  name,
147
205
  expected,
@@ -150,26 +208,48 @@ async function toHaveData(clipboard, expected, options = {}) {
150
208
  return matcherReturn;
151
209
  }
152
210
 
211
+ // src/matchers/toHaveData.ts
212
+ async function toHaveData(clipboard, expected, options = {}) {
213
+ const name = "toHaveData";
214
+ let matcherReturn = null;
215
+ if (typeof expected === "string") {
216
+ matcherReturn = await toHaveText.call(this, clipboard, expected, options);
217
+ } else {
218
+ matcherReturn = await toHaveJSON.call(this, clipboard, expected, options);
219
+ }
220
+ return {
221
+ ...matcherReturn,
222
+ name,
223
+ message: () => {
224
+ const originalMessage = matcherReturn.message();
225
+ return originalMessage.replace("toHaveText", name).replace("toHaveJSON", name);
226
+ }
227
+ };
228
+ }
229
+
153
230
  // src/matchers/clipboardMatchers.ts
154
231
  var clipboardMatchers = {
232
+ toHaveText,
233
+ toHaveJSON,
155
234
  toHaveData
156
235
  };
157
236
 
158
237
  // src/fixtures.ts
159
- var test = import_test2.test.extend({
238
+ var test = import_test3.test.extend({
160
239
  /**
161
240
  * A fixture that provides an instance of the Clipboard utility for interacting with the system clipboard.
162
241
  * It allows reading from the clipboard during tests.
163
242
  */
164
243
  clipboard: clipboardFixture
165
244
  });
166
- var expect2 = import_test2.expect.extend({ ...clipboardMatchers });
245
+ var expect3 = import_test3.expect.extend({ ...clipboardMatchers });
167
246
  // Annotate the CommonJS export names for ESM import in node:
168
247
  0 && (module.exports = {
169
248
  ClipboardHandler,
170
249
  clipboardFixture,
171
250
  clipboardMatchers,
172
251
  expect,
252
+ firefoxClipboardPrefs,
173
253
  test
174
254
  });
175
255
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/utils/clipboardHandler.ts","../src/fixtures/clipboardFixture.ts","../src/fixtures.ts","../src/matchers/toHaveData.ts","../src/utils/matcherUtils.ts","../src/matchers/clipboardMatchers.ts"],"sourcesContent":["export { clipboardFixture } from './fixtures/clipboardFixture.js';\nexport { expect, test } from './fixtures.js';\nexport { clipboardMatchers } from './matchers/clipboardMatchers.js';\nexport { ClipboardHandler } from './utils/clipboardHandler.js';\n","/**\n * Playwright-compatible Clipboard utilities.\n *\n * Core functions for interacting with the browser clipboard within Playwright tests.\n * Provides wrappers for reading plain text and JSON.\n *\n * @remarks\n * These utilities require 'clipboard-read' and 'clipboard-write' permissions\n * to be granted in the browser context.\n */\n\nimport type { BrowserContext, Page } from '@playwright/test';\n\nexport class ClipboardHandler {\n private isPermissionGranted: boolean;\n\n constructor(\n private readonly page: Page,\n private readonly context: BrowserContext,\n ) {\n this.isPermissionGranted = false;\n }\n\n /**\n * Grants the necessary clipboard permissions ('clipboard-read' and 'clipboard-write') to the browser context.\n * This method is called internally before any clipboard read operation to ensure permissions are set.\n * @private\n */\n private async grantPermissions() {\n if (!this.isPermissionGranted) {\n await this.context.grantPermissions(['clipboard-read', 'clipboard-write']);\n this.isPermissionGranted = true;\n }\n }\n\n /**\n * Reads the current text content from the browser clipboard.\n * If the content is a JSON-encoded string (e.g., has extra quotes),\n * it will be returned as is. Use readJSON for automatic parsing.\n *\n * @returns A promise that resolves to the clipboard string content.\n */\n async read(): Promise<string> {\n await this.grantPermissions();\n return await this.page.evaluate(() => navigator.clipboard.readText());\n }\n\n /**\n * Reads the clipboard content and parses it as JSON.\n * If the content is a string literal (e.g., '\"value\"'), it returns the unwrapped string ('value').\n *\n * @template T - The expected type of the parsed JSON object.\n * @returns A promise that resolves to the parsed JSON object of type T.\n * @throws {Error} If the clipboard content is not a valid JSON string.\n */\n async readJSON<T = unknown>(): Promise<T> {\n const text = await this.read();\n try {\n return JSON.parse(text);\n } catch {\n throw new Error(`Clipboard content is not a valid JSON: ${JSON.stringify(text)}`);\n }\n }\n}\n","import type { BrowserContext, Page, TestFixture } from '@playwright/test';\nimport { ClipboardHandler } from '../utils/clipboardHandler.js';\n\n/**\n * A fixture that provides an instance of the Clipboard utility for interacting with the system clipboard.\n * It allows reading from the clipboard during tests.\n */\nexport const clipboardFixture: TestFixture<\n ClipboardHandler,\n { page: Page; context: BrowserContext; browserName: string }\n> = async ({ page, context, browserName }, use) => {\n if (browserName !== 'chromium') {\n throw new Error(\n `[playwright-clipboard] Browser '${browserName}' is not supported. ` +\n 'Clipboard Permissions API is currently only supported in Chromium-based browsers.\\n' +\n `Use test.skip(browserName !== 'chromium') in your tests to skip non-Chromium runs.`,\n );\n }\n\n const handler = new ClipboardHandler(page, context);\n await use(handler);\n};\n","import { expect as baseExpect, test as baseTest } from '@playwright/test';\nimport { clipboardFixture } from './fixtures/clipboardFixture.js';\nimport { clipboardMatchers } from './matchers/clipboardMatchers.js';\nimport type { ClipboardHandler } from './utils/clipboardHandler.js';\n\nexport const test = baseTest.extend<{\n clipboard: ClipboardHandler;\n}>({\n /**\n * A fixture that provides an instance of the Clipboard utility for interacting with the system clipboard.\n * It allows reading from the clipboard during tests.\n */\n clipboard: clipboardFixture,\n});\n\nexport const expect = baseExpect.extend({ ...clipboardMatchers });\n","import { type ExpectMatcherState, expect, type MatcherReturnType } from '@playwright/test';\nimport type { ClipboardHandler } from '../utils/clipboardHandler.js';\nimport { getErrorMessage } from '../utils/matcherUtils.js';\nimport type { MatcherOptions } from './types.js';\n\n/**\n * Asserts that the clipboard content matches the expected value.\n * Uses smart polling to wait for the clipboard to be updated.\n * If the `expected` value is an object, it attempts to parse the clipboard\n * content as JSON before comparing.\n *\n * @this ExpectMatcherState\n * @param clipboard The Clipboard utility instance.\n * @param expected The string or object to compare against the clipboard content.\n * @param options Optional settings for the matcher, such as timeout.\n * @returns A Promise that resolves to a MatcherReturnType object.\n */\nexport async function toHaveData(\n this: ExpectMatcherState,\n clipboard: ClipboardHandler,\n expected: unknown,\n options: MatcherOptions = {},\n) {\n const name = 'toHaveData';\n let pass: boolean;\n let actual: unknown;\n\n const { timeout = 10_000 } = options;\n\n const poll = expect.poll(\n async () => {\n try {\n actual = await clipboard.readJSON();\n } catch {\n actual = await clipboard.read();\n }\n\n if (typeof expected === 'string' && actual !== null && actual !== undefined) {\n return String(actual);\n }\n\n return actual;\n },\n { timeout },\n );\n\n try {\n const expectation = this.isNot ? poll.not : poll;\n await expectation.toEqual(expected);\n pass = true;\n } catch {\n pass = false;\n }\n\n /**\n * NOTE ON INVERSION:\n * In Playwright custom matchers, if `this.isNot` is true, the matcher must return\n * `pass: false` for the assertion to pass. Since `poll.not.toEqual` resolves\n * to `true` when the condition is met, we invert it back to align with Playwright's expectations.\n */\n if (this.isNot) pass = !pass;\n\n const matcherReturn: MatcherReturnType = {\n message: getErrorMessage.call(this, name, expected, actual),\n pass,\n name,\n expected,\n actual,\n };\n\n return matcherReturn;\n}\n","import type { ExpectMatcherState } from '@playwright/test';\n\n/**\n * Generates an error message for a custom matcher.\n *\n * @this ExpectMatcherState\n * @param name The name of the matcher.\n * @param expected The expected value.\n * @param actual The actual value.\n * @param error An optional error object.\n * @param errorMessage An optional error message string.\n * @returns A function that returns the error message string.\n */\nexport function getErrorMessage(\n this: ExpectMatcherState,\n name: string,\n expected: unknown,\n actual: unknown,\n error?: Error | null,\n errorMessage?: string | undefined | null,\n): () => string {\n const message = `${this.utils.matcherHint(name, undefined, undefined, { isNot: this.isNot })}\\n\\n`;\n\n if (error) {\n return () => `${message}An unexpected error occurred:\\n${error.message}`;\n }\n\n if (errorMessage) {\n return () => `${message}${errorMessage}`;\n }\n\n return () =>\n message +\n `Expected: ${this.isNot ? 'not ' : ''}${this.utils.printExpected(expected)}\\n` +\n `Received: ${this.utils.printReceived(actual)}`;\n}\n","import { toHaveData } from './toHaveData.js';\n\n/**\n * Export an object containing all the custom clipboard matchers for Playwright.\n */\nexport const clipboardMatchers = {\n toHaveData,\n};\n"],"mappings":"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","names":["expect","import_test","baseTest","expect","baseExpect"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/utils/clipboardHandler.ts","../src/fixtures/clipboardFixture.ts","../src/fixtures.ts","../src/matchers/toHaveJSON.ts","../src/utils/matcherUtils.ts","../src/matchers/toHaveText.ts","../src/matchers/toHaveData.ts","../src/matchers/clipboardMatchers.ts"],"sourcesContent":["export { firefoxClipboardPrefs } from './constants.js';\nexport { clipboardFixture } from './fixtures/clipboardFixture.js';\nexport { expect, test } from './fixtures.js';\nexport { clipboardMatchers } from './matchers/clipboardMatchers.js';\nexport { ClipboardHandler } from './utils/clipboardHandler.js';\n","export const firefoxClipboardPrefs = {\n 'dom.events.testing.asyncClipboard': true,\n 'dom.events.asyncClipboard.readText': true,\n 'dom.events.asyncClipboard.writeText': true,\n 'permissions.default.clipboard-read': 1,\n 'permissions.default.clipboard-write': 1,\n};\n","/**\n * Playwright-compatible Clipboard utilities.\n *\n * Core functions for interacting with the browser clipboard within Playwright tests.\n * Provides wrappers for reading plain text and JSON.\n *\n * @remarks\n * These utilities require 'clipboard-read' and 'clipboard-write' permissions\n * to be granted in the browser context.\n */\n\nimport type { BrowserContext, Page } from '@playwright/test';\nimport type { BrowserName } from '../types.js';\n\nexport class ClipboardHandler {\n private readonly browserName: BrowserName;\n private isPermissionGranted: boolean;\n\n constructor(\n private readonly page: Page,\n private readonly context: BrowserContext,\n browserName: BrowserName = 'chromium',\n ) {\n this.browserName = browserName;\n this.isPermissionGranted = false;\n }\n\n /**\n * Grants clipboard permissions to the Chromium browser context if not already granted.\n * @private\n */\n private async grantPermissions() {\n if (this.browserName === 'chromium') {\n if (!this.isPermissionGranted) {\n await this.context.grantPermissions(['clipboard-read', 'clipboard-write']);\n this.isPermissionGranted = true;\n }\n }\n }\n\n /**\n * Reads the current text content from the browser clipboard.\n * If the content is a JSON-encoded string (e.g., has extra quotes),\n * it will be returned as is. Use readJSON for automatic parsing.\n *\n * @returns A promise that resolves to the clipboard string content.\n */\n async read(): Promise<string> {\n await this.grantPermissions();\n return await this.page.evaluate(() => navigator.clipboard.readText());\n }\n\n /**\n * Reads the clipboard content and parses it as JSON.\n * If the content is a string literal (e.g., '\"value\"'), it returns the unwrapped string ('value').\n *\n * @template T - The expected type of the parsed JSON object.\n * @returns A promise that resolves to the parsed JSON object of type T.\n * @throws {Error} If the clipboard content is not a valid JSON string.\n */\n async readJSON<T = unknown>(): Promise<T> {\n const text = await this.read();\n try {\n return JSON.parse(text);\n } catch {\n throw new Error(`Clipboard content is not a valid JSON: ${JSON.stringify(text)}`);\n }\n }\n}\n","import type { BrowserContext, Page, TestFixture } from '@playwright/test';\nimport type { BrowserName } from '../types.js';\nimport { ClipboardHandler } from '../utils/clipboardHandler.js';\n\n/**\n * A fixture that provides an instance of the Clipboard utility for interacting with the system clipboard.\n * It allows reading from the clipboard during tests.\n */\nexport const clipboardFixture: TestFixture<\n ClipboardHandler,\n { page: Page; context: BrowserContext; browserName: BrowserName }\n> = async ({ page, context, browserName }, use) => {\n if (browserName === 'webkit') {\n throw new Error(\n `[playwright-clipboard] Browser '${browserName}' is not supported. ` +\n 'Clipboard API testing is currently supported only in Chromium-based and Firefox browsers.\\n' +\n `Add test.skip(browserName === 'webkit', 'Clipboard API is only supported in Chromium and Firefox'); to your test body.`,\n );\n }\n\n const handler = new ClipboardHandler(page, context, browserName);\n await use(handler);\n};\n","import { expect as baseExpect, test as baseTest } from '@playwright/test';\nimport { clipboardFixture } from './fixtures/clipboardFixture.js';\nimport { clipboardMatchers } from './matchers/clipboardMatchers.js';\nimport type { ClipboardHandler } from './utils/clipboardHandler.js';\n\nexport const test = baseTest.extend<{\n clipboard: ClipboardHandler;\n}>({\n /**\n * A fixture that provides an instance of the Clipboard utility for interacting with the system clipboard.\n * It allows reading from the clipboard during tests.\n */\n clipboard: clipboardFixture,\n});\n\nexport const expect = baseExpect.extend({ ...clipboardMatchers });\n","import type { ExpectMatcherState, MatcherReturnType } from '@playwright/test';\nimport { expect } from '@playwright/test';\nimport type { ClipboardHandler } from '../utils/clipboardHandler.js';\nimport { getErrorMessage } from '../utils/matcherUtils.js';\nimport type { MatcherOptions } from './types.js';\n\n/**\n * Asserts that the clipboard content matches the expected JSON value.\n *\n * @this ExpectMatcherState\n * @param clipboard The Clipboard utility instance.\n * @param expected The expected JSON value.\n * @param options Matcher options.\n * @returns A Promise that resolves to a MatcherReturnType object.\n */\nexport async function toHaveJSON(\n this: ExpectMatcherState,\n clipboard: ClipboardHandler,\n expected: unknown,\n options: MatcherOptions = {},\n) {\n const name = 'toHaveJSON';\n let pass: boolean;\n let actual: unknown;\n let errorReason: Error | null = null;\n\n const { timeout = 10_000 } = options;\n\n const poll = expect.poll(\n async () => {\n try {\n actual = await clipboard.readJSON();\n errorReason = null;\n return actual;\n } catch (error) {\n errorReason = error instanceof Error ? error : new Error(String(error));\n\n try {\n actual = await clipboard.read();\n } catch {\n actual = undefined;\n }\n\n throw errorReason;\n }\n },\n { timeout },\n );\n\n try {\n const expectation = this.isNot ? poll.not : poll;\n await expectation.toEqual(expected);\n pass = true;\n } catch {\n pass = false;\n }\n\n if (this.isNot) pass = !pass;\n\n const matcherReturn: MatcherReturnType = {\n message: getErrorMessage.call(this, name, expected, actual, errorReason),\n pass,\n name,\n expected,\n actual,\n };\n\n return matcherReturn;\n}\n","import type { ExpectMatcherState } from '@playwright/test';\n\n/**\n * Generates an error message for a custom matcher.\n *\n * @this ExpectMatcherState\n * @param name The name of the matcher.\n * @param expected The expected value.\n * @param actual The actual value.\n * @param error An optional error object.\n * @param errorMessage An optional error message string.\n * @returns A function that returns the error message string.\n */\nexport function getErrorMessage(\n this: ExpectMatcherState,\n name: string,\n expected: unknown,\n actual: unknown,\n error?: Error | null,\n errorMessage?: string | undefined | null,\n): () => string {\n const message = `${this.utils.matcherHint(name, undefined, undefined, { isNot: this.isNot })}\\n\\n`;\n\n if (error) {\n return () => `${message}An unexpected error occurred:\\n${error.message}`;\n }\n\n if (errorMessage) {\n return () => `${message}${errorMessage}`;\n }\n\n return () =>\n message +\n `Expected: ${this.isNot ? 'not ' : ''}${this.utils.printExpected(expected)}\\n` +\n `Received: ${this.utils.printReceived(actual)}`;\n}\n","import { type ExpectMatcherState, expect, type MatcherReturnType } from '@playwright/test';\nimport type { ClipboardHandler } from '../utils/clipboardHandler.js';\nimport { getErrorMessage } from '../utils/matcherUtils.js';\nimport type { MatcherOptions } from './types.js';\n\n/**\n * Asserts that the clipboard content matches the expected text value.\n *\n * @this ExpectMatcherState\n * @param clipboard The Clipboard utility instance.\n * @param expected The expected text value.\n * @param options Matcher options.\n * @returns A Promise that resolves to a MatcherReturnType object.\n */\nexport async function toHaveText(\n this: ExpectMatcherState,\n clipboard: ClipboardHandler,\n expected: string,\n options: MatcherOptions = {},\n) {\n const name = 'toHaveText';\n let pass: boolean;\n let actual: unknown;\n let errorReason: Error | null = null;\n\n const { timeout = 10_000 } = options;\n\n const poll = expect.poll(\n async () => {\n try {\n actual = await clipboard.read();\n errorReason = null;\n return actual;\n } catch (error) {\n errorReason = error instanceof Error ? error : new Error(String(error));\n actual = undefined;\n\n throw errorReason;\n }\n },\n { timeout },\n );\n\n try {\n const expectation = this.isNot ? poll.not : poll;\n await expectation.toEqual(expected);\n pass = true;\n } catch {\n pass = false;\n }\n\n if (this.isNot) pass = !pass;\n\n const matcherReturn: MatcherReturnType = {\n message: getErrorMessage.call(this, name, expected, actual, errorReason),\n pass,\n name,\n expected,\n actual,\n };\n\n return matcherReturn;\n}\n","import type { ExpectMatcherState, MatcherReturnType } from '@playwright/test';\nimport type { ClipboardHandler } from '../utils/clipboardHandler.js';\nimport { toHaveJSON } from './toHaveJSON.js';\nimport { toHaveText } from './toHaveText.js';\nimport type { MatcherOptions } from './types.js';\n\n/**\n * Asserts that the clipboard content matches the expected value.\n * Uses smart polling to wait for the clipboard to be updated.\n * If the `expected` value is an object, it attempts to parse the clipboard\n * content as JSON before comparing.\n *\n * @this ExpectMatcherState\n * @param clipboard The Clipboard utility instance.\n * @param expected The string or object to compare against the clipboard content.\n * @param options Optional settings for the matcher, such as timeout.\n * @returns A Promise that resolves to a MatcherReturnType object.\n */\nexport async function toHaveData(\n this: ExpectMatcherState,\n clipboard: ClipboardHandler,\n expected: unknown,\n options: MatcherOptions = {},\n) {\n const name = 'toHaveData';\n let matcherReturn: MatcherReturnType | null = null;\n\n if (typeof expected === 'string') {\n matcherReturn = await toHaveText.call(this, clipboard, expected, options);\n } else {\n matcherReturn = await toHaveJSON.call(this, clipboard, expected, options);\n }\n\n return {\n ...matcherReturn,\n name,\n message: () => {\n const originalMessage = matcherReturn.message();\n return originalMessage.replace('toHaveText', name).replace('toHaveJSON', name);\n },\n };\n}\n","import { toHaveData } from './toHaveData.js';\nimport { toHaveJSON } from './toHaveJSON.js';\nimport { toHaveText } from './toHaveText.js';\n\n/**\n * Export an object containing all the custom clipboard matchers for Playwright.\n */\nexport const clipboardMatchers = {\n toHaveText,\n toHaveJSON,\n toHaveData,\n};\n"],"mappings":"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","names":["expect","import_test","import_test","baseTest","expect","baseExpect"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,16 @@
1
1
  import * as _playwright_test from '@playwright/test';
2
2
  import { Page, BrowserContext, TestFixture, ExpectMatcherState, MatcherReturnType } from '@playwright/test';
3
3
 
4
+ declare const firefoxClipboardPrefs: {
5
+ 'dom.events.testing.asyncClipboard': boolean;
6
+ 'dom.events.asyncClipboard.readText': boolean;
7
+ 'dom.events.asyncClipboard.writeText': boolean;
8
+ 'permissions.default.clipboard-read': number;
9
+ 'permissions.default.clipboard-write': number;
10
+ };
11
+
12
+ type BrowserName = 'chromium' | 'firefox' | 'webkit';
13
+
4
14
  /**
5
15
  * Playwright-compatible Clipboard utilities.
6
16
  *
@@ -15,11 +25,11 @@ import { Page, BrowserContext, TestFixture, ExpectMatcherState, MatcherReturnTyp
15
25
  declare class ClipboardHandler {
16
26
  private readonly page;
17
27
  private readonly context;
28
+ private readonly browserName;
18
29
  private isPermissionGranted;
19
- constructor(page: Page, context: BrowserContext);
30
+ constructor(page: Page, context: BrowserContext, browserName?: BrowserName);
20
31
  /**
21
- * Grants the necessary clipboard permissions ('clipboard-read' and 'clipboard-write') to the browser context.
22
- * This method is called internally before any clipboard read operation to ensure permissions are set.
32
+ * Grants clipboard permissions to the Chromium browser context if not already granted.
23
33
  * @private
24
34
  */
25
35
  private grantPermissions;
@@ -49,7 +59,7 @@ declare class ClipboardHandler {
49
59
  declare const clipboardFixture: TestFixture<ClipboardHandler, {
50
60
  page: Page;
51
61
  context: BrowserContext;
52
- browserName: string;
62
+ browserName: BrowserName;
53
63
  }>;
54
64
 
55
65
  type MatcherOptions = {
@@ -58,12 +68,38 @@ type MatcherOptions = {
58
68
  declare global {
59
69
  namespace PlaywrightTest {
60
70
  interface Matchers<R> {
71
+ /**
72
+ * Asserts that the clipboard content matches the expected text.
73
+ * Uses smart polling to wait for the clipboard to be updated.
74
+ *
75
+ * @param expected The string to compare against the clipboard content.
76
+ * @param options Matcher options.
77
+ * @returns A Promise that resolves when the assertion completes.
78
+ *
79
+ * @example
80
+ * await expect(clipboard).toHaveText('Copied value');
81
+ */
82
+ toHaveText(expected: string, options?: MatcherOptions): Promise<R>;
83
+ /**
84
+ * Asserts that the clipboard content matches the expected JSON value.
85
+ * Uses smart polling to wait for the clipboard to be updated.
86
+ *
87
+ * @param expected The JSON value to compare against the clipboard content.
88
+ * @param options Matcher options.
89
+ * @returns A Promise that resolves when the assertion completes.
90
+ *
91
+ * @example
92
+ * await expect(clipboard).toHaveJSON({ id: 123, status: 'success' });
93
+ */
94
+ toHaveJSON(expected: unknown, options?: MatcherOptions): Promise<R>;
61
95
  /**
62
96
  * Asserts that the clipboard content matches the expected value.
63
97
  * Uses smart polling to wait for the clipboard to be updated.
64
98
  * If the `expected` value is an object, it attempts to parse the clipboard
65
99
  * content as JSON before comparing.
66
100
  *
101
+ * @deprecated Use `toHaveText` or `toHaveJSON` instead. This matcher will be removed in future versions.
102
+ *
67
103
  * @param expected The string or object to compare against the clipboard content.
68
104
  * @param options Optional settings for the matcher, such as timeout.
69
105
  * @returns A Promise that resolves when the assertion completes.
@@ -89,12 +125,44 @@ declare global {
89
125
  * @param options Optional settings for the matcher, such as timeout.
90
126
  * @returns A Promise that resolves to a MatcherReturnType object.
91
127
  */
92
- declare function toHaveData(this: ExpectMatcherState, clipboard: ClipboardHandler, expected: unknown, options?: MatcherOptions): Promise<MatcherReturnType>;
128
+ declare function toHaveData(this: ExpectMatcherState, clipboard: ClipboardHandler, expected: unknown, options?: MatcherOptions): Promise<{
129
+ name: string;
130
+ message: () => string;
131
+ pass: boolean;
132
+ expected?: unknown;
133
+ actual?: any;
134
+ log?: string[];
135
+ timeout?: number;
136
+ }>;
137
+
138
+ /**
139
+ * Asserts that the clipboard content matches the expected JSON value.
140
+ *
141
+ * @this ExpectMatcherState
142
+ * @param clipboard The Clipboard utility instance.
143
+ * @param expected The expected JSON value.
144
+ * @param options Matcher options.
145
+ * @returns A Promise that resolves to a MatcherReturnType object.
146
+ */
147
+ declare function toHaveJSON(this: ExpectMatcherState, clipboard: ClipboardHandler, expected: unknown, options?: MatcherOptions): Promise<MatcherReturnType>;
148
+
149
+ /**
150
+ * Asserts that the clipboard content matches the expected text value.
151
+ *
152
+ * @this ExpectMatcherState
153
+ * @param clipboard The Clipboard utility instance.
154
+ * @param expected The expected text value.
155
+ * @param options Matcher options.
156
+ * @returns A Promise that resolves to a MatcherReturnType object.
157
+ */
158
+ declare function toHaveText(this: ExpectMatcherState, clipboard: ClipboardHandler, expected: string, options?: MatcherOptions): Promise<MatcherReturnType>;
93
159
 
94
160
  declare const test: _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & {
95
161
  clipboard: ClipboardHandler;
96
162
  }, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
97
163
  declare const expect: _playwright_test.Expect<{
164
+ toHaveText: typeof toHaveText;
165
+ toHaveJSON: typeof toHaveJSON;
98
166
  toHaveData: typeof toHaveData;
99
167
  }>;
100
168
 
@@ -102,7 +170,9 @@ declare const expect: _playwright_test.Expect<{
102
170
  * Export an object containing all the custom clipboard matchers for Playwright.
103
171
  */
104
172
  declare const clipboardMatchers: {
173
+ toHaveText: typeof toHaveText;
174
+ toHaveJSON: typeof toHaveJSON;
105
175
  toHaveData: typeof toHaveData;
106
176
  };
107
177
 
108
- export { ClipboardHandler, clipboardFixture, clipboardMatchers, expect, test };
178
+ export { ClipboardHandler, clipboardFixture, clipboardMatchers, expect, firefoxClipboardPrefs, test };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,16 @@
1
1
  import * as _playwright_test from '@playwright/test';
2
2
  import { Page, BrowserContext, TestFixture, ExpectMatcherState, MatcherReturnType } from '@playwright/test';
3
3
 
4
+ declare const firefoxClipboardPrefs: {
5
+ 'dom.events.testing.asyncClipboard': boolean;
6
+ 'dom.events.asyncClipboard.readText': boolean;
7
+ 'dom.events.asyncClipboard.writeText': boolean;
8
+ 'permissions.default.clipboard-read': number;
9
+ 'permissions.default.clipboard-write': number;
10
+ };
11
+
12
+ type BrowserName = 'chromium' | 'firefox' | 'webkit';
13
+
4
14
  /**
5
15
  * Playwright-compatible Clipboard utilities.
6
16
  *
@@ -15,11 +25,11 @@ import { Page, BrowserContext, TestFixture, ExpectMatcherState, MatcherReturnTyp
15
25
  declare class ClipboardHandler {
16
26
  private readonly page;
17
27
  private readonly context;
28
+ private readonly browserName;
18
29
  private isPermissionGranted;
19
- constructor(page: Page, context: BrowserContext);
30
+ constructor(page: Page, context: BrowserContext, browserName?: BrowserName);
20
31
  /**
21
- * Grants the necessary clipboard permissions ('clipboard-read' and 'clipboard-write') to the browser context.
22
- * This method is called internally before any clipboard read operation to ensure permissions are set.
32
+ * Grants clipboard permissions to the Chromium browser context if not already granted.
23
33
  * @private
24
34
  */
25
35
  private grantPermissions;
@@ -49,7 +59,7 @@ declare class ClipboardHandler {
49
59
  declare const clipboardFixture: TestFixture<ClipboardHandler, {
50
60
  page: Page;
51
61
  context: BrowserContext;
52
- browserName: string;
62
+ browserName: BrowserName;
53
63
  }>;
54
64
 
55
65
  type MatcherOptions = {
@@ -58,12 +68,38 @@ type MatcherOptions = {
58
68
  declare global {
59
69
  namespace PlaywrightTest {
60
70
  interface Matchers<R> {
71
+ /**
72
+ * Asserts that the clipboard content matches the expected text.
73
+ * Uses smart polling to wait for the clipboard to be updated.
74
+ *
75
+ * @param expected The string to compare against the clipboard content.
76
+ * @param options Matcher options.
77
+ * @returns A Promise that resolves when the assertion completes.
78
+ *
79
+ * @example
80
+ * await expect(clipboard).toHaveText('Copied value');
81
+ */
82
+ toHaveText(expected: string, options?: MatcherOptions): Promise<R>;
83
+ /**
84
+ * Asserts that the clipboard content matches the expected JSON value.
85
+ * Uses smart polling to wait for the clipboard to be updated.
86
+ *
87
+ * @param expected The JSON value to compare against the clipboard content.
88
+ * @param options Matcher options.
89
+ * @returns A Promise that resolves when the assertion completes.
90
+ *
91
+ * @example
92
+ * await expect(clipboard).toHaveJSON({ id: 123, status: 'success' });
93
+ */
94
+ toHaveJSON(expected: unknown, options?: MatcherOptions): Promise<R>;
61
95
  /**
62
96
  * Asserts that the clipboard content matches the expected value.
63
97
  * Uses smart polling to wait for the clipboard to be updated.
64
98
  * If the `expected` value is an object, it attempts to parse the clipboard
65
99
  * content as JSON before comparing.
66
100
  *
101
+ * @deprecated Use `toHaveText` or `toHaveJSON` instead. This matcher will be removed in future versions.
102
+ *
67
103
  * @param expected The string or object to compare against the clipboard content.
68
104
  * @param options Optional settings for the matcher, such as timeout.
69
105
  * @returns A Promise that resolves when the assertion completes.
@@ -89,12 +125,44 @@ declare global {
89
125
  * @param options Optional settings for the matcher, such as timeout.
90
126
  * @returns A Promise that resolves to a MatcherReturnType object.
91
127
  */
92
- declare function toHaveData(this: ExpectMatcherState, clipboard: ClipboardHandler, expected: unknown, options?: MatcherOptions): Promise<MatcherReturnType>;
128
+ declare function toHaveData(this: ExpectMatcherState, clipboard: ClipboardHandler, expected: unknown, options?: MatcherOptions): Promise<{
129
+ name: string;
130
+ message: () => string;
131
+ pass: boolean;
132
+ expected?: unknown;
133
+ actual?: any;
134
+ log?: string[];
135
+ timeout?: number;
136
+ }>;
137
+
138
+ /**
139
+ * Asserts that the clipboard content matches the expected JSON value.
140
+ *
141
+ * @this ExpectMatcherState
142
+ * @param clipboard The Clipboard utility instance.
143
+ * @param expected The expected JSON value.
144
+ * @param options Matcher options.
145
+ * @returns A Promise that resolves to a MatcherReturnType object.
146
+ */
147
+ declare function toHaveJSON(this: ExpectMatcherState, clipboard: ClipboardHandler, expected: unknown, options?: MatcherOptions): Promise<MatcherReturnType>;
148
+
149
+ /**
150
+ * Asserts that the clipboard content matches the expected text value.
151
+ *
152
+ * @this ExpectMatcherState
153
+ * @param clipboard The Clipboard utility instance.
154
+ * @param expected The expected text value.
155
+ * @param options Matcher options.
156
+ * @returns A Promise that resolves to a MatcherReturnType object.
157
+ */
158
+ declare function toHaveText(this: ExpectMatcherState, clipboard: ClipboardHandler, expected: string, options?: MatcherOptions): Promise<MatcherReturnType>;
93
159
 
94
160
  declare const test: _playwright_test.TestType<_playwright_test.PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & {
95
161
  clipboard: ClipboardHandler;
96
162
  }, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
97
163
  declare const expect: _playwright_test.Expect<{
164
+ toHaveText: typeof toHaveText;
165
+ toHaveJSON: typeof toHaveJSON;
98
166
  toHaveData: typeof toHaveData;
99
167
  }>;
100
168
 
@@ -102,7 +170,9 @@ declare const expect: _playwright_test.Expect<{
102
170
  * Export an object containing all the custom clipboard matchers for Playwright.
103
171
  */
104
172
  declare const clipboardMatchers: {
173
+ toHaveText: typeof toHaveText;
174
+ toHaveJSON: typeof toHaveJSON;
105
175
  toHaveData: typeof toHaveData;
106
176
  };
107
177
 
108
- export { ClipboardHandler, clipboardFixture, clipboardMatchers, expect, test };
178
+ export { ClipboardHandler, clipboardFixture, clipboardMatchers, expect, firefoxClipboardPrefs, test };
package/dist/index.js CHANGED
@@ -1,22 +1,34 @@
1
+ // src/constants.ts
2
+ var firefoxClipboardPrefs = {
3
+ "dom.events.testing.asyncClipboard": true,
4
+ "dom.events.asyncClipboard.readText": true,
5
+ "dom.events.asyncClipboard.writeText": true,
6
+ "permissions.default.clipboard-read": 1,
7
+ "permissions.default.clipboard-write": 1
8
+ };
9
+
1
10
  // src/utils/clipboardHandler.ts
2
11
  var ClipboardHandler = class {
3
- constructor(page, context) {
12
+ constructor(page, context, browserName = "chromium") {
4
13
  this.page = page;
5
14
  this.context = context;
15
+ this.browserName = browserName;
6
16
  this.isPermissionGranted = false;
7
17
  }
8
18
  page;
9
19
  context;
20
+ browserName;
10
21
  isPermissionGranted;
11
22
  /**
12
- * Grants the necessary clipboard permissions ('clipboard-read' and 'clipboard-write') to the browser context.
13
- * This method is called internally before any clipboard read operation to ensure permissions are set.
23
+ * Grants clipboard permissions to the Chromium browser context if not already granted.
14
24
  * @private
15
25
  */
16
26
  async grantPermissions() {
17
- if (!this.isPermissionGranted) {
18
- await this.context.grantPermissions(["clipboard-read", "clipboard-write"]);
19
- this.isPermissionGranted = true;
27
+ if (this.browserName === "chromium") {
28
+ if (!this.isPermissionGranted) {
29
+ await this.context.grantPermissions(["clipboard-read", "clipboard-write"]);
30
+ this.isPermissionGranted = true;
31
+ }
20
32
  }
21
33
  }
22
34
  /**
@@ -50,20 +62,20 @@ var ClipboardHandler = class {
50
62
 
51
63
  // src/fixtures/clipboardFixture.ts
52
64
  var clipboardFixture = async ({ page, context, browserName }, use) => {
53
- if (browserName !== "chromium") {
65
+ if (browserName === "webkit") {
54
66
  throw new Error(
55
- `[playwright-clipboard] Browser '${browserName}' is not supported. Clipboard Permissions API is currently only supported in Chromium-based browsers.
56
- Use test.skip(browserName !== 'chromium') in your tests to skip non-Chromium runs.`
67
+ `[playwright-clipboard] Browser '${browserName}' is not supported. Clipboard API testing is currently supported only in Chromium-based and Firefox browsers.
68
+ Add test.skip(browserName === 'webkit', 'Clipboard API is only supported in Chromium and Firefox'); to your test body.`
57
69
  );
58
70
  }
59
- const handler = new ClipboardHandler(page, context);
71
+ const handler = new ClipboardHandler(page, context, browserName);
60
72
  await use(handler);
61
73
  };
62
74
 
63
75
  // src/fixtures.ts
64
76
  import { expect as baseExpect, test as baseTest } from "@playwright/test";
65
77
 
66
- // src/matchers/toHaveData.ts
78
+ // src/matchers/toHaveJSON.ts
67
79
  import { expect } from "@playwright/test";
68
80
 
69
81
  // src/utils/matcherUtils.ts
@@ -82,23 +94,68 @@ ${error.message}`;
82
94
  Received: ${this.utils.printReceived(actual)}`;
83
95
  }
84
96
 
85
- // src/matchers/toHaveData.ts
86
- async function toHaveData(clipboard, expected, options = {}) {
87
- const name = "toHaveData";
97
+ // src/matchers/toHaveJSON.ts
98
+ async function toHaveJSON(clipboard, expected, options = {}) {
99
+ const name = "toHaveJSON";
88
100
  let pass;
89
101
  let actual;
102
+ let errorReason = null;
90
103
  const { timeout = 1e4 } = options;
91
104
  const poll = expect.poll(
92
105
  async () => {
93
106
  try {
94
107
  actual = await clipboard.readJSON();
95
- } catch {
96
- actual = await clipboard.read();
108
+ errorReason = null;
109
+ return actual;
110
+ } catch (error) {
111
+ errorReason = error instanceof Error ? error : new Error(String(error));
112
+ try {
113
+ actual = await clipboard.read();
114
+ } catch {
115
+ actual = void 0;
116
+ }
117
+ throw errorReason;
97
118
  }
98
- if (typeof expected === "string" && actual !== null && actual !== void 0) {
99
- return String(actual);
119
+ },
120
+ { timeout }
121
+ );
122
+ try {
123
+ const expectation = this.isNot ? poll.not : poll;
124
+ await expectation.toEqual(expected);
125
+ pass = true;
126
+ } catch {
127
+ pass = false;
128
+ }
129
+ if (this.isNot) pass = !pass;
130
+ const matcherReturn = {
131
+ message: getErrorMessage.call(this, name, expected, actual, errorReason),
132
+ pass,
133
+ name,
134
+ expected,
135
+ actual
136
+ };
137
+ return matcherReturn;
138
+ }
139
+
140
+ // src/matchers/toHaveText.ts
141
+ import { expect as expect2 } from "@playwright/test";
142
+ async function toHaveText(clipboard, expected, options = {}) {
143
+ const name = "toHaveText";
144
+ let pass;
145
+ let actual;
146
+ let errorReason = null;
147
+ const { timeout = 1e4 } = options;
148
+ const poll = expect2.poll(
149
+ async () => {
150
+ try {
151
+ actual = await clipboard.read();
152
+ errorReason = null;
153
+ return actual;
154
+ } catch (error) {
155
+ errorReason = error instanceof Error ? error : new Error(String(error));
156
+ actual = void 0;
157
+ throw errorReason;
100
158
  }
101
- return actual;
102
159
  },
103
160
  { timeout }
104
161
  );
@@ -111,7 +168,7 @@ async function toHaveData(clipboard, expected, options = {}) {
111
168
  }
112
169
  if (this.isNot) pass = !pass;
113
170
  const matcherReturn = {
114
- message: getErrorMessage.call(this, name, expected, actual),
171
+ message: getErrorMessage.call(this, name, expected, actual, errorReason),
115
172
  pass,
116
173
  name,
117
174
  expected,
@@ -120,8 +177,29 @@ async function toHaveData(clipboard, expected, options = {}) {
120
177
  return matcherReturn;
121
178
  }
122
179
 
180
+ // src/matchers/toHaveData.ts
181
+ async function toHaveData(clipboard, expected, options = {}) {
182
+ const name = "toHaveData";
183
+ let matcherReturn = null;
184
+ if (typeof expected === "string") {
185
+ matcherReturn = await toHaveText.call(this, clipboard, expected, options);
186
+ } else {
187
+ matcherReturn = await toHaveJSON.call(this, clipboard, expected, options);
188
+ }
189
+ return {
190
+ ...matcherReturn,
191
+ name,
192
+ message: () => {
193
+ const originalMessage = matcherReturn.message();
194
+ return originalMessage.replace("toHaveText", name).replace("toHaveJSON", name);
195
+ }
196
+ };
197
+ }
198
+
123
199
  // src/matchers/clipboardMatchers.ts
124
200
  var clipboardMatchers = {
201
+ toHaveText,
202
+ toHaveJSON,
125
203
  toHaveData
126
204
  };
127
205
 
@@ -133,12 +211,13 @@ var test = baseTest.extend({
133
211
  */
134
212
  clipboard: clipboardFixture
135
213
  });
136
- var expect2 = baseExpect.extend({ ...clipboardMatchers });
214
+ var expect3 = baseExpect.extend({ ...clipboardMatchers });
137
215
  export {
138
216
  ClipboardHandler,
139
217
  clipboardFixture,
140
218
  clipboardMatchers,
141
- expect2 as expect,
219
+ expect3 as expect,
220
+ firefoxClipboardPrefs,
142
221
  test
143
222
  };
144
223
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/clipboardHandler.ts","../src/fixtures/clipboardFixture.ts","../src/fixtures.ts","../src/matchers/toHaveData.ts","../src/utils/matcherUtils.ts","../src/matchers/clipboardMatchers.ts"],"sourcesContent":["/**\n * Playwright-compatible Clipboard utilities.\n *\n * Core functions for interacting with the browser clipboard within Playwright tests.\n * Provides wrappers for reading plain text and JSON.\n *\n * @remarks\n * These utilities require 'clipboard-read' and 'clipboard-write' permissions\n * to be granted in the browser context.\n */\n\nimport type { BrowserContext, Page } from '@playwright/test';\n\nexport class ClipboardHandler {\n private isPermissionGranted: boolean;\n\n constructor(\n private readonly page: Page,\n private readonly context: BrowserContext,\n ) {\n this.isPermissionGranted = false;\n }\n\n /**\n * Grants the necessary clipboard permissions ('clipboard-read' and 'clipboard-write') to the browser context.\n * This method is called internally before any clipboard read operation to ensure permissions are set.\n * @private\n */\n private async grantPermissions() {\n if (!this.isPermissionGranted) {\n await this.context.grantPermissions(['clipboard-read', 'clipboard-write']);\n this.isPermissionGranted = true;\n }\n }\n\n /**\n * Reads the current text content from the browser clipboard.\n * If the content is a JSON-encoded string (e.g., has extra quotes),\n * it will be returned as is. Use readJSON for automatic parsing.\n *\n * @returns A promise that resolves to the clipboard string content.\n */\n async read(): Promise<string> {\n await this.grantPermissions();\n return await this.page.evaluate(() => navigator.clipboard.readText());\n }\n\n /**\n * Reads the clipboard content and parses it as JSON.\n * If the content is a string literal (e.g., '\"value\"'), it returns the unwrapped string ('value').\n *\n * @template T - The expected type of the parsed JSON object.\n * @returns A promise that resolves to the parsed JSON object of type T.\n * @throws {Error} If the clipboard content is not a valid JSON string.\n */\n async readJSON<T = unknown>(): Promise<T> {\n const text = await this.read();\n try {\n return JSON.parse(text);\n } catch {\n throw new Error(`Clipboard content is not a valid JSON: ${JSON.stringify(text)}`);\n }\n }\n}\n","import type { BrowserContext, Page, TestFixture } from '@playwright/test';\nimport { ClipboardHandler } from '../utils/clipboardHandler.js';\n\n/**\n * A fixture that provides an instance of the Clipboard utility for interacting with the system clipboard.\n * It allows reading from the clipboard during tests.\n */\nexport const clipboardFixture: TestFixture<\n ClipboardHandler,\n { page: Page; context: BrowserContext; browserName: string }\n> = async ({ page, context, browserName }, use) => {\n if (browserName !== 'chromium') {\n throw new Error(\n `[playwright-clipboard] Browser '${browserName}' is not supported. ` +\n 'Clipboard Permissions API is currently only supported in Chromium-based browsers.\\n' +\n `Use test.skip(browserName !== 'chromium') in your tests to skip non-Chromium runs.`,\n );\n }\n\n const handler = new ClipboardHandler(page, context);\n await use(handler);\n};\n","import { expect as baseExpect, test as baseTest } from '@playwright/test';\nimport { clipboardFixture } from './fixtures/clipboardFixture.js';\nimport { clipboardMatchers } from './matchers/clipboardMatchers.js';\nimport type { ClipboardHandler } from './utils/clipboardHandler.js';\n\nexport const test = baseTest.extend<{\n clipboard: ClipboardHandler;\n}>({\n /**\n * A fixture that provides an instance of the Clipboard utility for interacting with the system clipboard.\n * It allows reading from the clipboard during tests.\n */\n clipboard: clipboardFixture,\n});\n\nexport const expect = baseExpect.extend({ ...clipboardMatchers });\n","import { type ExpectMatcherState, expect, type MatcherReturnType } from '@playwright/test';\nimport type { ClipboardHandler } from '../utils/clipboardHandler.js';\nimport { getErrorMessage } from '../utils/matcherUtils.js';\nimport type { MatcherOptions } from './types.js';\n\n/**\n * Asserts that the clipboard content matches the expected value.\n * Uses smart polling to wait for the clipboard to be updated.\n * If the `expected` value is an object, it attempts to parse the clipboard\n * content as JSON before comparing.\n *\n * @this ExpectMatcherState\n * @param clipboard The Clipboard utility instance.\n * @param expected The string or object to compare against the clipboard content.\n * @param options Optional settings for the matcher, such as timeout.\n * @returns A Promise that resolves to a MatcherReturnType object.\n */\nexport async function toHaveData(\n this: ExpectMatcherState,\n clipboard: ClipboardHandler,\n expected: unknown,\n options: MatcherOptions = {},\n) {\n const name = 'toHaveData';\n let pass: boolean;\n let actual: unknown;\n\n const { timeout = 10_000 } = options;\n\n const poll = expect.poll(\n async () => {\n try {\n actual = await clipboard.readJSON();\n } catch {\n actual = await clipboard.read();\n }\n\n if (typeof expected === 'string' && actual !== null && actual !== undefined) {\n return String(actual);\n }\n\n return actual;\n },\n { timeout },\n );\n\n try {\n const expectation = this.isNot ? poll.not : poll;\n await expectation.toEqual(expected);\n pass = true;\n } catch {\n pass = false;\n }\n\n /**\n * NOTE ON INVERSION:\n * In Playwright custom matchers, if `this.isNot` is true, the matcher must return\n * `pass: false` for the assertion to pass. Since `poll.not.toEqual` resolves\n * to `true` when the condition is met, we invert it back to align with Playwright's expectations.\n */\n if (this.isNot) pass = !pass;\n\n const matcherReturn: MatcherReturnType = {\n message: getErrorMessage.call(this, name, expected, actual),\n pass,\n name,\n expected,\n actual,\n };\n\n return matcherReturn;\n}\n","import type { ExpectMatcherState } from '@playwright/test';\n\n/**\n * Generates an error message for a custom matcher.\n *\n * @this ExpectMatcherState\n * @param name The name of the matcher.\n * @param expected The expected value.\n * @param actual The actual value.\n * @param error An optional error object.\n * @param errorMessage An optional error message string.\n * @returns A function that returns the error message string.\n */\nexport function getErrorMessage(\n this: ExpectMatcherState,\n name: string,\n expected: unknown,\n actual: unknown,\n error?: Error | null,\n errorMessage?: string | undefined | null,\n): () => string {\n const message = `${this.utils.matcherHint(name, undefined, undefined, { isNot: this.isNot })}\\n\\n`;\n\n if (error) {\n return () => `${message}An unexpected error occurred:\\n${error.message}`;\n }\n\n if (errorMessage) {\n return () => `${message}${errorMessage}`;\n }\n\n return () =>\n message +\n `Expected: ${this.isNot ? 'not ' : ''}${this.utils.printExpected(expected)}\\n` +\n `Received: ${this.utils.printReceived(actual)}`;\n}\n","import { toHaveData } from './toHaveData.js';\n\n/**\n * Export an object containing all the custom clipboard matchers for Playwright.\n */\nexport const clipboardMatchers = {\n toHaveData,\n};\n"],"mappings":"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","names":["expect"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/utils/clipboardHandler.ts","../src/fixtures/clipboardFixture.ts","../src/fixtures.ts","../src/matchers/toHaveJSON.ts","../src/utils/matcherUtils.ts","../src/matchers/toHaveText.ts","../src/matchers/toHaveData.ts","../src/matchers/clipboardMatchers.ts"],"sourcesContent":["export const firefoxClipboardPrefs = {\n 'dom.events.testing.asyncClipboard': true,\n 'dom.events.asyncClipboard.readText': true,\n 'dom.events.asyncClipboard.writeText': true,\n 'permissions.default.clipboard-read': 1,\n 'permissions.default.clipboard-write': 1,\n};\n","/**\n * Playwright-compatible Clipboard utilities.\n *\n * Core functions for interacting with the browser clipboard within Playwright tests.\n * Provides wrappers for reading plain text and JSON.\n *\n * @remarks\n * These utilities require 'clipboard-read' and 'clipboard-write' permissions\n * to be granted in the browser context.\n */\n\nimport type { BrowserContext, Page } from '@playwright/test';\nimport type { BrowserName } from '../types.js';\n\nexport class ClipboardHandler {\n private readonly browserName: BrowserName;\n private isPermissionGranted: boolean;\n\n constructor(\n private readonly page: Page,\n private readonly context: BrowserContext,\n browserName: BrowserName = 'chromium',\n ) {\n this.browserName = browserName;\n this.isPermissionGranted = false;\n }\n\n /**\n * Grants clipboard permissions to the Chromium browser context if not already granted.\n * @private\n */\n private async grantPermissions() {\n if (this.browserName === 'chromium') {\n if (!this.isPermissionGranted) {\n await this.context.grantPermissions(['clipboard-read', 'clipboard-write']);\n this.isPermissionGranted = true;\n }\n }\n }\n\n /**\n * Reads the current text content from the browser clipboard.\n * If the content is a JSON-encoded string (e.g., has extra quotes),\n * it will be returned as is. Use readJSON for automatic parsing.\n *\n * @returns A promise that resolves to the clipboard string content.\n */\n async read(): Promise<string> {\n await this.grantPermissions();\n return await this.page.evaluate(() => navigator.clipboard.readText());\n }\n\n /**\n * Reads the clipboard content and parses it as JSON.\n * If the content is a string literal (e.g., '\"value\"'), it returns the unwrapped string ('value').\n *\n * @template T - The expected type of the parsed JSON object.\n * @returns A promise that resolves to the parsed JSON object of type T.\n * @throws {Error} If the clipboard content is not a valid JSON string.\n */\n async readJSON<T = unknown>(): Promise<T> {\n const text = await this.read();\n try {\n return JSON.parse(text);\n } catch {\n throw new Error(`Clipboard content is not a valid JSON: ${JSON.stringify(text)}`);\n }\n }\n}\n","import type { BrowserContext, Page, TestFixture } from '@playwright/test';\nimport type { BrowserName } from '../types.js';\nimport { ClipboardHandler } from '../utils/clipboardHandler.js';\n\n/**\n * A fixture that provides an instance of the Clipboard utility for interacting with the system clipboard.\n * It allows reading from the clipboard during tests.\n */\nexport const clipboardFixture: TestFixture<\n ClipboardHandler,\n { page: Page; context: BrowserContext; browserName: BrowserName }\n> = async ({ page, context, browserName }, use) => {\n if (browserName === 'webkit') {\n throw new Error(\n `[playwright-clipboard] Browser '${browserName}' is not supported. ` +\n 'Clipboard API testing is currently supported only in Chromium-based and Firefox browsers.\\n' +\n `Add test.skip(browserName === 'webkit', 'Clipboard API is only supported in Chromium and Firefox'); to your test body.`,\n );\n }\n\n const handler = new ClipboardHandler(page, context, browserName);\n await use(handler);\n};\n","import { expect as baseExpect, test as baseTest } from '@playwright/test';\nimport { clipboardFixture } from './fixtures/clipboardFixture.js';\nimport { clipboardMatchers } from './matchers/clipboardMatchers.js';\nimport type { ClipboardHandler } from './utils/clipboardHandler.js';\n\nexport const test = baseTest.extend<{\n clipboard: ClipboardHandler;\n}>({\n /**\n * A fixture that provides an instance of the Clipboard utility for interacting with the system clipboard.\n * It allows reading from the clipboard during tests.\n */\n clipboard: clipboardFixture,\n});\n\nexport const expect = baseExpect.extend({ ...clipboardMatchers });\n","import type { ExpectMatcherState, MatcherReturnType } from '@playwright/test';\nimport { expect } from '@playwright/test';\nimport type { ClipboardHandler } from '../utils/clipboardHandler.js';\nimport { getErrorMessage } from '../utils/matcherUtils.js';\nimport type { MatcherOptions } from './types.js';\n\n/**\n * Asserts that the clipboard content matches the expected JSON value.\n *\n * @this ExpectMatcherState\n * @param clipboard The Clipboard utility instance.\n * @param expected The expected JSON value.\n * @param options Matcher options.\n * @returns A Promise that resolves to a MatcherReturnType object.\n */\nexport async function toHaveJSON(\n this: ExpectMatcherState,\n clipboard: ClipboardHandler,\n expected: unknown,\n options: MatcherOptions = {},\n) {\n const name = 'toHaveJSON';\n let pass: boolean;\n let actual: unknown;\n let errorReason: Error | null = null;\n\n const { timeout = 10_000 } = options;\n\n const poll = expect.poll(\n async () => {\n try {\n actual = await clipboard.readJSON();\n errorReason = null;\n return actual;\n } catch (error) {\n errorReason = error instanceof Error ? error : new Error(String(error));\n\n try {\n actual = await clipboard.read();\n } catch {\n actual = undefined;\n }\n\n throw errorReason;\n }\n },\n { timeout },\n );\n\n try {\n const expectation = this.isNot ? poll.not : poll;\n await expectation.toEqual(expected);\n pass = true;\n } catch {\n pass = false;\n }\n\n if (this.isNot) pass = !pass;\n\n const matcherReturn: MatcherReturnType = {\n message: getErrorMessage.call(this, name, expected, actual, errorReason),\n pass,\n name,\n expected,\n actual,\n };\n\n return matcherReturn;\n}\n","import type { ExpectMatcherState } from '@playwright/test';\n\n/**\n * Generates an error message for a custom matcher.\n *\n * @this ExpectMatcherState\n * @param name The name of the matcher.\n * @param expected The expected value.\n * @param actual The actual value.\n * @param error An optional error object.\n * @param errorMessage An optional error message string.\n * @returns A function that returns the error message string.\n */\nexport function getErrorMessage(\n this: ExpectMatcherState,\n name: string,\n expected: unknown,\n actual: unknown,\n error?: Error | null,\n errorMessage?: string | undefined | null,\n): () => string {\n const message = `${this.utils.matcherHint(name, undefined, undefined, { isNot: this.isNot })}\\n\\n`;\n\n if (error) {\n return () => `${message}An unexpected error occurred:\\n${error.message}`;\n }\n\n if (errorMessage) {\n return () => `${message}${errorMessage}`;\n }\n\n return () =>\n message +\n `Expected: ${this.isNot ? 'not ' : ''}${this.utils.printExpected(expected)}\\n` +\n `Received: ${this.utils.printReceived(actual)}`;\n}\n","import { type ExpectMatcherState, expect, type MatcherReturnType } from '@playwright/test';\nimport type { ClipboardHandler } from '../utils/clipboardHandler.js';\nimport { getErrorMessage } from '../utils/matcherUtils.js';\nimport type { MatcherOptions } from './types.js';\n\n/**\n * Asserts that the clipboard content matches the expected text value.\n *\n * @this ExpectMatcherState\n * @param clipboard The Clipboard utility instance.\n * @param expected The expected text value.\n * @param options Matcher options.\n * @returns A Promise that resolves to a MatcherReturnType object.\n */\nexport async function toHaveText(\n this: ExpectMatcherState,\n clipboard: ClipboardHandler,\n expected: string,\n options: MatcherOptions = {},\n) {\n const name = 'toHaveText';\n let pass: boolean;\n let actual: unknown;\n let errorReason: Error | null = null;\n\n const { timeout = 10_000 } = options;\n\n const poll = expect.poll(\n async () => {\n try {\n actual = await clipboard.read();\n errorReason = null;\n return actual;\n } catch (error) {\n errorReason = error instanceof Error ? error : new Error(String(error));\n actual = undefined;\n\n throw errorReason;\n }\n },\n { timeout },\n );\n\n try {\n const expectation = this.isNot ? poll.not : poll;\n await expectation.toEqual(expected);\n pass = true;\n } catch {\n pass = false;\n }\n\n if (this.isNot) pass = !pass;\n\n const matcherReturn: MatcherReturnType = {\n message: getErrorMessage.call(this, name, expected, actual, errorReason),\n pass,\n name,\n expected,\n actual,\n };\n\n return matcherReturn;\n}\n","import type { ExpectMatcherState, MatcherReturnType } from '@playwright/test';\nimport type { ClipboardHandler } from '../utils/clipboardHandler.js';\nimport { toHaveJSON } from './toHaveJSON.js';\nimport { toHaveText } from './toHaveText.js';\nimport type { MatcherOptions } from './types.js';\n\n/**\n * Asserts that the clipboard content matches the expected value.\n * Uses smart polling to wait for the clipboard to be updated.\n * If the `expected` value is an object, it attempts to parse the clipboard\n * content as JSON before comparing.\n *\n * @this ExpectMatcherState\n * @param clipboard The Clipboard utility instance.\n * @param expected The string or object to compare against the clipboard content.\n * @param options Optional settings for the matcher, such as timeout.\n * @returns A Promise that resolves to a MatcherReturnType object.\n */\nexport async function toHaveData(\n this: ExpectMatcherState,\n clipboard: ClipboardHandler,\n expected: unknown,\n options: MatcherOptions = {},\n) {\n const name = 'toHaveData';\n let matcherReturn: MatcherReturnType | null = null;\n\n if (typeof expected === 'string') {\n matcherReturn = await toHaveText.call(this, clipboard, expected, options);\n } else {\n matcherReturn = await toHaveJSON.call(this, clipboard, expected, options);\n }\n\n return {\n ...matcherReturn,\n name,\n message: () => {\n const originalMessage = matcherReturn.message();\n return originalMessage.replace('toHaveText', name).replace('toHaveJSON', name);\n },\n };\n}\n","import { toHaveData } from './toHaveData.js';\nimport { toHaveJSON } from './toHaveJSON.js';\nimport { toHaveText } from './toHaveText.js';\n\n/**\n * Export an object containing all the custom clipboard matchers for Playwright.\n */\nexport const clipboardMatchers = {\n toHaveText,\n toHaveJSON,\n toHaveData,\n};\n"],"mappings":"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","names":["expect","expect","expect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playwright-clipboard-testing",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Playwright fixture and custom matchers for clipboard testing and assertions.",
5
5
  "author": "Sergii Oleksenko <serg.oleksenko@gmail.com>",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@
40
40
  "dist"
41
41
  ],
42
42
  "engines": {
43
- "node": ">=18.0.0"
43
+ "node": ">=20.0.0"
44
44
  },
45
45
  "sideEffects": false,
46
46
  "scripts": {
@@ -50,13 +50,15 @@
50
50
  "lint:fix": "biome check --write .",
51
51
  "typecheck": "tsc --noEmit",
52
52
  "format": "biome format --write .",
53
- "test": "echo \"Error: no test specified\" && exit 1"
53
+ "test": "vitest run",
54
+ "test:watch": "vitest"
54
55
  },
55
56
  "devDependencies": {
56
57
  "@biomejs/biome": "2.5.10",
57
58
  "@playwright/test": "1.62.1",
58
59
  "tsup": "^8.5.1",
59
- "typescript": "5.8.3"
60
+ "typescript": "5.8.3",
61
+ "vitest": "^4.1.11"
60
62
  },
61
63
  "peerDependencies": {
62
64
  "@playwright/test": ">=1.40.0"