playwright-test-explorer 0.0.6 → 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 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,7 @@ var TestServerConnection = class {
637
637
  };
638
638
 
639
639
  //#endregion
640
- //#region src/playwrightTestServer.ts
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 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
- }
771
+ const connection = await this._ensureConnection();
697
772
  const result = { projects: [] };
698
- const { report } = await testServer.connection.listFiles({});
699
- const teleReceiver = new TeleReporterReceiver({
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: project.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
- if (!connection) return [];
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
- if (!connection) return;
810
+ const connection = await this._ensureConnection();
741
811
  const escapedLocations = locations.map(escapeRegex);
742
812
  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);
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) return {
753
- testFiles: files,
754
- errors: [{ message: "Internal error: unable to connect to the test server" }]
755
- };
756
- return await testServer.connection.findRelatedTestFiles({ files });
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, this._findPlaywrightCli());
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
- 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
- }
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 env = this._addNpmRunPath({
809
- ...process.env,
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", options.onError);
823
- serverProcess.on("close", options.onClose);
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", async (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) => path.relative(workspaceFolder, filePath).replace(/\\/g, "/"))
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) 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);
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(relativePath);
948
+ } else expandedPaths.push(normalizedInputPath);
904
949
  } catch {
905
- expandedPaths.push(relativePath);
950
+ expandedPaths.push(normalizedInputPath);
906
951
  }
907
952
  }
908
953
  if (expandedPaths.length === 0) {
@@ -916,6 +961,7 @@ async function listTestCase(workspaceFolder = process.cwd(), configFile = "playw
916
961
  return {
917
962
  type: "test",
918
963
  title: entry.title,
964
+ testId: entry.testId,
919
965
  location: {
920
966
  file: filePath,
921
967
  line: entry.location.line,
@@ -943,11 +989,6 @@ async function listTestCase(workspaceFolder = process.cwd(), configFile = "playw
943
989
  title: "Root",
944
990
  children: []
945
991
  };
946
- let rootDir = "";
947
- for (const message of report) if (message.method === "onConfigure") {
948
- rootDir = message.params.config.rootDir || "";
949
- break;
950
- }
951
992
  for (const message of report) if (message.method === "onProject") {
952
993
  const project = message.params.project;
953
994
  const projectNode = {
@@ -955,8 +996,8 @@ async function listTestCase(workspaceFolder = process.cwd(), configFile = "playw
955
996
  title: project.name,
956
997
  children: []
957
998
  };
958
- const relativeRootDir = rootDir ? path.relative(workspaceFolder, rootDir).replace(/\\/g, "/") : "";
959
- const testDir = project.testDir ? path.join(relativeRootDir, project.testDir).replace(/\\/g, "/") : relativeRootDir;
999
+ let testDir = "";
1000
+ if (project.testDir) testDir = path.isAbsolute(project.testDir) ? path.relative(workspaceFolder, project.testDir).replace(/\\/g, "/") : project.testDir.replace(/\\/g, "/");
960
1001
  if (project.suites && Array.isArray(project.suites)) for (const suite of project.suites) {
961
1002
  const suiteNode = parseEntry(suite, testDir);
962
1003
  projectNode.children.push(suiteNode);
@@ -988,8 +1029,9 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
988
1029
  }
989
1030
  const allRelativePaths = [];
990
1031
  for (const project of files.projects) if (project.files.length > 0) {
991
- const relativePaths = project.files.map((absolutePath) => {
992
- return path.relative(workspaceFolder, absolutePath).replace(/\\/g, "/");
1032
+ const testDirRelative = project.testDir || "";
1033
+ const relativePaths = project.files.map((file) => {
1034
+ return testDirRelative ? path.join(testDirRelative, file).replace(/\\/g, "/") : file.replace(/\\/g, "/");
993
1035
  });
994
1036
  allRelativePaths.push(...relativePaths);
995
1037
  }
@@ -1000,10 +1042,11 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
1000
1042
  const report = await testServer.getRawTestReport(allRelativePaths, token);
1001
1043
  function parseEntry(entry, testDir) {
1002
1044
  if (entry.testId) {
1003
- const filePath = testDir ? path.join(testDir, entry.location.file).replace(/\\/g, "/") : entry.location.file.replace(/\\/g, "/");
1045
+ const filePath = entry.location.file.replace(/\\/g, "/");
1004
1046
  return {
1005
1047
  type: "test",
1006
1048
  title: entry.title,
1049
+ testId: entry.testId,
1007
1050
  location: {
1008
1051
  file: filePath,
1009
1052
  line: entry.location.line,
@@ -1015,7 +1058,7 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
1015
1058
  timeout: 0
1016
1059
  };
1017
1060
  } else {
1018
- const filePath = entry.location && testDir ? path.join(testDir, entry.location.file).replace(/\\/g, "/") : entry.location?.file.replace(/\\/g, "/");
1061
+ const filePath = entry.location?.file.replace(/\\/g, "/");
1019
1062
  const node = {
1020
1063
  type: "suite",
1021
1064
  title: entry.title,
@@ -1031,11 +1074,6 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
1031
1074
  title: "Root",
1032
1075
  children: []
1033
1076
  };
1034
- let rootDir = "";
1035
- for (const message of report) if (message.method === "onConfigure") {
1036
- rootDir = message.params.config.rootDir || "";
1037
- break;
1038
- }
1039
1077
  for (const message of report) if (message.method === "onProject") {
1040
1078
  const project = message.params.project;
1041
1079
  const projectNode = {
@@ -1043,8 +1081,8 @@ async function treeStructure(workspaceFolder = process.cwd(), configFile = "play
1043
1081
  title: project.name,
1044
1082
  children: []
1045
1083
  };
1046
- const relativeRootDir = rootDir ? path.relative(workspaceFolder, rootDir).replace(/\\/g, "/") : "";
1047
- const testDir = project.testDir ? path.join(relativeRootDir, project.testDir).replace(/\\/g, "/") : relativeRootDir;
1084
+ let testDir = "";
1085
+ if (project.testDir) testDir = path.isAbsolute(project.testDir) ? path.relative(workspaceFolder, project.testDir).replace(/\\/g, "/") : project.testDir.replace(/\\/g, "/");
1048
1086
  if (project.suites && Array.isArray(project.suites)) for (const suite of project.suites) {
1049
1087
  const suiteNode = parseEntry(suite, testDir);
1050
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-CPNS0jOv.js";
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
@@ -1,3 +1,3 @@
1
- import { n as listTestFile, r as treeStructure, t as listTestCase } from "./api-CPNS0jOv.js";
1
+ import { n as listTestFile, r as treeStructure, t as listTestCase } from "./api-B_yjDCcC.js";
2
2
 
3
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.6",
3
+ "version": "0.0.8",
4
4
  "description": "A CLI tool and library for exploring Playwright tests",
5
5
  "type": "module",
6
6
  "license": "MIT",