playwright-clipboard-testing 0.3.0 → 0.5.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 +81 -44
- package/dist/index.cjs +190 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +135 -79
- package/dist/index.d.ts +135 -79
- package/dist/index.js +187 -76
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Testing the Clipboard API in Playwright usually requires boilerplate code to man
|
|
|
15
15
|
**playwright-clipboard-testing** simplifies this with:
|
|
16
16
|
- 🔌 **Zero configuration** — Chromium permissions are granted automatically under the hood (Firefox requires a 1-line config setup).
|
|
17
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>()`.
|
|
18
|
+
- 📦 **TypeScript Ready** — Full type safety for JSON objects with `readJSON<T>()` and `writeJSON<T>()`.
|
|
19
19
|
|
|
20
20
|
## Table of Contents
|
|
21
21
|
- [Why?](#why)
|
|
@@ -25,10 +25,12 @@ Testing the Clipboard API in Playwright usually requires boilerplate code to man
|
|
|
25
25
|
- [Direct Usage](#direct-usage)
|
|
26
26
|
- [Extended Usage](#extended-usage)
|
|
27
27
|
- [API](#api)
|
|
28
|
-
- [
|
|
29
|
-
- [
|
|
30
|
-
- [
|
|
31
|
-
- [
|
|
28
|
+
- [Fixtures](#fixtures)
|
|
29
|
+
- [ClipboardHandler](#clipboardhandler)
|
|
30
|
+
- [toBeBlank Matcher](#tobeblank-matcher)
|
|
31
|
+
- [toHaveTextContent Matcher](#tohavetextcontent-matcher)
|
|
32
|
+
- [toHaveJSONContent Matcher](#tohavejsoncontent-matcher)
|
|
33
|
+
- [PATTERNS](#patterns)
|
|
32
34
|
- [Author](#author)
|
|
33
35
|
- [License](#license)
|
|
34
36
|
|
|
@@ -84,85 +86,120 @@ test('should copy text to clipboard', async ({ page, clipboard }) => {
|
|
|
84
86
|
await page.goto('https://example.com');
|
|
85
87
|
await page.locator('#copy-button').click();
|
|
86
88
|
|
|
87
|
-
await expect(clipboard).
|
|
89
|
+
await expect(clipboard).toHaveTextContent('Hello, World!');
|
|
88
90
|
});
|
|
89
91
|
```
|
|
90
92
|
|
|
91
93
|
### Extended Usage
|
|
92
94
|
|
|
93
|
-
If you already have a custom test fixture file, extend Playwright's `test` and `expect` with `
|
|
95
|
+
If you already have a custom test fixture file, extend Playwright's `test` and `expect` with `clipboardFixtures` and `clipboardMatchers`:
|
|
94
96
|
```ts
|
|
95
97
|
import { expect as baseExpect, test as baseTest } from '@playwright/test';
|
|
96
98
|
import {
|
|
97
99
|
type ClipboardHandler,
|
|
98
|
-
|
|
100
|
+
clipboardFixtures,
|
|
99
101
|
clipboardMatchers,
|
|
100
102
|
} from 'playwright-clipboard-testing';
|
|
101
103
|
|
|
102
|
-
export const test = baseTest.extend<{ clipboard: ClipboardHandler }>(
|
|
103
|
-
clipboard: clipboardFixture,
|
|
104
|
-
});
|
|
104
|
+
export const test = baseTest.extend<{ clipboard: ClipboardHandler }>(clipboardFixtures);
|
|
105
105
|
|
|
106
106
|
export const expect = baseExpect.extend(clipboardMatchers);
|
|
107
|
-
|
|
108
107
|
```
|
|
109
108
|
|
|
110
109
|
## API
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
|
|
111
|
+
### Fixtures
|
|
112
|
+
The package exports the following fixtures for Playwright test configuration:
|
|
113
|
+
|
|
114
|
+
- `clipboardFixtures` — Object containing both `context` and `clipboard` fixtures. Recommended for extending test fixtures.
|
|
115
|
+
- `contextFixture` (`context`) — Playwright context fixture that automatically grants `clipboard-read` and `clipboard-write` permissions in Chromium.
|
|
116
|
+
- `clipboardFixture` (`clipboard`) — Playwright test fixture that provides access to the `ClipboardHandler` instance in your tests.
|
|
117
|
+
|
|
118
|
+
### ClipboardHandler
|
|
119
|
+
The `clipboard` fixture provides direct access to the `ClipboardHandler` instance for managing clipboard state:
|
|
120
|
+
|
|
121
|
+
| Method | Return Type | Description |
|
|
122
|
+
|:-------------------------------|:------------------|:-------------------------------------------------------------------------------------------------------------------------|
|
|
123
|
+
| `clipboard.read()` | `Promise<string>` | Reads plain text content from the clipboard. |
|
|
124
|
+
| `clipboard.write(text)` | `Promise<void>` | Writes the specified plain text to the clipboard. |
|
|
125
|
+
| `clipboard.readJSON<T>()` | `Promise<T>` | Reads clipboard content and parses it as a JSON object of type `T`. Throws an error if invalid JSON. |
|
|
126
|
+
| `clipboard.writeJSON<T>(data)` | `Promise<void>` | Serializes an object of type `T` to JSON and writes it to the clipboard. Throws an error if object cannot be serialized. |
|
|
127
|
+
| `clipboard.clear()` | `Promise<void>` | Clears all clipboard content. |
|
|
115
128
|
|
|
116
129
|

