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 +83 -8
- package/dist/index.cjs +104 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -6
- package/dist/index.d.ts +76 -6
- package/dist/index.js +101 -22
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -2,11 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
# Playwright Clipboard Testing
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Effortless clipboard testing for Playwright. Features custom fixtures, smart polling matchers, and auto-managed permissions.
|
|
6
6
|
|
|
7
|
+
[](https://github.com/sergoleksenko/playwright-clipboard-testing/actions/workflows/tests.yml)
|
|
8
|
+
[](https://www.npmjs.com/package/playwright-clipboard-testing)
|
|
9
|
+
[](https://www.npmjs.com/package/playwright-clipboard-testing)
|
|
7
10
|
[](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
|
-

|
|
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
|
-
|
|
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
|
|
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.
|
|
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 {
|
|
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
|
-

|
|
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
|
+

|
|
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: () =>
|
|
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
|
|
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 (
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
96
|
+
if (browserName === "webkit") {
|
|
84
97
|
throw new Error(
|
|
85
|
-
`[playwright-clipboard] Browser '${browserName}' is not supported. Clipboard
|
|
86
|
-
|
|
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
|
|
107
|
+
var import_test3 = require("@playwright/test");
|
|
95
108
|
|
|
96
|
-
// src/matchers/
|
|
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/
|
|
116
|
-
async function
|
|
117
|
-
const name = "
|
|
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
|
-
|
|
126
|
-
actual
|
|
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
|
-
|
|
129
|
-
|
|
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 =
|
|
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
|
|
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
|
package/dist/index.cjs.map
CHANGED
|
@@ -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
|
|
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:
|
|
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<
|
|
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
|
|
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:
|
|
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<
|
|
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
|
|
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 (
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
65
|
+
if (browserName === "webkit") {
|
|
54
66
|
throw new Error(
|
|
55
|
-
`[playwright-clipboard] Browser '${browserName}' is not supported. Clipboard
|
|
56
|
-
|
|
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/
|
|
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/
|
|
86
|
-
async function
|
|
87
|
-
const name = "
|
|
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
|
-
|
|
96
|
-
actual
|
|
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
|
-
|
|
99
|
-
|
|
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
|
|
214
|
+
var expect3 = baseExpect.extend({ ...clipboardMatchers });
|
|
137
215
|
export {
|
|
138
216
|
ClipboardHandler,
|
|
139
217
|
clipboardFixture,
|
|
140
218
|
clipboardMatchers,
|
|
141
|
-
|
|
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.
|
|
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": ">=
|
|
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": "
|
|
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"
|