playwright-test-explorer 0.0.7 → 0.0.10
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-Bc2ZCCQ0.mjs} +157 -109
- package/dist/{cli.js → cli.mjs} +6 -2
- package/dist/index.mjs +3 -0
- package/package.json +17 -17
- package/dist/index.js +0 -3
- /package/dist/{cli.d.ts → cli.d.mts} +0 -0
- /package/dist/{index.d.ts → index.d.mts} +0 -0
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,10 @@ var TestServerConnection = class {
|
|
|
637
637
|
};
|
|
638
638
|
|
|
639
639
|
//#endregion
|
|
640
|
-
//#region src/
|
|
640
|
+
//#region src/utils/pathUtils.ts
|
|
641
|
+
/**
|
|
642
|
+
* Path utility functions for Playwright test server
|
|
643
|
+
*/
|
|
641
644
|
/**
|
|
642
645
|
* Escape special regex characters in a path
|
|
643
646
|
* From playwright-vscode: src/utils.ts
|
|
@@ -646,6 +649,108 @@ function escapeRegex(text) {
|
|
|
646
649
|
if (text.startsWith("/")) text = text.substring(1);
|
|
647
650
|
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
648
651
|
}
|
|
652
|
+
/**
|
|
653
|
+
* Path resolution strategy for TeleReporterReceiver
|
|
654
|
+
* For file paths (with extensions), return relative path (Playwright format)
|
|
655
|
+
* For directory paths (testDir, outputDir, etc.), return absolute path
|
|
656
|
+
*/
|
|
657
|
+
function resolvePathForFiles(rootDir, relativePath) {
|
|
658
|
+
return /\.\w+$/.test(relativePath) ? relativePath : path.join(rootDir, relativePath);
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Standard path resolution - always returns absolute path
|
|
662
|
+
*/
|
|
663
|
+
function resolveAbsolutePath(rootDir, relativePath) {
|
|
664
|
+
return path.join(rootDir, relativePath);
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Find Playwright CLI path
|
|
668
|
+
*/
|
|
669
|
+
function findPlaywrightCli(workspaceFolder) {
|
|
670
|
+
try {
|
|
671
|
+
const playwrightPackage = __require.resolve("@playwright/test/package.json", { paths: [workspaceFolder] });
|
|
672
|
+
return path.join(path.dirname(playwrightPackage), "cli.js");
|
|
673
|
+
} catch {
|
|
674
|
+
throw new Error("Playwright installation not found. Please install @playwright/test");
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
//#endregion
|
|
679
|
+
//#region src/utils/processUtils.ts
|
|
680
|
+
/**
|
|
681
|
+
* Process and environment utilities for spawning Playwright test server
|
|
682
|
+
*/
|
|
683
|
+
/**
|
|
684
|
+
* Add node_modules/.bin to PATH environment variable
|
|
685
|
+
*/
|
|
686
|
+
function addNpmRunPath(env, cwd) {
|
|
687
|
+
const pathSeparator = process.platform === "win32" ? ";" : ":";
|
|
688
|
+
const newPath = [];
|
|
689
|
+
let currentPath = path.resolve(cwd);
|
|
690
|
+
let previousPath;
|
|
691
|
+
while (previousPath !== currentPath) {
|
|
692
|
+
newPath.push(path.join(currentPath, "node_modules", ".bin"));
|
|
693
|
+
previousPath = currentPath;
|
|
694
|
+
currentPath = path.resolve(currentPath, "..");
|
|
695
|
+
}
|
|
696
|
+
const defaultPath = process.platform === "win32" ? "Path" : "PATH";
|
|
697
|
+
const pathKey = Object.keys(env).find((k) => k.toLowerCase() === "path") ?? defaultPath;
|
|
698
|
+
if (env[pathKey]) newPath.push(env[pathKey]);
|
|
699
|
+
return {
|
|
700
|
+
...env,
|
|
701
|
+
[pathKey]: newPath.join(pathSeparator)
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Create environment variables for Playwright test server process
|
|
706
|
+
*/
|
|
707
|
+
function createTestServerEnv(baseEnv, customEnv, cwd) {
|
|
708
|
+
return addNpmRunPath({
|
|
709
|
+
...baseEnv,
|
|
710
|
+
...customEnv,
|
|
711
|
+
FORCE_COLOR: "1"
|
|
712
|
+
}, cwd);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
//#endregion
|
|
716
|
+
//#region src/utils/reporterFactory.ts
|
|
717
|
+
/**
|
|
718
|
+
* Create TeleReporterReceiver for listing files
|
|
719
|
+
* Uses special path resolution for preserving relative paths for files
|
|
720
|
+
*/
|
|
721
|
+
function createFileListReceiver(reporter) {
|
|
722
|
+
return new TeleReporterReceiver(reporter, {
|
|
723
|
+
mergeProjects: true,
|
|
724
|
+
mergeTestCases: true,
|
|
725
|
+
resolvePath: resolvePathForFiles
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Create TeleReporterReceiver for listing tests
|
|
730
|
+
* Uses standard absolute path resolution
|
|
731
|
+
*/
|
|
732
|
+
function createTestListReceiver(reporter) {
|
|
733
|
+
return new TeleReporterReceiver(reporter, {
|
|
734
|
+
mergeProjects: true,
|
|
735
|
+
mergeTestCases: true,
|
|
736
|
+
resolvePath: resolveAbsolutePath
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Process report messages through receiver
|
|
741
|
+
*/
|
|
742
|
+
function processReport(receiver, report) {
|
|
743
|
+
for (const message of report) receiver.dispatch(message);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
//#endregion
|
|
747
|
+
//#region src/playwrightTestServer.ts
|
|
748
|
+
/**
|
|
749
|
+
* Standalone Playwright Test Server
|
|
750
|
+
* Based on Microsoft's playwright-vscode implementation
|
|
751
|
+
* Removed all VSCode-specific dependencies and test running functionality
|
|
752
|
+
* Only supports listing tests and files
|
|
753
|
+
*/
|
|
649
754
|
var CancellationToken = class {
|
|
650
755
|
_isCancelled = false;
|
|
651
756
|
_onCancelEmitter = new EventEmitter();
|
|
@@ -664,7 +769,6 @@ var PlaywrightTestServer = class {
|
|
|
664
769
|
_config;
|
|
665
770
|
_options;
|
|
666
771
|
_testServerPromise;
|
|
667
|
-
_cli = null;
|
|
668
772
|
constructor(config = {
|
|
669
773
|
workspaceFolder: process.cwd(),
|
|
670
774
|
configFile: "playwright.config.ts"
|
|
@@ -672,39 +776,24 @@ var PlaywrightTestServer = class {
|
|
|
672
776
|
this._config = config;
|
|
673
777
|
this._options = options;
|
|
674
778
|
}
|
|
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
779
|
reset() {
|
|
689
780
|
this._disposeTestServer();
|
|
690
781
|
}
|
|
691
782
|
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
|
-
}
|
|
783
|
+
const connection = await this._ensureConnection();
|
|
697
784
|
const result = { projects: [] };
|
|
698
|
-
const { report } = await
|
|
699
|
-
const
|
|
785
|
+
const { report } = await connection.listFiles({});
|
|
786
|
+
const workspaceFolder = this._config.workspaceFolder;
|
|
787
|
+
processReport(createFileListReceiver({
|
|
700
788
|
onConfigure: (_config) => {},
|
|
701
789
|
onBegin(rootSuite) {
|
|
702
790
|
for (const projectSuite of rootSuite.suites) {
|
|
703
791
|
const project = projectSuite.project();
|
|
704
792
|
const files = [];
|
|
793
|
+
const testDir = project.testDir ? path.relative(workspaceFolder, project.testDir).replace(/\\/g, "/") : "";
|
|
705
794
|
result.projects.push({
|
|
706
795
|
name: project.name,
|
|
707
|
-
testDir
|
|
796
|
+
testDir,
|
|
708
797
|
use: project.use || {},
|
|
709
798
|
files
|
|
710
799
|
});
|
|
@@ -714,12 +803,7 @@ var PlaywrightTestServer = class {
|
|
|
714
803
|
onError(error) {
|
|
715
804
|
result.error = error;
|
|
716
805
|
}
|
|
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);
|
|
806
|
+
}), report);
|
|
723
807
|
return result;
|
|
724
808
|
}
|
|
725
809
|
/**
|
|
@@ -727,33 +811,33 @@ var PlaywrightTestServer = class {
|
|
|
727
811
|
* This preserves the complete test.describe hierarchy
|
|
728
812
|
*/
|
|
729
813
|
async getRawTestReport(locations, token) {
|
|
730
|
-
const { connection } = await this._testServer();
|
|
731
814
|
if (token?.isCancellationRequested) return [];
|
|
732
|
-
|
|
815
|
+
const connection = await this._ensureConnection();
|
|
733
816
|
const escapedLocations = locations.map(escapeRegex);
|
|
734
817
|
const { report } = await connection.listTests({ locations: escapedLocations });
|
|
735
818
|
return report;
|
|
736
819
|
}
|
|
737
820
|
async listTests(locations, reporter, token) {
|
|
738
|
-
const { connection } = await this._testServer();
|
|
739
821
|
if (token?.isCancellationRequested) return;
|
|
740
|
-
|
|
822
|
+
const connection = await this._ensureConnection();
|
|
741
823
|
const escapedLocations = locations.map(escapeRegex);
|
|
742
824
|
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);
|
|
825
|
+
processReport(createTestListReceiver(reporter), report);
|
|
749
826
|
}
|
|
750
827
|
async findRelatedTestFiles(files) {
|
|
828
|
+
return await (await this._ensureConnection()).findRelatedTestFiles({ files });
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Ensure connection is established and return it
|
|
832
|
+
* Throws error if connection cannot be established
|
|
833
|
+
*/
|
|
834
|
+
async _ensureConnection() {
|
|
751
835
|
const testServer = await this._testServer();
|
|
752
|
-
if (!testServer.connection)
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
}
|
|
756
|
-
return
|
|
836
|
+
if (!testServer.connection) {
|
|
837
|
+
const errors = testServer.errors.length ? ". Test server errors: " + testServer.errors.join("\n") : "";
|
|
838
|
+
throw new Error("Internal error: unable to connect to the test server." + errors);
|
|
839
|
+
}
|
|
840
|
+
return testServer.connection;
|
|
757
841
|
}
|
|
758
842
|
_testServer() {
|
|
759
843
|
if (this._testServerPromise) return this._testServerPromise;
|
|
@@ -762,7 +846,7 @@ var PlaywrightTestServer = class {
|
|
|
762
846
|
}
|
|
763
847
|
async _createTestServer() {
|
|
764
848
|
const cwd = this._config.workspaceFolder;
|
|
765
|
-
const cli = path.relative(cwd,
|
|
849
|
+
const cli = path.relative(cwd, findPlaywrightCli(cwd));
|
|
766
850
|
const config = path.relative(cwd, this._config.configFile);
|
|
767
851
|
const errors = [];
|
|
768
852
|
const wsEndpoint = await this._startBackend({
|
|
@@ -773,21 +857,7 @@ var PlaywrightTestServer = class {
|
|
|
773
857
|
config
|
|
774
858
|
],
|
|
775
859
|
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
|
-
}
|
|
860
|
+
errors
|
|
791
861
|
});
|
|
792
862
|
if (!wsEndpoint) return {
|
|
793
863
|
connection: null,
|
|
@@ -805,10 +875,8 @@ var PlaywrightTestServer = class {
|
|
|
805
875
|
}
|
|
806
876
|
async _startBackend(options) {
|
|
807
877
|
const node = process.execPath;
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
...options.envProvider()
|
|
811
|
-
}, options.cwd);
|
|
878
|
+
const customEnv = this._options.envProvider?.(this._config.configFile) || {};
|
|
879
|
+
const env = createTestServerEnv(process.env, customEnv, options.cwd);
|
|
812
880
|
const serverProcess = spawn(node, options.args, {
|
|
813
881
|
cwd: options.cwd,
|
|
814
882
|
stdio: "pipe",
|
|
@@ -819,10 +887,14 @@ var PlaywrightTestServer = class {
|
|
|
819
887
|
options.errors.push(message);
|
|
820
888
|
this._options.onStdErr?.(message);
|
|
821
889
|
});
|
|
822
|
-
serverProcess.on("error",
|
|
823
|
-
|
|
890
|
+
serverProcess.on("error", () => {
|
|
891
|
+
this._testServerPromise = void 0;
|
|
892
|
+
});
|
|
893
|
+
serverProcess.on("close", () => {
|
|
894
|
+
this._testServerPromise = void 0;
|
|
895
|
+
});
|
|
824
896
|
return new Promise((fulfill) => {
|
|
825
|
-
serverProcess.stdout?.on("data",
|
|
897
|
+
serverProcess.stdout?.on("data", (data) => {
|
|
826
898
|
const output = data.toString();
|
|
827
899
|
this._options.onStdOut?.(output);
|
|
828
900
|
const match = output.match(/Listening on (.*)/);
|
|
@@ -831,24 +903,6 @@ var PlaywrightTestServer = class {
|
|
|
831
903
|
serverProcess.on("exit", () => fulfill(null));
|
|
832
904
|
});
|
|
833
905
|
}
|
|
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
906
|
_disposeTestServer() {
|
|
853
907
|
const testServer = this._testServerPromise;
|
|
854
908
|
this._testServerPromise = void 0;
|
|
@@ -869,7 +923,7 @@ async function listTestFile(workspaceFolder = process.cwd(), configFile = "playw
|
|
|
869
923
|
try {
|
|
870
924
|
const output = { projects: (await testServer.listFiles()).projects.map((project) => ({
|
|
871
925
|
...project,
|
|
872
|
-
files: project.files.map((filePath) =>
|
|
926
|
+
files: project.files.map((filePath) => filePath.replace(/\\/g, "/"))
|
|
873
927
|
})) };
|
|
874
928
|
const jsonString = JSON.stringify(output, null, 2);
|
|
875
929
|
if (outputFile) {
|
|
@@ -896,13 +950,16 @@ async function listTestCase(workspaceFolder = process.cwd(), configFile = "playw
|
|
|
896
950
|
try {
|
|
897
951
|
if ((await fs.stat(absolutePath)).isDirectory()) {
|
|
898
952
|
const files = await testServer.listFiles();
|
|
899
|
-
for (const project of files.projects)
|
|
900
|
-
const
|
|
901
|
-
|
|
953
|
+
for (const project of files.projects) {
|
|
954
|
+
const testDirRelative = project.testDir || "";
|
|
955
|
+
for (const file of project.files) {
|
|
956
|
+
const fullPath = testDirRelative ? path.join(testDirRelative, file).replace(/\\/g, "/") : file.replace(/\\/g, "/");
|
|
957
|
+
if (fullPath.startsWith(normalizedInputPath + "/") || fullPath === normalizedInputPath) expandedPaths.push(fullPath);
|
|
958
|
+
}
|
|
902
959
|
}
|
|
903
|
-
} else expandedPaths.push(
|
|
960
|
+
} else expandedPaths.push(normalizedInputPath);
|
|
904
961
|
} catch {
|
|
905
|
-
expandedPaths.push(
|
|
962
|
+
expandedPaths.push(normalizedInputPath);
|
|
906
963
|
}
|
|
907
964
|
}
|
|
908
965
|
if (expandedPaths.length === 0) {
|
|
@@ -944,11 +1001,6 @@ async function listTestCase(workspaceFolder = process.cwd(), configFile = "playw
|
|
|
944
1001
|
title: "Root",
|
|
945
1002
|
children: []
|
|
946
1003
|
};
|
|
947
|
-
let rootDir = "";
|
|
948
|
-
for (const message of report) if (message.method === "onConfigure") {
|
|
949
|
-
rootDir = message.params.config.rootDir || "";
|
|
950
|
-
break;
|
|
951
|
-
}
|
|
952
1004
|
for (const message of report) if (message.method === "onProject") {
|
|
953
1005
|
const project = message.params.project;
|
|
954
1006
|
const projectNode = {
|
|
@@ -956,8 +1008,8 @@ async function listTestCase(workspaceFolder = process.cwd(), configFile = "playw
|
|
|
956
1008
|
title: project.name,
|
|
957
1009
|
children: []
|
|
958
1010
|
};
|
|
959
|
-
|
|
960
|
-
|
|
1011
|
+
let testDir = "";
|
|
1012
|
+
if (project.testDir) testDir = path.isAbsolute(project.testDir) ? path.relative(workspaceFolder, project.testDir).replace(/\\/g, "/") : project.testDir.replace(/\\/g, "/");
|
|
961
1013
|
if (project.suites && Array.isArray(project.suites)) for (const suite of project.suites) {
|
|
962
1014
|
const suiteNode = parseEntry(suite, testDir);
|
|
963
1015
|
projectNode.children.push(suiteNode);
|
|
@@ -989,8 +1041,9 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
|
|
|
989
1041
|
}
|
|
990
1042
|
const allRelativePaths = [];
|
|
991
1043
|
for (const project of files.projects) if (project.files.length > 0) {
|
|
992
|
-
const
|
|
993
|
-
|
|
1044
|
+
const testDirRelative = project.testDir || "";
|
|
1045
|
+
const relativePaths = project.files.map((file) => {
|
|
1046
|
+
return testDirRelative ? path.join(testDirRelative, file).replace(/\\/g, "/") : file.replace(/\\/g, "/");
|
|
994
1047
|
});
|
|
995
1048
|
allRelativePaths.push(...relativePaths);
|
|
996
1049
|
}
|
|
@@ -1001,7 +1054,7 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
|
|
|
1001
1054
|
const report = await testServer.getRawTestReport(allRelativePaths, token);
|
|
1002
1055
|
function parseEntry(entry, testDir) {
|
|
1003
1056
|
if (entry.testId) {
|
|
1004
|
-
const filePath =
|
|
1057
|
+
const filePath = entry.location.file.replace(/\\/g, "/");
|
|
1005
1058
|
return {
|
|
1006
1059
|
type: "test",
|
|
1007
1060
|
title: entry.title,
|
|
@@ -1017,7 +1070,7 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
|
|
|
1017
1070
|
timeout: 0
|
|
1018
1071
|
};
|
|
1019
1072
|
} else {
|
|
1020
|
-
const filePath = entry.location
|
|
1073
|
+
const filePath = entry.location?.file.replace(/\\/g, "/");
|
|
1021
1074
|
const node = {
|
|
1022
1075
|
type: "suite",
|
|
1023
1076
|
title: entry.title,
|
|
@@ -1033,11 +1086,6 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
|
|
|
1033
1086
|
title: "Root",
|
|
1034
1087
|
children: []
|
|
1035
1088
|
};
|
|
1036
|
-
let rootDir = "";
|
|
1037
|
-
for (const message of report) if (message.method === "onConfigure") {
|
|
1038
|
-
rootDir = message.params.config.rootDir || "";
|
|
1039
|
-
break;
|
|
1040
|
-
}
|
|
1041
1089
|
for (const message of report) if (message.method === "onProject") {
|
|
1042
1090
|
const project = message.params.project;
|
|
1043
1091
|
const projectNode = {
|
|
@@ -1045,8 +1093,8 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
|
|
|
1045
1093
|
title: project.name,
|
|
1046
1094
|
children: []
|
|
1047
1095
|
};
|
|
1048
|
-
|
|
1049
|
-
|
|
1096
|
+
let testDir = "";
|
|
1097
|
+
if (project.testDir) testDir = path.isAbsolute(project.testDir) ? path.relative(workspaceFolder, project.testDir).replace(/\\/g, "/") : project.testDir.replace(/\\/g, "/");
|
|
1050
1098
|
if (project.suites && Array.isArray(project.suites)) for (const suite of project.suites) {
|
|
1051
1099
|
const suiteNode = parseEntry(suite, testDir);
|
|
1052
1100
|
projectNode.children.push(suiteNode);
|
package/dist/{cli.js → cli.mjs}
RENAMED
|
@@ -1,11 +1,15 @@
|
|
|
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-Bc2ZCCQ0.mjs";
|
|
3
|
+
import path from "path";
|
|
3
4
|
import { program } from "commander";
|
|
4
5
|
import chalk from "chalk";
|
|
5
6
|
import ora from "ora";
|
|
6
|
-
import path from "path";
|
|
7
7
|
|
|
8
8
|
//#region src/cli.ts
|
|
9
|
+
/**
|
|
10
|
+
* Playwright Test Explorer CLI
|
|
11
|
+
* Command-line interface for exploring Playwright tests
|
|
12
|
+
*/
|
|
9
13
|
program.name("pw-test-explorer").description("CLI tool for exploring Playwright tests").version("0.1.0");
|
|
10
14
|
program.command("list-files").description("List all test files in the Playwright project").option("-w, --workspace <path>", "workspace folder path", process.cwd()).option("-c, --config <file>", "config file path (relative to workspace)", "playwright.config.ts").option("-o, --output <file>", "output file path to save JSON results").action(async (options) => {
|
|
11
15
|
const spinner = ora("Loading test files...").start();
|
package/dist/index.mjs
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "playwright-test-explorer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "A CLI tool and library for exploring Playwright tests",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
"files": [
|
|
17
17
|
"dist"
|
|
18
18
|
],
|
|
19
|
-
"main": "./dist/index.
|
|
20
|
-
"module": "./dist/index.
|
|
21
|
-
"types": "./dist/index.d.
|
|
19
|
+
"main": "./dist/index.mjs",
|
|
20
|
+
"module": "./dist/index.mjs",
|
|
21
|
+
"types": "./dist/index.d.mts",
|
|
22
22
|
"bin": {
|
|
23
|
-
"pw-test-explorer": "./dist/cli.
|
|
23
|
+
"pw-test-explorer": "./dist/cli.mjs"
|
|
24
24
|
},
|
|
25
25
|
"exports": {
|
|
26
|
-
".": "./dist/index.
|
|
26
|
+
".": "./dist/index.mjs",
|
|
27
27
|
"./package.json": "./package.json"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
"test:coverage": "vitest --coverage",
|
|
35
35
|
"test:legacy": "node test-cli.js",
|
|
36
36
|
"typecheck": "tsc --noEmit",
|
|
37
|
-
"test:example": "node dist/cli.
|
|
38
|
-
"test:list-files": "node dist/cli.
|
|
39
|
-
"test:list-tests": "node dist/cli.
|
|
40
|
-
"test:save-tree": "node dist/cli.
|
|
41
|
-
"test:save-files": "node dist/cli.
|
|
37
|
+
"test:example": "node dist/cli.mjs tree -w example",
|
|
38
|
+
"test:list-files": "node dist/cli.mjs list-files -w example",
|
|
39
|
+
"test:list-tests": "node dist/cli.mjs list-tests -w example -f tests/example.spec.ts",
|
|
40
|
+
"test:save-tree": "node dist/cli.mjs tree -w example -o example-tree.json",
|
|
41
|
+
"test:save-files": "node dist/cli.mjs list-files -w example -o example-files.json"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"chalk": "^5.6.2",
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
"ws": "^8.18.3"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@playwright/test": "^1.
|
|
51
|
-
"@types/node": "^24.
|
|
50
|
+
"@playwright/test": "^1.57.0",
|
|
51
|
+
"@types/node": "^24.10.1",
|
|
52
52
|
"@types/ws": "^8.18.1",
|
|
53
|
-
"@vitest/coverage-v8": "4.0.
|
|
54
|
-
"bumpp": "^10.3.
|
|
55
|
-
"tsdown": "^0.
|
|
53
|
+
"@vitest/coverage-v8": "4.0.14",
|
|
54
|
+
"bumpp": "^10.3.2",
|
|
55
|
+
"tsdown": "^0.16.7",
|
|
56
56
|
"typescript": "^5.9.3",
|
|
57
|
-
"vitest": "^4.0.
|
|
57
|
+
"vitest": "^4.0.14"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/dist/index.js
DELETED
|
File without changes
|
|
File without changes
|