|
|
117
130
|
|
|
118
|
-
###
|
|
119
|
-
`expect(clipboard).
|
|
131
|
+
### toBeBlank Matcher
|
|
132
|
+
`expect(clipboard).toBeBlank(options?)`
|
|
133
|
+
Asserts that the clipboard is empty. Uses Playwright's smart polling mechanism to wait for the clipboard to update.
|
|
134
|
+
- `options.timeout: number (optional, default: 10000ms)`
|
|
135
|
+
- `options.trim: boolean (optional, default: false)` — Trims whitespace from the clipboard content before checking if it's blank.
|
|
136
|
+
```ts
|
|
137
|
+
// assert that the clipboard is empty
|
|
138
|
+
await expect(clipboard).toBeBlank();
|
|
139
|
+
```
|
|
140
|
+
```ts
|
|
141
|
+
// assert that the clipboard is not empty with a custom timeout
|
|
142
|
+
await expect(clipboard).not.toBeBlank({ timeout: 5000 });
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### toHaveTextContent Matcher
|
|
146
|
+
`expect(clipboard).toHaveTextContent(expected, options?)`
|
|
120
147
|
|
|
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
|
|
148
|
+
Asserts that the clipboard content matches the expected string or regular expression. Uses Playwright's smart polling mechanism to wait for the clipboard to update.
|
|
149
|
+
- `expected: string | RegExp` — Expected text string or regular expression to match against.
|
|
123
150
|
- `options.timeout: number (optional, default: 10000ms)` — Time in milliseconds to wait for the clipboard content to match.
|
|
151
|
+
- `options.ignoreCase: boolean (optional, default: false)` — Ignores case when comparing strings or matching regular expressions.
|
|
152
|
+
- `options.trim: boolean (optional, default: false)` — Trims whitespace from both expected and actual string before comparison.
|
|
124
153
|
```ts
|
|
125
154
|
// assert that the clipboard contains the expected text
|
|
126
|
-
await expect(clipboard).
|
|
155
|
+
await expect(clipboard).toHaveTextContent('Hello, World!');
|
|
127
156
|
```
|
|
128
157
|
```ts
|
|
129
|
-
//
|
|
130
|
-
await expect(clipboard).
|
|
158
|
+
// assert that the clipboard text matches a regular expression
|
|
159
|
+
await expect(clipboard).toHaveTextContent(/Hello, World!/i);
|
|
160
|
+
```
|
|
161
|
+
```ts
|
|
162
|
+
// assert that the clipboard is not containing the expected text with a custom timeout
|
|
163
|
+
await expect(clipboard).not.toHaveTextContent('Async copied value', { timeout: 5000 });
|
|
131
164
|
```
|
|
132
165
|
|
|
133
|
-
###
|
|
134
|
-
`expect(clipboard).
|
|
166
|
+
### toHaveJSONContent Matcher
|
|
167
|
+
`expect(clipboard).toHaveJSONContent(expected, options?)`
|
|
135
168
|
Asserts that the clipboard content matches the expected JSON value. Uses Playwright's smart polling mechanism to wait for the clipboard to update.
|
|
136
169
|
- `expected: unknown` — Expected JSON value to compare against.
|
|
137
170
|
- `options.timeout: number (optional, default: 10000ms)` — Time in milliseconds to wait for the clipboard content to match.
|
|
138
171
|
```ts
|
|
139
172
|
// assert that the clipboard contains the expected JSON data
|
|
140
|
-
await expect(clipboard).
|
|
173
|
+
await expect(clipboard).toHaveJSONContent({ message: 'Hello, World!' });
|
|
141
174
|
```
|
|
142
175
|
```ts
|
|
143
|
-
//
|
|
144
|
-
await expect(clipboard).
|
|
176
|
+
// assert that the clipboard is not containing the expected JSON data with a custom timeout
|
|
177
|
+
await expect(clipboard).not.toHaveJSONContent({ message: 'Async copied value' }, { timeout: 5000 });
|
|
145
178
|
```
|
|
146
179
|
|
|
147
|
-
###
|
|
148
|
-
|
|
180
|
+
### PATTERNS
|
|
181
|
+
The package exports pre-defined regular expression patterns for common data formats (`PATTERNS`), which can be passed directly to `toHaveTextContent`:
|
|
149
182
|
|
|
150
|
-
|
|
183
|
+
| Pattern | Description | Example Match |
|
|
184
|
+
|:---------------------|:-------------------------------------|:------------------------------------------|
|
|
185
|
+
| `PATTERNS.UUID` | UUID v1–v5 format | `123e4567-e89b-12d3-a456-426614174000` |
|
|
186
|
+
| `PATTERNS.EMAIL` | Email address format | `user@example.com` |
|
|
187
|
+
| `PATTERNS.JWT` | JWT token format | `header.payload.signature` |
|
|
188
|
+
| `PATTERNS.BEARER` | Bearer authentication token format | `Bearer token123` |
|
|
189
|
+
| `PATTERNS.HEX_COLOR` | HEX color format | `#FFF`, `#FFFFFF`, `#FFFFFFFF` |
|
|
190
|
+
| `PATTERNS.IP.V4` | IPv4 address format | `192.168.1.1` |
|
|
191
|
+
| `PATTERNS.IP.V6` | IPv6 address format | `2001:0db8:85a3:0000:0000:8a2e:0370:7334` |
|
|
192
|
+
| `PATTERNS.IP.ANY` | Any IP address format (IPv4 or IPv6) | `192.168.1.1` or `2001:db8::1` |
|
|
151
193
|
|
|
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.
|
|
153
|
-
- `expected: unknown` — Expected text or object to compare against.
|
|
154
|
-
- `options.timeout: number (optional, default: 10000ms)` — Time in milliseconds to wait for the clipboard data to match.
|
|
155
|
-
```ts
|
|
156
|
-
// assert that the clipboard contains the expected text
|
|
157
|
-
await expect(clipboard).toHaveData('Hello, World!');
|
|
158
|
-
```
|
|
159
194
|
```ts
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
195
|
+
import { test, expect, PATTERNS } from 'playwright-clipboard-testing';
|
|
196
|
+
|
|
197
|
+
test('should copy UUID to clipboard', async ({ page, clipboard }) => {
|
|
198
|
+
await page.goto('https://example.com');
|
|
199
|
+
await page.locator('#copy-uuid-button').click();
|
|
200
|
+
|
|
201
|
+
await expect(clipboard).toHaveTextContent(PATTERNS.UUID);
|
|
202
|
+
});
|
|
166
203
|
```
|
|
167
204
|
|
|
168
205
|
## Author
|
package/dist/index.cjs
CHANGED
|
@@ -21,47 +21,26 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
ClipboardHandler: () => ClipboardHandler,
|
|
24
|
+
PATTERNS: () => PATTERNS,
|
|
24
25
|
clipboardFixture: () => clipboardFixture,
|
|
26
|
+
clipboardFixtures: () => clipboardFixtures,
|
|
25
27
|
clipboardMatchers: () => clipboardMatchers,
|
|
26
|
-
|
|
28
|
+
contextFixture: () => contextFixture,
|
|
29
|
+
expect: () => expect4,
|
|
27
30
|
firefoxClipboardPrefs: () => firefoxClipboardPrefs,
|
|
28
31
|
test: () => test
|
|
29
32
|
});
|
|
30
33
|
module.exports = __toCommonJS(index_exports);
|
|
31
34
|
|
|
32
|
-
// src/
|
|
33
|
-
var
|
|
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
|
-
};
|
|
35
|
+
// src/baseFixtures.ts
|
|
36
|
+
var import_test4 = require("@playwright/test");
|
|
40
37
|
|
|
41
38
|
// src/utils/clipboardHandler.ts
|
|
42
39
|
var ClipboardHandler = class {
|
|
43
|
-
constructor(page
|
|
40
|
+
constructor(page) {
|
|
44
41
|
this.page = page;
|
|
45
|
-
this.context = context;
|
|
46
|
-
this.browserName = browserName;
|
|
47
|
-
this.isPermissionGranted = false;
|
|
48
42
|
}
|
|
49
43
|
page;
|
|
50
|
-
context;
|
|
51
|
-
browserName;
|
|
52
|
-
isPermissionGranted;
|
|
53
|
-
/**
|
|
54
|
-
* Grants clipboard permissions to the Chromium browser context if not already granted.
|
|
55
|
-
* @private
|
|
56
|
-
*/
|
|
57
|
-
async grantPermissions() {
|
|
58
|
-
if (this.browserName === "chromium") {
|
|
59
|
-
if (!this.isPermissionGranted) {
|
|
60
|
-
await this.context.grantPermissions(["clipboard-read", "clipboard-write"]);
|
|
61
|
-
this.isPermissionGranted = true;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
44
|
/**
|
|
66
45
|
* Reads the current text content from the browser clipboard.
|
|
67
46
|
* If the content is a JSON-encoded string (e.g., has extra quotes),
|
|
@@ -70,7 +49,6 @@ var ClipboardHandler = class {
|
|
|
70
49
|
* @returns A promise that resolves to the clipboard string content.
|
|
71
50
|
*/
|
|
72
51
|
async read() {
|
|
73
|
-
await this.grantPermissions();
|
|
74
52
|
return await this.page.evaluate(() => navigator.clipboard.readText());
|
|
75
53
|
}
|
|
76
54
|
/**
|
|
@@ -86,27 +64,72 @@ var ClipboardHandler = class {
|
|
|
86
64
|
try {
|
|
87
65
|
return JSON.parse(text);
|
|
88
66
|
} catch {
|
|
89
|
-
throw new Error(
|
|
67
|
+
throw new Error(
|
|
68
|
+
`[playwright-clipboard] Clipboard content is not a valid JSON: ${JSON.stringify(text)}`
|
|
69
|
+
);
|
|
90
70
|
}
|
|
91
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Writes the provided string data to the browser clipboard.
|
|
74
|
+
* @param data The data to write to the clipboard.
|
|
75
|
+
*/
|
|
76
|
+
async write(data) {
|
|
77
|
+
await this.page.evaluate((value) => navigator.clipboard.writeText(value), data);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Writes the provided data to the browser clipboard as a JSON string.
|
|
81
|
+
* @param data The data to write to the clipboard. It will be stringified as JSON.
|
|
82
|
+
*/
|
|
83
|
+
async writeJSON(data) {
|
|
84
|
+
const errorMessage = "[playwright-clipboard] Provided data cannot be stringified to valid JSON";
|
|
85
|
+
let jsonString;
|
|
86
|
+
try {
|
|
87
|
+
jsonString = JSON.stringify(data);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
const message = error instanceof Error ? error : String(error);
|
|
90
|
+
throw new Error(`${errorMessage}: ${message}`);
|
|
91
|
+
}
|
|
92
|
+
if (jsonString === void 0) {
|
|
93
|
+
throw new Error(`${errorMessage} (received undefined).`);
|
|
94
|
+
}
|
|
95
|
+
await this.write(jsonString);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Clears the browser clipboard by writing an empty string to it.
|
|
99
|
+
* This effectively removes any existing content from the clipboard.
|
|
100
|
+
*/
|
|
101
|
+
async clear() {
|
|
102
|
+
await this.write("");
|
|
103
|
+
}
|
|
92
104
|
};
|
|
93
105
|
|
|
94
106
|
// src/fixtures/clipboardFixture.ts
|
|
95
|
-
var clipboardFixture = async ({ page,
|
|
107
|
+
var clipboardFixture = async ({ page, browserName }, use) => {
|
|
96
108
|
if (browserName === "webkit") {
|
|
97
109
|
throw new Error(
|
|
98
110
|
`[playwright-clipboard] Browser '${browserName}' is not supported. Clipboard API testing is currently supported only in Chromium-based and Firefox browsers.
|
|
99
111
|
Add test.skip(browserName === 'webkit', 'Clipboard API is only supported in Chromium and Firefox'); to your test body.`
|
|
100
112
|
);
|
|
101
113
|
}
|
|
102
|
-
const handler = new ClipboardHandler(page
|
|
114
|
+
const handler = new ClipboardHandler(page);
|
|
103
115
|
await use(handler);
|
|
104
116
|
};
|
|
105
117
|
|
|
106
|
-
// src/fixtures.ts
|
|
107
|
-
var
|
|
118
|
+
// src/fixtures/contextFixture.ts
|
|
119
|
+
var contextFixture = async ({ context, browserName }, use) => {
|
|
120
|
+
if (browserName === "chromium") {
|
|
121
|
+
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
|
|
122
|
+
}
|
|
123
|
+
await use(context);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// src/fixtures/index.ts
|
|
127
|
+
var clipboardFixtures = {
|
|
128
|
+
context: contextFixture,
|
|
129
|
+
clipboard: clipboardFixture
|
|
130
|
+
};
|
|
108
131
|
|
|
109
|
-
// src/matchers/
|
|
132
|
+
// src/matchers/toBeBlank.ts
|
|
110
133
|
var import_test = require("@playwright/test");
|
|
111
134
|
|
|
112
135
|
// src/utils/matcherUtils.ts
|
|
@@ -124,27 +147,43 @@ ${error.message}`;
|
|
|
124
147
|
return () => message + `Expected: ${this.isNot ? "not " : ""}${this.utils.printExpected(expected)}
|
|
125
148
|
Received: ${this.utils.printReceived(actual)}`;
|
|
126
149
|
}
|
|
150
|
+
function normalizeText(data, options = {}) {
|
|
151
|
+
const { ignoreCase, trim } = options;
|
|
152
|
+
if (typeof data === "string") {
|
|
153
|
+
let result = data;
|
|
154
|
+
if (trim) result = result.trim();
|
|
155
|
+
if (ignoreCase) result = result.toLowerCase();
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
if (data instanceof RegExp) {
|
|
159
|
+
const flags = data.flags.replace(/[gy]/g, "");
|
|
160
|
+
const clearFlags = ignoreCase && !flags.includes("i") ? `${flags}i` : flags;
|
|
161
|
+
return new RegExp(data.source, clearFlags);
|
|
162
|
+
}
|
|
163
|
+
return data;
|
|
164
|
+
}
|
|
127
165
|
|
|
128
|
-
// src/matchers/
|
|
129
|
-
async function
|
|
130
|
-
const name = "
|
|
166
|
+
// src/matchers/toBeBlank.ts
|
|
167
|
+
async function toBeBlank(clipboard, options = {}) {
|
|
168
|
+
const name = "toBeBlank";
|
|
131
169
|
let pass;
|
|
132
170
|
let actual;
|
|
171
|
+
let normalizedActual;
|
|
172
|
+
const expected = "";
|
|
133
173
|
let errorReason = null;
|
|
134
|
-
const { timeout = 1e4 } = options;
|
|
174
|
+
const { timeout = 1e4, trim = false } = options;
|
|
135
175
|
const poll = import_test.expect.poll(
|
|
136
176
|
async () => {
|
|
137
177
|
try {
|
|
138
|
-
actual = await clipboard.
|
|
178
|
+
actual = await clipboard.read();
|
|
179
|
+
normalizedActual = actual;
|
|
180
|
+
normalizedActual = normalizeText(normalizedActual, { trim });
|
|
139
181
|
errorReason = null;
|
|
140
|
-
return
|
|
182
|
+
return normalizedActual;
|
|
141
183
|
} catch (error) {
|
|
142
184
|
errorReason = error instanceof Error ? error : new Error(String(error));
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
} catch {
|
|
146
|
-
actual = void 0;
|
|
147
|
-
}
|
|
185
|
+
actual = void 0;
|
|
186
|
+
normalizedActual = void 0;
|
|
148
187
|
throw errorReason;
|
|
149
188
|
}
|
|
150
189
|
},
|
|
@@ -168,10 +207,10 @@ async function toHaveJSON(clipboard, expected, options = {}) {
|
|
|
168
207
|
return matcherReturn;
|
|
169
208
|
}
|
|
170
209
|
|
|
171
|
-
// src/matchers/
|
|
210
|
+
// src/matchers/toHaveJSONContent.ts
|
|
172
211
|
var import_test2 = require("@playwright/test");
|
|
173
|
-
async function
|
|
174
|
-
const name = "
|
|
212
|
+
async function toHaveJSONContent(clipboard, expected, options = {}) {
|
|
213
|
+
const name = "toHaveJSONContent";
|
|
175
214
|
let pass;
|
|
176
215
|
let actual;
|
|
177
216
|
let errorReason = null;
|
|
@@ -179,12 +218,16 @@ async function toHaveText(clipboard, expected, options = {}) {
|
|
|
179
218
|
const poll = import_test2.expect.poll(
|
|
180
219
|
async () => {
|
|
181
220
|
try {
|
|
182
|
-
actual = await clipboard.
|
|
221
|
+
actual = await clipboard.readJSON();
|
|
183
222
|
errorReason = null;
|
|
184
223
|
return actual;
|
|
185
224
|
} catch (error) {
|
|
186
225
|
errorReason = error instanceof Error ? error : new Error(String(error));
|
|
187
|
-
|
|
226
|
+
try {
|
|
227
|
+
actual = await clipboard.read();
|
|
228
|
+
} catch {
|
|
229
|
+
actual = void 0;
|
|
230
|
+
}
|
|
188
231
|
throw errorReason;
|
|
189
232
|
}
|
|
190
233
|
},
|
|
@@ -208,46 +251,117 @@ async function toHaveText(clipboard, expected, options = {}) {
|
|
|
208
251
|
return matcherReturn;
|
|
209
252
|
}
|
|
210
253
|
|
|
211
|
-
// src/matchers/
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
254
|
+
// src/matchers/toHaveTextContent.ts
|
|
255
|
+
var import_test3 = require("@playwright/test");
|
|
256
|
+
async function toHaveTextContent(clipboard, expected, options = {}) {
|
|
257
|
+
const name = "toHaveTextContent";
|
|
258
|
+
let pass;
|
|
259
|
+
let actual;
|
|
260
|
+
let normalizedActual;
|
|
261
|
+
let errorReason = null;
|
|
262
|
+
const { timeout = 1e4, ignoreCase = false, trim = false } = options;
|
|
263
|
+
const normalizedExpected = normalizeText(expected, { ignoreCase, trim });
|
|
264
|
+
const poll = import_test3.expect.poll(
|
|
265
|
+
async () => {
|
|
266
|
+
try {
|
|
267
|
+
actual = await clipboard.read();
|
|
268
|
+
normalizedActual = actual;
|
|
269
|
+
normalizedActual = normalizeText(normalizedActual, { ignoreCase, trim });
|
|
270
|
+
errorReason = null;
|
|
271
|
+
return normalizedActual;
|
|
272
|
+
} catch (error) {
|
|
273
|
+
errorReason = error instanceof Error ? error : new Error(String(error));
|
|
274
|
+
actual = void 0;
|
|
275
|
+
normalizedActual = void 0;
|
|
276
|
+
throw errorReason;
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
{ timeout }
|
|
280
|
+
);
|
|
281
|
+
try {
|
|
282
|
+
const expectation = this.isNot ? poll.not : poll;
|
|
283
|
+
if (expected instanceof RegExp) {
|
|
284
|
+
await expectation.toMatch(normalizedExpected);
|
|
285
|
+
} else {
|
|
286
|
+
await expectation.toEqual(normalizedExpected);
|
|
226
287
|
}
|
|
288
|
+
pass = true;
|
|
289
|
+
} catch {
|
|
290
|
+
pass = false;
|
|
291
|
+
}
|
|
292
|
+
if (this.isNot) pass = !pass;
|
|
293
|
+
const matcherReturn = {
|
|
294
|
+
message: getErrorMessage.call(this, name, expected, actual, errorReason),
|
|
295
|
+
pass,
|
|
296
|
+
name,
|
|
297
|
+
expected,
|
|
298
|
+
actual
|
|
227
299
|
};
|
|
300
|
+
return matcherReturn;
|
|
228
301
|
}
|
|
229
302
|
|
|
230
303
|
// src/matchers/clipboardMatchers.ts
|
|
231
304
|
var clipboardMatchers = {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
305
|
+
toBeBlank,
|
|
306
|
+
toHaveJSONContent,
|
|
307
|
+
toHaveTextContent
|
|
235
308
|
};
|
|
236
309
|
|
|
237
|
-
// src/
|
|
238
|
-
var test =
|
|
310
|
+
// src/baseFixtures.ts
|
|
311
|
+
var test = import_test4.test.extend(clipboardFixtures);
|
|
312
|
+
var expect4 = import_test4.expect.extend({ ...clipboardMatchers });
|
|
313
|
+
|
|
314
|
+
// src/constants.ts
|
|
315
|
+
var firefoxClipboardPrefs = {
|
|
316
|
+
"dom.events.testing.asyncClipboard": true,
|
|
317
|
+
"dom.events.asyncClipboard.readText": true,
|
|
318
|
+
"dom.events.asyncClipboard.writeText": true,
|
|
319
|
+
"permissions.default.clipboard-read": 1,
|
|
320
|
+
"permissions.default.clipboard-write": 1
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// src/patterns.ts
|
|
324
|
+
var PATTERNS = {
|
|
239
325
|
/**
|
|
240
|
-
*
|
|
241
|
-
* It allows reading from the clipboard during tests.
|
|
326
|
+
* UUID v1-v5 (example: 123e4567-e89b-12d3-a456-426614174000).
|
|
242
327
|
*/
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
328
|
+
UUID: /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
|
|
329
|
+
/**
|
|
330
|
+
* Email addresses (example: user@example.com).
|
|
331
|
+
*/
|
|
332
|
+
EMAIL: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
|
333
|
+
/**
|
|
334
|
+
* JWT tokens (example: header.payload.signature).
|
|
335
|
+
*/
|
|
336
|
+
JWT: /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/,
|
|
337
|
+
/**
|
|
338
|
+
* Authorization Bearer tokens (example: Bearer <token>).
|
|
339
|
+
*/
|
|
340
|
+
BEARER: /^Bearer\s+[a-zA-Z0-9._~+/-]+=*$/i,
|
|
341
|
+
/**
|
|
342
|
+
* HEX-colors (example: #FFF, #FFFFFF, #FFFFFFFF).
|
|
343
|
+
*/
|
|
344
|
+
HEX_COLOR: /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i,
|
|
345
|
+
/**
|
|
346
|
+
* IP (IPv4, IPv6 or any).
|
|
347
|
+
*/
|
|
348
|
+
IP: {
|
|
349
|
+
/** IPv4 (example: 192.168.1.1 or 127.0.0.1) */
|
|
350
|
+
V4: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
|
|
351
|
+
/** IPv6 (example: 2001:0db8:85a3:0000:0000:8a2e:0370:7334) */
|
|
352
|
+
V6: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,
|
|
353
|
+
/** Any IP address (IPv4 or IPv6) */
|
|
354
|
+
ANY: /^(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])))$/
|
|
355
|
+
}
|
|
356
|
+
};
|
|
246
357
|
// Annotate the CommonJS export names for ESM import in node:
|
|
247
358
|
0 && (module.exports = {
|
|
248
359
|
ClipboardHandler,
|
|
360
|
+
PATTERNS,
|
|
249
361
|
clipboardFixture,
|
|
362
|
+
clipboardFixtures,
|
|
250
363
|
clipboardMatchers,
|
|
364
|
+
contextFixture,
|
|
251
365
|
expect,
|
|
252
366
|
firefoxClipboardPrefs,
|
|
253
367
|
test
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/baseFixtures.ts","../src/utils/clipboardHandler.ts","../src/fixtures/clipboardFixture.ts","../src/fixtures/contextFixture.ts","../src/fixtures/index.ts","../src/matchers/toBeBlank.ts","../src/utils/matcherUtils.ts","../src/matchers/toHaveJSONContent.ts","../src/matchers/toHaveTextContent.ts","../src/matchers/clipboardMatchers.ts","../src/constants.ts","../src/patterns.ts"],"sourcesContent":["export { expect, test } from './baseFixtures';\nexport { firefoxClipboardPrefs } from './constants';\nexport { clipboardFixture, clipboardFixtures, contextFixture } from './fixtures/index';\nexport { clipboardMatchers } from './matchers/index';\nexport { PATTERNS } from './patterns';\nexport { ClipboardHandler } from './utils/index';\n","import { expect as baseExpect, test as baseTest } from '@playwright/test';\nimport { clipboardFixtures } from './fixtures';\nimport { clipboardMatchers } from './matchers';\nimport type { ClipboardHandler } from './utils';\n\nexport const test = baseTest.extend<{\n clipboard: ClipboardHandler;\n}>(clipboardFixtures);\n\nexport const expect = baseExpect.extend({ ...clipboardMatchers });\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 { Page } from '@playwright/test';\n\nexport class ClipboardHandler {\n constructor(private readonly page: Page) {}\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 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(\n `[playwright-clipboard] Clipboard content is not a valid JSON: ${JSON.stringify(text)}`,\n );\n }\n }\n\n /**\n * Writes the provided string data to the browser clipboard.\n * @param data The data to write to the clipboard.\n */\n async write(data: string): Promise<void> {\n await this.page.evaluate((value) => navigator.clipboard.writeText(value), data);\n }\n\n /**\n * Writes the provided data to the browser clipboard as a JSON string.\n * @param data The data to write to the clipboard. It will be stringified as JSON.\n */\n async writeJSON<T = unknown>(data: T): Promise<void> {\n const errorMessage = '[playwright-clipboard] Provided data cannot be stringified to valid JSON';\n let jsonString: string | undefined;\n\n try {\n jsonString = JSON.stringify(data);\n } catch (error) {\n const message = error instanceof Error ? error : String(error);\n throw new Error(`${errorMessage}: ${message}`);\n }\n\n if (jsonString === undefined) {\n throw new Error(`${errorMessage} (received undefined).`);\n }\n\n await this.write(jsonString);\n }\n\n /**\n * Clears the browser clipboard by writing an empty string to it.\n * This effectively removes any existing content from the clipboard.\n */\n async clear(): Promise<void> {\n await this.write('');\n }\n}\n","import type { Page, TestFixture } from '@playwright/test';\nimport { ClipboardHandler } from '../utils';\nimport type { BrowserName } from './types';\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; browserName: BrowserName }\n> = async ({ page, 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);\n await use(handler);\n};\n","import type { BrowserContext, TestFixture } from '@playwright/test';\nimport type { BrowserName } from './types';\n\n/**\n * A fixture that provides a BrowserContext with clipboard permissions granted for Chromium browsers.\n */\nexport const contextFixture: TestFixture<\n BrowserContext,\n { context: BrowserContext; browserName: BrowserName }\n> = async ({ context, browserName }, use) => {\n if (browserName === 'chromium') {\n await context.grantPermissions(['clipboard-read', 'clipboard-write']);\n }\n\n await use(context);\n};\n","import { clipboardFixture } from './clipboardFixture';\nimport { contextFixture } from './contextFixture';\n\n/**\n * A collection of fixtures related to clipboard testing, including the clipboard fixture and context fixture.\n * These fixtures can be used in Playwright tests to facilitate clipboard interactions and context management.\n */\nexport const clipboardFixtures = {\n context: contextFixture,\n clipboard: clipboardFixture,\n};\n\nexport { clipboardFixture, contextFixture };\n","import { type ExpectMatcherState, expect, type MatcherReturnType } from '@playwright/test';\nimport type { ClipboardHandler } from '../utils';\nimport { getErrorMessage, normalizeText } from '../utils/matcherUtils';\nimport type { TimeoutMatcherOptions, TrimMatcherOptions } from './types';\n\n/**\n * Asserts that the clipboard content is blank (empty string).\n *\n * @this ExpectMatcherState\n * @param clipboard The Clipboard utility instance.\n * @param options Matcher options.\n * @returns A Promise that resolves to a MatcherReturnType object.\n */\nexport async function toBeBlank(\n this: ExpectMatcherState,\n clipboard: ClipboardHandler,\n options: TimeoutMatcherOptions & TrimMatcherOptions = {},\n) {\n const name = 'toBeBlank';\n let pass: boolean;\n let actual: string | undefined;\n let normalizedActual: string | undefined;\n const expected: string = '';\n let errorReason: Error | null = null;\n\n const { timeout = 10_000, trim = false } = options;\n\n const poll = expect.poll(\n async () => {\n try {\n actual = await clipboard.read();\n normalizedActual = actual;\n normalizedActual = normalizeText(normalizedActual, { trim });\n errorReason = null;\n\n return normalizedActual;\n } catch (error) {\n errorReason = error instanceof Error ? error : new Error(String(error));\n actual = undefined;\n normalizedActual = 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 } 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\nexport function normalizeText<T extends string | RegExp>(\n data: T,\n options: { ignoreCase?: boolean; trim?: boolean } = {},\n): T {\n const { ignoreCase, trim } = options;\n\n if (typeof data === 'string') {\n let result: string = data;\n\n if (trim) result = result.trim();\n\n if (ignoreCase) result = result.toLowerCase();\n\n return result as T;\n }\n\n if (data instanceof RegExp) {\n const flags = data.flags.replace(/[gy]/g, '');\n const clearFlags = ignoreCase && !flags.includes('i') ? `${flags}i` : flags;\n\n return new RegExp(data.source, clearFlags) as T;\n }\n\n return data;\n}\n","import type { ExpectMatcherState, MatcherReturnType } from '@playwright/test';\nimport { expect } from '@playwright/test';\nimport type { ClipboardHandler } from '../utils';\nimport { getErrorMessage } from '../utils/matcherUtils';\nimport type { TimeoutMatcherOptions } from './types';\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 toHaveJSONContent(\n this: ExpectMatcherState,\n clipboard: ClipboardHandler,\n expected: unknown,\n options: TimeoutMatcherOptions = {},\n) {\n const name = 'toHaveJSONContent';\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, expect, type MatcherReturnType } from '@playwright/test';\nimport type { ClipboardHandler } from '../utils';\nimport { getErrorMessage, normalizeText } from '../utils/matcherUtils';\nimport type { IgnoreCaseMatcherOptions, TimeoutMatcherOptions, TrimMatcherOptions } from './types';\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 toHaveTextContent(\n this: ExpectMatcherState,\n clipboard: ClipboardHandler,\n expected: string | RegExp,\n options: TimeoutMatcherOptions & IgnoreCaseMatcherOptions & TrimMatcherOptions = {},\n) {\n const name = 'toHaveTextContent';\n let pass: boolean;\n let actual: string | undefined;\n let normalizedActual: string | undefined;\n let errorReason: Error | null = null;\n\n const { timeout = 10_000, ignoreCase = false, trim = false } = options;\n\n const normalizedExpected = normalizeText(expected, { ignoreCase, trim });\n\n const poll = expect.poll(\n async () => {\n try {\n actual = await clipboard.read();\n\n normalizedActual = actual;\n normalizedActual = normalizeText(normalizedActual, { ignoreCase, trim });\n errorReason = null;\n\n return normalizedActual;\n } catch (error) {\n errorReason = error instanceof Error ? error : new Error(String(error));\n actual = undefined;\n normalizedActual = undefined;\n\n throw errorReason;\n }\n },\n { timeout },\n );\n\n try {\n const expectation = this.isNot ? poll.not : poll;\n\n if (expected instanceof RegExp) {\n await expectation.toMatch(normalizedExpected as RegExp);\n } else {\n await expectation.toEqual(normalizedExpected);\n }\n\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 { toBeBlank } from './toBeBlank';\nimport { toHaveJSONContent } from './toHaveJSONContent';\nimport { toHaveTextContent } from './toHaveTextContent';\n\n/**\n * Export an object containing all the custom clipboard matchers for Playwright.\n */\nexport const clipboardMatchers = {\n toBeBlank,\n toHaveJSONContent,\n toHaveTextContent,\n};\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 * This module contains regular expression patterns for various common formats.\n * These patterns can be used for validation purposes in applications.\n */\nexport const PATTERNS = {\n /**\n * UUID v1-v5 (example: 123e4567-e89b-12d3-a456-426614174000).\n */\n UUID: /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,\n\n /**\n * Email addresses (example: user@example.com).\n */\n EMAIL: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/,\n\n /**\n * JWT tokens (example: header.payload.signature).\n */\n JWT: /^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/,\n\n /**\n * Authorization Bearer tokens (example: Bearer <token>).\n */\n BEARER: /^Bearer\\s+[a-zA-Z0-9._~+/-]+=*$/i,\n\n /**\n * HEX-colors (example: #FFF, #FFFFFF, #FFFFFFFF).\n */\n HEX_COLOR: /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i,\n\n /**\n * IP (IPv4, IPv6 or any).\n */\n IP: {\n /** IPv4 (example: 192.168.1.1 or 127.0.0.1) */\n V4: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,\n\n /** IPv6 (example: 2001:0db8:85a3:0000:0000:8a2e:0370:7334) */\n V6: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,\n\n /** Any IP address (IPv4 or IPv6) */\n ANY: /^(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])))$/,\n },\n} as const;\n"],"mappings":"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","names":["expect","import_test","import_test","import_test","baseTest","expect","baseExpect"]}
|