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 CHANGED
@@ -1,363 +1,79 @@
1
1
  # Playwright Test Explorer
2
2
 
3
- A CLI tool and library for exploring Playwright tests. This tool provides easy access to test structure, test files, and test cases in your Playwright projects.
3
+ Playwright 测试结构探索工具 - 用最简单的方式查看和分析你的测试。
4
4
 
5
- ## Features
5
+ ## 特性
6
6
 
7
- - 📋 List all test files in a Playwright project
8
- - 🔍 List test cases with detailed information (location, tags, annotations)
9
- - 🌳 Display test structure as a tree
10
- - 💻 Easy-to-use CLI interface
11
- - 📦 Can be used as a library in your Node.js projects
7
+ - 📋 列出所有测试文件
8
+ - 🔍 分析测试用例详情(位置、标签、注解)
9
+ - 🌳 树形结构展示完整测试层级
10
+ - 💻 CLI 命令 & JavaScript API 双模式
11
+ - 📦 支持输出到 JSON 文件
12
12
 
13
- ## Installation
13
+ ## 快速开始
14
14
 
15
- ### Global Installation
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
- ```bash
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
- ## CLI Usage
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
- ### Display Test Tree
31
+ # 列出指定文件中的测试用例
32
+ pw-test-explorer list-tests -f tests/example.spec.ts
89
33
 
90
- Display the complete test structure as a tree:
91
-
92
- ```bash
34
+ # 显示完整测试树
93
35
  pw-test-explorer tree
94
- ```
95
36
 
96
- With custom workspace:
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
- Save results to a JSON file:
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
- // 1. List all test files in a project (output to console)
46
+ // 列出测试文件
125
47
  await listTestFile('/path/to/project', 'playwright.config.ts');
126
48
 
127
- // List all test files and save to a file
128
- await listTestFile('/path/to/project', 'playwright.config.ts', 'output.json');
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
- // List test cases and save to a file
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
- All three functions output results in JSON format - either to `console.log` or to a specified file.
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
- **Parameters:**
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
- **Returns:** `Promise<void>`
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
- # Install dependencies and build
274
- pnpm install
275
- pnpm run build
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
- ### Example Project
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
- ### Test Suites
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
- ## Credits
77
+ ---
362
78
 
363
- Based on the test server implementation from [Microsoft's playwright-vscode](https://github.com/microsoft/playwright-vscode).
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/playwrightTestServer.ts
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 testServer = await this._testServer();
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 testServer.connection.listFiles({});
699
- const teleReceiver = new TeleReporterReceiver({
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: project.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
- if (!connection) return [];
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
- if (!connection) return;
822
+ const connection = await this._ensureConnection();
741
823
  const escapedLocations = locations.map(escapeRegex);
742
824
  const { report } = await connection.listTests({ locations: escapedLocations });
743
- const teleReceiver = new TeleReporterReceiver(reporter, {
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) return {
753
- testFiles: files,
754
- errors: [{ message: "Internal error: unable to connect to the test server" }]
755
- };
756
- return await testServer.connection.findRelatedTestFiles({ files });
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, this._findPlaywrightCli());
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
- envProvider: () => {
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 env = this._addNpmRunPath({
809
- ...process.env,
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", options.onError);
823
- serverProcess.on("close", options.onClose);
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", async (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) => path.relative(workspaceFolder, filePath).replace(/\\/g, "/"))
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) for (const file of project.files) {
900
- const relativeFile = path.relative(workspaceFolder, file).replace(/\\/g, "/");
901
- if (relativeFile.startsWith(normalizedInputPath + "/") || relativeFile === normalizedInputPath) expandedPaths.push(relativeFile);
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(relativePath);
960
+ } else expandedPaths.push(normalizedInputPath);
904
961
  } catch {
905
- expandedPaths.push(relativePath);
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
- const relativeRootDir = rootDir ? path.relative(workspaceFolder, rootDir).replace(/\\/g, "/") : "";
960
- const testDir = project.testDir ? path.join(relativeRootDir, project.testDir).replace(/\\/g, "/") : relativeRootDir;
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 relativePaths = project.files.map((absolutePath) => {
993
- return path.relative(workspaceFolder, absolutePath).replace(/\\/g, "/");
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 = testDir ? path.join(testDir, entry.location.file).replace(/\\/g, "/") : entry.location.file.replace(/\\/g, "/");
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 && testDir ? path.join(testDir, entry.location.file).replace(/\\/g, "/") : entry.location?.file.replace(/\\/g, "/");
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
- const relativeRootDir = rootDir ? path.relative(workspaceFolder, rootDir).replace(/\\/g, "/") : "";
1049
- const testDir = project.testDir ? path.join(relativeRootDir, project.testDir).replace(/\\/g, "/") : relativeRootDir;
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);
@@ -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-JooYjXT5.js";
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
@@ -0,0 +1,3 @@
1
+ import { n as listTestFile, r as treeStructure, t as listTestCase } from "./api-Bc2ZCCQ0.mjs";
2
+
3
+ export { listTestCase, listTestFile, treeStructure };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playwright-test-explorer",
3
- "version": "0.0.7",
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.js",
20
- "module": "./dist/index.js",
21
- "types": "./dist/index.d.ts",
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.js"
23
+ "pw-test-explorer": "./dist/cli.mjs"
24
24
  },
25
25
  "exports": {
26
- ".": "./dist/index.js",
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.js tree -w example",
38
- "test:list-files": "node dist/cli.js list-files -w example",
39
- "test:list-tests": "node dist/cli.js list-tests -w example -f tests/example.spec.ts",
40
- "test:save-tree": "node dist/cli.js tree -w example -o example-tree.json",
41
- "test:save-files": "node dist/cli.js list-files -w example -o example-files.json"
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.56.1",
51
- "@types/node": "^24.9.2",
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.5",
54
- "bumpp": "^10.3.1",
55
- "tsdown": "^0.15.12",
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.5"
57
+ "vitest": "^4.0.14"
58
58
  }
59
59
  }
package/dist/index.js DELETED
@@ -1,3 +0,0 @@
1
- import { n as listTestFile, r as treeStructure, t as listTestCase } from "./api-JooYjXT5.js";
2
-
3
- export { listTestCase, listTestFile, treeStructure };
File without changes
File without changes