playwright-test-explorer 0.0.7 → 0.0.8
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 +35 -319
- package/dist/{api-JooYjXT5.js → api-B_yjDCcC.js} +145 -109
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,363 +1,79 @@
|
|
|
1
1
|
# Playwright Test Explorer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Playwright 测试结构探索工具 - 用最简单的方式查看和分析你的测试。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 特性
|
|
6
6
|
|
|
7
|
-
- 📋
|
|
8
|
-
- 🔍
|
|
9
|
-
- 🌳
|
|
10
|
-
- 💻
|
|
11
|
-
- 📦
|
|
7
|
+
- 📋 列出所有测试文件
|
|
8
|
+
- 🔍 分析测试用例详情(位置、标签、注解)
|
|
9
|
+
- 🌳 树形结构展示完整测试层级
|
|
10
|
+
- 💻 CLI 命令 & JavaScript API 双模式
|
|
11
|
+
- 📦 支持输出到 JSON 文件
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## 快速开始
|
|
14
14
|
|
|
15
|
-
###
|
|
15
|
+
### 安装
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
+
# 全局安装
|
|
18
19
|
npm install -g playwright-test-explorer
|
|
19
|
-
# or
|
|
20
|
-
pnpm add -g playwright-test-explorer
|
|
21
|
-
# or
|
|
22
|
-
yarn global add playwright-test-explorer
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
### Local Installation
|
|
26
20
|
|
|
27
|
-
|
|
21
|
+
# 本地安装
|
|
28
22
|
npm install playwright-test-explorer
|
|
29
|
-
# or
|
|
30
|
-
pnpm add playwright-test-explorer
|
|
31
|
-
# or
|
|
32
|
-
yarn add playwright-test-explorer
|
|
33
23
|
```
|
|
34
24
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
### List Test Files
|
|
38
|
-
|
|
39
|
-
List all test files in the Playwright project:
|
|
25
|
+
### CLI 使用
|
|
40
26
|
|
|
41
27
|
```bash
|
|
28
|
+
# 列出所有测试文件
|
|
42
29
|
pw-test-explorer list-files
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
With custom workspace and config:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
pw-test-explorer list-files -w /path/to/project -c e2e/playwright.config.ts
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Save results to a JSON file:
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
pw-test-explorer list-files -o output.json
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Options:
|
|
58
|
-
- `-w, --workspace <path>` - Workspace folder path (default: current directory)
|
|
59
|
-
- `-c, --config <file>` - Config file path relative to workspace (default: `playwright.config.ts`)
|
|
60
|
-
- `-o, --output <file>` - Output file path to save JSON results (optional)
|
|
61
|
-
|
|
62
|
-
### List Test Cases
|
|
63
|
-
|
|
64
|
-
List all test cases in specified files:
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
pw-test-explorer list-tests -f tests/example.spec.ts tests/another.spec.ts
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
With custom workspace:
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
pw-test-explorer list-tests -w /path/to/project -f e2e/tests/example.spec.ts
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Save results to a JSON file:
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
pw-test-explorer list-tests -f tests/example.spec.ts -o tests.json
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Options:
|
|
83
|
-
- `-w, --workspace <path>` - Workspace folder path (default: current directory)
|
|
84
|
-
- `-c, --config <file>` - Config file path relative to workspace (default: `playwright.config.ts`)
|
|
85
|
-
- `-f, --files <files...>` - Test file paths relative to workspace (required)
|
|
86
|
-
- `-o, --output <file>` - Output file path to save JSON results (optional)
|
|
87
30
|
|
|
88
|
-
|
|
31
|
+
# 列出指定文件中的测试用例
|
|
32
|
+
pw-test-explorer list-tests -f tests/example.spec.ts
|
|
89
33
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
```bash
|
|
34
|
+
# 显示完整测试树
|
|
93
35
|
pw-test-explorer tree
|
|
94
|
-
```
|
|
95
36
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
pw-test-explorer tree -w /path/to/project -c playwright.config.ts
|
|
37
|
+
# 保存结果到文件
|
|
38
|
+
pw-test-explorer tree -o output.json
|
|
100
39
|
```
|
|
101
40
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
pw-test-explorer tree -o tree.json
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Options:
|
|
109
|
-
- `-w, --workspace <path>` - Workspace folder path (default: current directory)
|
|
110
|
-
- `-c, --config <file>` - Config file path relative to workspace (default: `playwright.config.ts`)
|
|
111
|
-
- `-o, --output <file>` - Output file path to save JSON results (optional)
|
|
112
|
-
|
|
113
|
-
## Library Usage
|
|
114
|
-
|
|
115
|
-
This package provides three simple APIs for exploring Playwright tests:
|
|
41
|
+
### API 使用
|
|
116
42
|
|
|
117
43
|
```typescript
|
|
118
|
-
import {
|
|
119
|
-
listTestFile,
|
|
120
|
-
listTestCase,
|
|
121
|
-
treeStructure
|
|
122
|
-
} from 'playwright-test-explorer';
|
|
44
|
+
import { listTestFile, listTestCase, treeStructure } from 'playwright-test-explorer';
|
|
123
45
|
|
|
124
|
-
//
|
|
46
|
+
// 列出测试文件
|
|
125
47
|
await listTestFile('/path/to/project', 'playwright.config.ts');
|
|
126
48
|
|
|
127
|
-
//
|
|
128
|
-
await
|
|
129
|
-
|
|
130
|
-
// 2. List test cases in specific files (output to console)
|
|
131
|
-
await listTestCase(
|
|
132
|
-
'/path/to/project',
|
|
133
|
-
'playwright.config.ts',
|
|
134
|
-
['tests/example.spec.ts', 'tests/another.spec.ts']
|
|
135
|
-
);
|
|
49
|
+
// 列出测试用例
|
|
50
|
+
await listTestCase('/path/to/project', 'playwright.config.ts', ['tests/example.spec.ts']);
|
|
136
51
|
|
|
137
|
-
//
|
|
138
|
-
await listTestCase(
|
|
139
|
-
'/path/to/project',
|
|
140
|
-
'playwright.config.ts',
|
|
141
|
-
['tests/example.spec.ts', 'tests/another.spec.ts'],
|
|
142
|
-
'tests.json'
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
// 3. Display complete test structure as a tree (output to console)
|
|
52
|
+
// 生成测试树
|
|
146
53
|
await treeStructure('/path/to/project', 'playwright.config.ts');
|
|
147
|
-
|
|
148
|
-
// Display test tree and save to a file
|
|
149
|
-
await treeStructure(
|
|
150
|
-
'/path/to/project',
|
|
151
|
-
'playwright.config.ts',
|
|
152
|
-
undefined, // cancellationToken
|
|
153
|
-
'tree.json'
|
|
154
|
-
);
|
|
155
54
|
```
|
|
156
55
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
## API Reference
|
|
160
|
-
|
|
161
|
-
### `listTestFile(workspaceFolder, configFile, outputFile?)`
|
|
162
|
-
|
|
163
|
-
Lists all test files in the Playwright project and outputs the result to console or a file.
|
|
56
|
+
## 文档
|
|
164
57
|
|
|
165
|
-
|
|
166
|
-
- `workspaceFolder`: `string` - Path to the workspace folder (default: `process.cwd()`)
|
|
167
|
-
- `configFile`: `string` - Config file path relative to workspace (default: `'playwright.config.ts'`)
|
|
168
|
-
- `outputFile`: `string` (optional) - Path to save JSON output. If not provided, outputs to console.
|
|
58
|
+
- [功能规格说明](./SPEC.md) - 完整的功能需求和 API 文档
|
|
169
59
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
**Output Example:**
|
|
173
|
-
```json
|
|
174
|
-
{
|
|
175
|
-
"projects": [
|
|
176
|
-
{
|
|
177
|
-
"name": "chromium",
|
|
178
|
-
"testDir": "/path/to/tests",
|
|
179
|
-
"use": { "browserName": "chromium" },
|
|
180
|
-
"files": [
|
|
181
|
-
"/path/to/tests/example.spec.ts",
|
|
182
|
-
"/path/to/tests/another.spec.ts"
|
|
183
|
-
]
|
|
184
|
-
}
|
|
185
|
-
]
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### `listTestCase(workspaceFolder, configFile, relativePaths, outputFile?)`
|
|
190
|
-
|
|
191
|
-
Lists all test cases in specified files and outputs the result to console or a file.
|
|
192
|
-
|
|
193
|
-
**Parameters:**
|
|
194
|
-
- `workspaceFolder`: `string` - Path to the workspace folder (default: `process.cwd()`)
|
|
195
|
-
- `configFile`: `string` - Config file path relative to workspace (default: `'playwright.config.ts'`)
|
|
196
|
-
- `relativePaths`: `string[]` - Array of test file paths relative to workspace
|
|
197
|
-
- `outputFile`: `string` (optional) - Path to save JSON output. If not provided, outputs to console.
|
|
198
|
-
|
|
199
|
-
**Returns:** `Promise<void>`
|
|
200
|
-
|
|
201
|
-
**Output Example:**
|
|
202
|
-
```json
|
|
203
|
-
{
|
|
204
|
-
"tests": [
|
|
205
|
-
{
|
|
206
|
-
"title": "should login successfully",
|
|
207
|
-
"file": "/path/to/tests/login.spec.ts",
|
|
208
|
-
"line": 10,
|
|
209
|
-
"column": 3,
|
|
210
|
-
"tags": ["@smoke"],
|
|
211
|
-
"annotations": [],
|
|
212
|
-
"expectedStatus": "passed",
|
|
213
|
-
"timeout": 30000,
|
|
214
|
-
"titlePath": ["Login Tests", "should login successfully"]
|
|
215
|
-
}
|
|
216
|
-
]
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### `treeStructure(workspaceFolder, configFile, cancellationToken?, outputFile?)`
|
|
221
|
-
|
|
222
|
-
Displays the complete test structure as a tree and outputs the result to console or a file.
|
|
223
|
-
|
|
224
|
-
**Parameters:**
|
|
225
|
-
- `workspaceFolder`: `string` - Path to the workspace folder (default: `process.cwd()`)
|
|
226
|
-
- `configFile`: `string` - Config file path relative to workspace (default: `'playwright.config.ts'`)
|
|
227
|
-
- `cancellationToken`: `CancellationToken` (optional) - Optional cancellation token for cancelling the operation
|
|
228
|
-
- `outputFile`: `string` (optional) - Path to save JSON output. If not provided, outputs to console.
|
|
229
|
-
|
|
230
|
-
**Returns:** `Promise<void>`
|
|
231
|
-
|
|
232
|
-
**Output Example:**
|
|
233
|
-
```json
|
|
234
|
-
{
|
|
235
|
-
"type": "suite",
|
|
236
|
-
"title": "Root",
|
|
237
|
-
"children": [
|
|
238
|
-
{
|
|
239
|
-
"type": "suite",
|
|
240
|
-
"title": "Login Tests",
|
|
241
|
-
"file": "/path/to/tests/login.spec.ts",
|
|
242
|
-
"children": [
|
|
243
|
-
{
|
|
244
|
-
"type": "test",
|
|
245
|
-
"title": "should login successfully",
|
|
246
|
-
"location": {
|
|
247
|
-
"file": "/path/to/tests/login.spec.ts",
|
|
248
|
-
"line": 10,
|
|
249
|
-
"column": 3
|
|
250
|
-
},
|
|
251
|
-
"tags": ["@smoke"],
|
|
252
|
-
"expectedStatus": "passed",
|
|
253
|
-
"timeout": 30000
|
|
254
|
-
}
|
|
255
|
-
]
|
|
256
|
-
}
|
|
257
|
-
]
|
|
258
|
-
}
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
## Requirements
|
|
262
|
-
|
|
263
|
-
- Node.js 16 or higher
|
|
264
|
-
- `@playwright/test` installed in your project
|
|
265
|
-
|
|
266
|
-
## Testing
|
|
267
|
-
|
|
268
|
-
The project includes comprehensive tests using Vitest and an example Playwright project.
|
|
269
|
-
|
|
270
|
-
### Run All Tests
|
|
60
|
+
## 开发
|
|
271
61
|
|
|
272
62
|
```bash
|
|
273
|
-
#
|
|
274
|
-
pnpm
|
|
275
|
-
pnpm
|
|
276
|
-
|
|
277
|
-
# Run vitest tests (includes API and CLI tests)
|
|
278
|
-
pnpm test
|
|
279
|
-
|
|
280
|
-
# Run tests in watch mode
|
|
281
|
-
pnpm test -- --watch
|
|
282
|
-
|
|
283
|
-
# Run with coverage
|
|
284
|
-
pnpm test -- --coverage
|
|
63
|
+
pnpm install # 安装依赖
|
|
64
|
+
pnpm run build # 构建项目
|
|
65
|
+
pnpm test # 运行测试
|
|
285
66
|
```
|
|
286
67
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
The `example/` directory contains a sample Playwright project with:
|
|
290
|
-
- 4 projects (chromium, firefox, webkit, mobile)
|
|
291
|
-
- 3 test files with 18+ tests
|
|
292
|
-
- Various test scenarios (UI, API, authentication)
|
|
68
|
+
## 要求
|
|
293
69
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
The project includes two comprehensive test suites:
|
|
297
|
-
|
|
298
|
-
1. **API Tests** (`tests/api.test.ts`) - Tests the JavaScript API functions
|
|
299
|
-
- listTestFile functionality
|
|
300
|
-
- listTestCase functionality
|
|
301
|
-
- treeStructure functionality
|
|
302
|
-
- File output capabilities
|
|
303
|
-
|
|
304
|
-
2. **CLI Tests** (`tests/cli.test.ts`) - Tests the command-line interface
|
|
305
|
-
- list-files command
|
|
306
|
-
- list-tests command
|
|
307
|
-
- tree command
|
|
308
|
-
- Help and version commands
|
|
309
|
-
- File output functionality
|
|
310
|
-
|
|
311
|
-
All tests use the example Playwright project in `example/` directory.
|
|
312
|
-
|
|
313
|
-
### Manual Testing
|
|
314
|
-
|
|
315
|
-
```bash
|
|
316
|
-
# List test files
|
|
317
|
-
pnpm run test:list-files
|
|
318
|
-
|
|
319
|
-
# List test cases
|
|
320
|
-
pnpm run test:list-tests
|
|
321
|
-
|
|
322
|
-
# Display test tree
|
|
323
|
-
pnpm run test:example
|
|
324
|
-
|
|
325
|
-
# Save results to files
|
|
326
|
-
pnpm run test:save-files
|
|
327
|
-
pnpm run test:save-tree
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
See [TESTING.md](TESTING.md) for comprehensive testing documentation.
|
|
331
|
-
|
|
332
|
-
## Development
|
|
333
|
-
|
|
334
|
-
```bash
|
|
335
|
-
# Install dependencies
|
|
336
|
-
pnpm install
|
|
337
|
-
|
|
338
|
-
# Build the project
|
|
339
|
-
pnpm run build
|
|
340
|
-
|
|
341
|
-
# Run in development mode
|
|
342
|
-
pnpm run dev
|
|
343
|
-
|
|
344
|
-
# Type check
|
|
345
|
-
pnpm run typecheck
|
|
346
|
-
|
|
347
|
-
# Run tests
|
|
348
|
-
pnpm test
|
|
349
|
-
|
|
350
|
-
# Run tests in watch mode
|
|
351
|
-
pnpm run test:watch
|
|
352
|
-
|
|
353
|
-
# Run tests with coverage
|
|
354
|
-
pnpm run test:coverage
|
|
355
|
-
```
|
|
70
|
+
- Node.js 16+
|
|
71
|
+
- @playwright/test
|
|
356
72
|
|
|
357
73
|
## License
|
|
358
74
|
|
|
359
75
|
MIT
|
|
360
76
|
|
|
361
|
-
|
|
77
|
+
---
|
|
362
78
|
|
|
363
|
-
|
|
79
|
+
参考 [Microsoft playwright-vscode](https://github.com/microsoft/playwright-vscode) 的测试服务器实现。
|
|
@@ -637,7 +637,7 @@ var TestServerConnection = class {
|
|
|
637
637
|
};
|
|
638
638
|
|
|
639
639
|
//#endregion
|
|
640
|
-
//#region src/
|
|
640
|
+
//#region src/utils/pathUtils.ts
|
|
641
641
|
/**
|
|
642
642
|
* Escape special regex characters in a path
|
|
643
643
|
* From playwright-vscode: src/utils.ts
|
|
@@ -646,6 +646,99 @@ function escapeRegex(text) {
|
|
|
646
646
|
if (text.startsWith("/")) text = text.substring(1);
|
|
647
647
|
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
648
648
|
}
|
|
649
|
+
/**
|
|
650
|
+
* Path resolution strategy for TeleReporterReceiver
|
|
651
|
+
* For file paths (with extensions), return relative path (Playwright format)
|
|
652
|
+
* For directory paths (testDir, outputDir, etc.), return absolute path
|
|
653
|
+
*/
|
|
654
|
+
function resolvePathForFiles(rootDir, relativePath) {
|
|
655
|
+
return /\.\w+$/.test(relativePath) ? relativePath : path.join(rootDir, relativePath);
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Standard path resolution - always returns absolute path
|
|
659
|
+
*/
|
|
660
|
+
function resolveAbsolutePath(rootDir, relativePath) {
|
|
661
|
+
return path.join(rootDir, relativePath);
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Find Playwright CLI path
|
|
665
|
+
*/
|
|
666
|
+
function findPlaywrightCli(workspaceFolder) {
|
|
667
|
+
try {
|
|
668
|
+
const playwrightPackage = __require.resolve("@playwright/test/package.json", { paths: [workspaceFolder] });
|
|
669
|
+
return path.join(path.dirname(playwrightPackage), "cli.js");
|
|
670
|
+
} catch {
|
|
671
|
+
throw new Error("Playwright installation not found. Please install @playwright/test");
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
//#endregion
|
|
676
|
+
//#region src/utils/processUtils.ts
|
|
677
|
+
/**
|
|
678
|
+
* Add node_modules/.bin to PATH environment variable
|
|
679
|
+
*/
|
|
680
|
+
function addNpmRunPath(env, cwd) {
|
|
681
|
+
const pathSeparator = process.platform === "win32" ? ";" : ":";
|
|
682
|
+
const newPath = [];
|
|
683
|
+
let currentPath = path.resolve(cwd);
|
|
684
|
+
let previousPath;
|
|
685
|
+
while (previousPath !== currentPath) {
|
|
686
|
+
newPath.push(path.join(currentPath, "node_modules", ".bin"));
|
|
687
|
+
previousPath = currentPath;
|
|
688
|
+
currentPath = path.resolve(currentPath, "..");
|
|
689
|
+
}
|
|
690
|
+
const defaultPath = process.platform === "win32" ? "Path" : "PATH";
|
|
691
|
+
const pathKey = Object.keys(env).find((k) => k.toLowerCase() === "path") ?? defaultPath;
|
|
692
|
+
if (env[pathKey]) newPath.push(env[pathKey]);
|
|
693
|
+
return {
|
|
694
|
+
...env,
|
|
695
|
+
[pathKey]: newPath.join(pathSeparator)
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Create environment variables for Playwright test server process
|
|
700
|
+
*/
|
|
701
|
+
function createTestServerEnv(baseEnv, customEnv, cwd) {
|
|
702
|
+
return addNpmRunPath({
|
|
703
|
+
...baseEnv,
|
|
704
|
+
...customEnv,
|
|
705
|
+
FORCE_COLOR: "1"
|
|
706
|
+
}, cwd);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
//#endregion
|
|
710
|
+
//#region src/utils/reporterFactory.ts
|
|
711
|
+
/**
|
|
712
|
+
* Create TeleReporterReceiver for listing files
|
|
713
|
+
* Uses special path resolution for preserving relative paths for files
|
|
714
|
+
*/
|
|
715
|
+
function createFileListReceiver(reporter) {
|
|
716
|
+
return new TeleReporterReceiver(reporter, {
|
|
717
|
+
mergeProjects: true,
|
|
718
|
+
mergeTestCases: true,
|
|
719
|
+
resolvePath: resolvePathForFiles
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Create TeleReporterReceiver for listing tests
|
|
724
|
+
* Uses standard absolute path resolution
|
|
725
|
+
*/
|
|
726
|
+
function createTestListReceiver(reporter) {
|
|
727
|
+
return new TeleReporterReceiver(reporter, {
|
|
728
|
+
mergeProjects: true,
|
|
729
|
+
mergeTestCases: true,
|
|
730
|
+
resolvePath: resolveAbsolutePath
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Process report messages through receiver
|
|
735
|
+
*/
|
|
736
|
+
function processReport(receiver, report) {
|
|
737
|
+
for (const message of report) receiver.dispatch(message);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region src/playwrightTestServer.ts
|
|
649
742
|
var CancellationToken = class {
|
|
650
743
|
_isCancelled = false;
|
|
651
744
|
_onCancelEmitter = new EventEmitter();
|
|
@@ -664,7 +757,6 @@ var PlaywrightTestServer = class {
|
|
|
664
757
|
_config;
|
|
665
758
|
_options;
|
|
666
759
|
_testServerPromise;
|
|
667
|
-
_cli = null;
|
|
668
760
|
constructor(config = {
|
|
669
761
|
workspaceFolder: process.cwd(),
|
|
670
762
|
configFile: "playwright.config.ts"
|
|
@@ -672,39 +764,24 @@ var PlaywrightTestServer = class {
|
|
|
672
764
|
this._config = config;
|
|
673
765
|
this._options = options;
|
|
674
766
|
}
|
|
675
|
-
/**
|
|
676
|
-
* Find Playwright CLI path
|
|
677
|
-
*/
|
|
678
|
-
_findPlaywrightCli() {
|
|
679
|
-
if (this._cli) return this._cli;
|
|
680
|
-
try {
|
|
681
|
-
const playwrightPackage = __require.resolve("@playwright/test/package.json", { paths: [this._config.workspaceFolder] });
|
|
682
|
-
this._cli = path.join(path.dirname(playwrightPackage), "cli.js");
|
|
683
|
-
return this._cli;
|
|
684
|
-
} catch {
|
|
685
|
-
throw new Error("Playwright installation not found. Please install @playwright/test");
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
767
|
reset() {
|
|
689
768
|
this._disposeTestServer();
|
|
690
769
|
}
|
|
691
770
|
async listFiles() {
|
|
692
|
-
const
|
|
693
|
-
if (!testServer.connection) {
|
|
694
|
-
const errors = testServer.errors.length ? ". Test server errors: " + testServer.errors.join("\n") : "";
|
|
695
|
-
throw new Error("Internal error: unable to connect to the test server." + errors);
|
|
696
|
-
}
|
|
771
|
+
const connection = await this._ensureConnection();
|
|
697
772
|
const result = { projects: [] };
|
|
698
|
-
const { report } = await
|
|
699
|
-
const
|
|
773
|
+
const { report } = await connection.listFiles({});
|
|
774
|
+
const workspaceFolder = this._config.workspaceFolder;
|
|
775
|
+
processReport(createFileListReceiver({
|
|
700
776
|
onConfigure: (_config) => {},
|
|
701
777
|
onBegin(rootSuite) {
|
|
702
778
|
for (const projectSuite of rootSuite.suites) {
|
|
703
779
|
const project = projectSuite.project();
|
|
704
780
|
const files = [];
|
|
781
|
+
const testDir = project.testDir ? path.relative(workspaceFolder, project.testDir).replace(/\\/g, "/") : "";
|
|
705
782
|
result.projects.push({
|
|
706
783
|
name: project.name,
|
|
707
|
-
testDir
|
|
784
|
+
testDir,
|
|
708
785
|
use: project.use || {},
|
|
709
786
|
files
|
|
710
787
|
});
|
|
@@ -714,12 +791,7 @@ var PlaywrightTestServer = class {
|
|
|
714
791
|
onError(error) {
|
|
715
792
|
result.error = error;
|
|
716
793
|
}
|
|
717
|
-
},
|
|
718
|
-
mergeProjects: true,
|
|
719
|
-
mergeTestCases: true,
|
|
720
|
-
resolvePath: (rootDir, relativePath) => path.join(rootDir, relativePath)
|
|
721
|
-
});
|
|
722
|
-
for (const message of report) teleReceiver.dispatch(message);
|
|
794
|
+
}), report);
|
|
723
795
|
return result;
|
|
724
796
|
}
|
|
725
797
|
/**
|
|
@@ -727,33 +799,33 @@ var PlaywrightTestServer = class {
|
|
|
727
799
|
* This preserves the complete test.describe hierarchy
|
|
728
800
|
*/
|
|
729
801
|
async getRawTestReport(locations, token) {
|
|
730
|
-
const { connection } = await this._testServer();
|
|
731
802
|
if (token?.isCancellationRequested) return [];
|
|
732
|
-
|
|
803
|
+
const connection = await this._ensureConnection();
|
|
733
804
|
const escapedLocations = locations.map(escapeRegex);
|
|
734
805
|
const { report } = await connection.listTests({ locations: escapedLocations });
|
|
735
806
|
return report;
|
|
736
807
|
}
|
|
737
808
|
async listTests(locations, reporter, token) {
|
|
738
|
-
const { connection } = await this._testServer();
|
|
739
809
|
if (token?.isCancellationRequested) return;
|
|
740
|
-
|
|
810
|
+
const connection = await this._ensureConnection();
|
|
741
811
|
const escapedLocations = locations.map(escapeRegex);
|
|
742
812
|
const { report } = await connection.listTests({ locations: escapedLocations });
|
|
743
|
-
|
|
744
|
-
mergeProjects: true,
|
|
745
|
-
mergeTestCases: true,
|
|
746
|
-
resolvePath: (rootDir, relativePath) => path.join(rootDir, relativePath)
|
|
747
|
-
});
|
|
748
|
-
for (const message of report) teleReceiver.dispatch(message);
|
|
813
|
+
processReport(createTestListReceiver(reporter), report);
|
|
749
814
|
}
|
|
750
815
|
async findRelatedTestFiles(files) {
|
|
816
|
+
return await (await this._ensureConnection()).findRelatedTestFiles({ files });
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Ensure connection is established and return it
|
|
820
|
+
* Throws error if connection cannot be established
|
|
821
|
+
*/
|
|
822
|
+
async _ensureConnection() {
|
|
751
823
|
const testServer = await this._testServer();
|
|
752
|
-
if (!testServer.connection)
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
}
|
|
756
|
-
return
|
|
824
|
+
if (!testServer.connection) {
|
|
825
|
+
const errors = testServer.errors.length ? ". Test server errors: " + testServer.errors.join("\n") : "";
|
|
826
|
+
throw new Error("Internal error: unable to connect to the test server." + errors);
|
|
827
|
+
}
|
|
828
|
+
return testServer.connection;
|
|
757
829
|
}
|
|
758
830
|
_testServer() {
|
|
759
831
|
if (this._testServerPromise) return this._testServerPromise;
|
|
@@ -762,7 +834,7 @@ var PlaywrightTestServer = class {
|
|
|
762
834
|
}
|
|
763
835
|
async _createTestServer() {
|
|
764
836
|
const cwd = this._config.workspaceFolder;
|
|
765
|
-
const cli = path.relative(cwd,
|
|
837
|
+
const cli = path.relative(cwd, findPlaywrightCli(cwd));
|
|
766
838
|
const config = path.relative(cwd, this._config.configFile);
|
|
767
839
|
const errors = [];
|
|
768
840
|
const wsEndpoint = await this._startBackend({
|
|
@@ -773,21 +845,7 @@ var PlaywrightTestServer = class {
|
|
|
773
845
|
config
|
|
774
846
|
],
|
|
775
847
|
cwd,
|
|
776
|
-
|
|
777
|
-
const env = this._options.envProvider?.(this._config.configFile) || {};
|
|
778
|
-
return {
|
|
779
|
-
...process.env,
|
|
780
|
-
...env,
|
|
781
|
-
FORCE_COLOR: "1"
|
|
782
|
-
};
|
|
783
|
-
},
|
|
784
|
-
errors,
|
|
785
|
-
onClose: () => {
|
|
786
|
-
this._testServerPromise = void 0;
|
|
787
|
-
},
|
|
788
|
-
onError: () => {
|
|
789
|
-
this._testServerPromise = void 0;
|
|
790
|
-
}
|
|
848
|
+
errors
|
|
791
849
|
});
|
|
792
850
|
if (!wsEndpoint) return {
|
|
793
851
|
connection: null,
|
|
@@ -805,10 +863,8 @@ var PlaywrightTestServer = class {
|
|
|
805
863
|
}
|
|
806
864
|
async _startBackend(options) {
|
|
807
865
|
const node = process.execPath;
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
...options.envProvider()
|
|
811
|
-
}, options.cwd);
|
|
866
|
+
const customEnv = this._options.envProvider?.(this._config.configFile) || {};
|
|
867
|
+
const env = createTestServerEnv(process.env, customEnv, options.cwd);
|
|
812
868
|
const serverProcess = spawn(node, options.args, {
|
|
813
869
|
cwd: options.cwd,
|
|
814
870
|
stdio: "pipe",
|
|
@@ -819,10 +875,14 @@ var PlaywrightTestServer = class {
|
|
|
819
875
|
options.errors.push(message);
|
|
820
876
|
this._options.onStdErr?.(message);
|
|
821
877
|
});
|
|
822
|
-
serverProcess.on("error",
|
|
823
|
-
|
|
878
|
+
serverProcess.on("error", () => {
|
|
879
|
+
this._testServerPromise = void 0;
|
|
880
|
+
});
|
|
881
|
+
serverProcess.on("close", () => {
|
|
882
|
+
this._testServerPromise = void 0;
|
|
883
|
+
});
|
|
824
884
|
return new Promise((fulfill) => {
|
|
825
|
-
serverProcess.stdout?.on("data",
|
|
885
|
+
serverProcess.stdout?.on("data", (data) => {
|
|
826
886
|
const output = data.toString();
|
|
827
887
|
this._options.onStdOut?.(output);
|
|
828
888
|
const match = output.match(/Listening on (.*)/);
|
|
@@ -831,24 +891,6 @@ var PlaywrightTestServer = class {
|
|
|
831
891
|
serverProcess.on("exit", () => fulfill(null));
|
|
832
892
|
});
|
|
833
893
|
}
|
|
834
|
-
_addNpmRunPath(env, cwd) {
|
|
835
|
-
const pathSeparator = process.platform === "win32" ? ";" : ":";
|
|
836
|
-
const newPath = [];
|
|
837
|
-
let currentPath = path.resolve(cwd);
|
|
838
|
-
let previousPath;
|
|
839
|
-
while (previousPath !== currentPath) {
|
|
840
|
-
newPath.push(path.join(currentPath, "node_modules", ".bin"));
|
|
841
|
-
previousPath = currentPath;
|
|
842
|
-
currentPath = path.resolve(currentPath, "..");
|
|
843
|
-
}
|
|
844
|
-
const defaultPath = process.platform === "win32" ? "Path" : "PATH";
|
|
845
|
-
const pathKey = Object.keys(env).find((k) => k.toLowerCase() === "path") ?? defaultPath;
|
|
846
|
-
if (env[pathKey]) newPath.push(env[pathKey]);
|
|
847
|
-
return {
|
|
848
|
-
...env,
|
|
849
|
-
[pathKey]: newPath.join(pathSeparator)
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
894
|
_disposeTestServer() {
|
|
853
895
|
const testServer = this._testServerPromise;
|
|
854
896
|
this._testServerPromise = void 0;
|
|
@@ -869,7 +911,7 @@ async function listTestFile(workspaceFolder = process.cwd(), configFile = "playw
|
|
|
869
911
|
try {
|
|
870
912
|
const output = { projects: (await testServer.listFiles()).projects.map((project) => ({
|
|
871
913
|
...project,
|
|
872
|
-
files: project.files.map((filePath) =>
|
|
914
|
+
files: project.files.map((filePath) => filePath.replace(/\\/g, "/"))
|
|
873
915
|
})) };
|
|
874
916
|
const jsonString = JSON.stringify(output, null, 2);
|
|
875
917
|
if (outputFile) {
|
|
@@ -896,13 +938,16 @@ async function listTestCase(workspaceFolder = process.cwd(), configFile = "playw
|
|
|
896
938
|
try {
|
|
897
939
|
if ((await fs.stat(absolutePath)).isDirectory()) {
|
|
898
940
|
const files = await testServer.listFiles();
|
|
899
|
-
for (const project of files.projects)
|
|
900
|
-
const
|
|
901
|
-
|
|
941
|
+
for (const project of files.projects) {
|
|
942
|
+
const testDirRelative = project.testDir || "";
|
|
943
|
+
for (const file of project.files) {
|
|
944
|
+
const fullPath = testDirRelative ? path.join(testDirRelative, file).replace(/\\/g, "/") : file.replace(/\\/g, "/");
|
|
945
|
+
if (fullPath.startsWith(normalizedInputPath + "/") || fullPath === normalizedInputPath) expandedPaths.push(fullPath);
|
|
946
|
+
}
|
|
902
947
|
}
|
|
903
|
-
} else expandedPaths.push(
|
|
948
|
+
} else expandedPaths.push(normalizedInputPath);
|
|
904
949
|
} catch {
|
|
905
|
-
expandedPaths.push(
|
|
950
|
+
expandedPaths.push(normalizedInputPath);
|
|
906
951
|
}
|
|
907
952
|
}
|
|
908
953
|
if (expandedPaths.length === 0) {
|
|
@@ -944,11 +989,6 @@ async function listTestCase(workspaceFolder = process.cwd(), configFile = "playw
|
|
|
944
989
|
title: "Root",
|
|
945
990
|
children: []
|
|
946
991
|
};
|
|
947
|
-
let rootDir = "";
|
|
948
|
-
for (const message of report) if (message.method === "onConfigure") {
|
|
949
|
-
rootDir = message.params.config.rootDir || "";
|
|
950
|
-
break;
|
|
951
|
-
}
|
|
952
992
|
for (const message of report) if (message.method === "onProject") {
|
|
953
993
|
const project = message.params.project;
|
|
954
994
|
const projectNode = {
|
|
@@ -956,8 +996,8 @@ async function listTestCase(workspaceFolder = process.cwd(), configFile = "playw
|
|
|
956
996
|
title: project.name,
|
|
957
997
|
children: []
|
|
958
998
|
};
|
|
959
|
-
|
|
960
|
-
|
|
999
|
+
let testDir = "";
|
|
1000
|
+
if (project.testDir) testDir = path.isAbsolute(project.testDir) ? path.relative(workspaceFolder, project.testDir).replace(/\\/g, "/") : project.testDir.replace(/\\/g, "/");
|
|
961
1001
|
if (project.suites && Array.isArray(project.suites)) for (const suite of project.suites) {
|
|
962
1002
|
const suiteNode = parseEntry(suite, testDir);
|
|
963
1003
|
projectNode.children.push(suiteNode);
|
|
@@ -989,8 +1029,9 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
|
|
|
989
1029
|
}
|
|
990
1030
|
const allRelativePaths = [];
|
|
991
1031
|
for (const project of files.projects) if (project.files.length > 0) {
|
|
992
|
-
const
|
|
993
|
-
|
|
1032
|
+
const testDirRelative = project.testDir || "";
|
|
1033
|
+
const relativePaths = project.files.map((file) => {
|
|
1034
|
+
return testDirRelative ? path.join(testDirRelative, file).replace(/\\/g, "/") : file.replace(/\\/g, "/");
|
|
994
1035
|
});
|
|
995
1036
|
allRelativePaths.push(...relativePaths);
|
|
996
1037
|
}
|
|
@@ -1001,7 +1042,7 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
|
|
|
1001
1042
|
const report = await testServer.getRawTestReport(allRelativePaths, token);
|
|
1002
1043
|
function parseEntry(entry, testDir) {
|
|
1003
1044
|
if (entry.testId) {
|
|
1004
|
-
const filePath =
|
|
1045
|
+
const filePath = entry.location.file.replace(/\\/g, "/");
|
|
1005
1046
|
return {
|
|
1006
1047
|
type: "test",
|
|
1007
1048
|
title: entry.title,
|
|
@@ -1017,7 +1058,7 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
|
|
|
1017
1058
|
timeout: 0
|
|
1018
1059
|
};
|
|
1019
1060
|
} else {
|
|
1020
|
-
const filePath = entry.location
|
|
1061
|
+
const filePath = entry.location?.file.replace(/\\/g, "/");
|
|
1021
1062
|
const node = {
|
|
1022
1063
|
type: "suite",
|
|
1023
1064
|
title: entry.title,
|
|
@@ -1033,11 +1074,6 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
|
|
|
1033
1074
|
title: "Root",
|
|
1034
1075
|
children: []
|
|
1035
1076
|
};
|
|
1036
|
-
let rootDir = "";
|
|
1037
|
-
for (const message of report) if (message.method === "onConfigure") {
|
|
1038
|
-
rootDir = message.params.config.rootDir || "";
|
|
1039
|
-
break;
|
|
1040
|
-
}
|
|
1041
1077
|
for (const message of report) if (message.method === "onProject") {
|
|
1042
1078
|
const project = message.params.project;
|
|
1043
1079
|
const projectNode = {
|
|
@@ -1045,8 +1081,8 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
|
|
|
1045
1081
|
title: project.name,
|
|
1046
1082
|
children: []
|
|
1047
1083
|
};
|
|
1048
|
-
|
|
1049
|
-
|
|
1084
|
+
let testDir = "";
|
|
1085
|
+
if (project.testDir) testDir = path.isAbsolute(project.testDir) ? path.relative(workspaceFolder, project.testDir).replace(/\\/g, "/") : project.testDir.replace(/\\/g, "/");
|
|
1050
1086
|
if (project.suites && Array.isArray(project.suites)) for (const suite of project.suites) {
|
|
1051
1087
|
const suiteNode = parseEntry(suite, testDir);
|
|
1052
1088
|
projectNode.children.push(suiteNode);
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as CancellationToken, n as listTestFile, r as treeStructure, t as listTestCase } from "./api-
|
|
2
|
+
import { i as CancellationToken, n as listTestFile, r as treeStructure, t as listTestCase } from "./api-B_yjDCcC.js";
|
|
3
3
|
import { program } from "commander";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import ora from "ora";
|
package/dist/index.js
CHANGED