diff --git a/packages/vscode/README.md b/packages/vscode/README.md
index dc49bffd1..a0274c8ce 100644
--- a/packages/vscode/README.md
+++ b/packages/vscode/README.md
@@ -15,10 +15,11 @@ The extension activates automatically when your workspace contains Rstest config
## Configuration
-| Setting | Type | Default | Description |
-| ------------------------------ | -------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `rstest.rstestPackagePath` | string | `undefined` | The path to a `package.json` file of a Rstest executable (it's usually inside `node_modules`) in case the extension cannot find it. It will be used to resolve Rstest API paths. This should be used as a last resort fix. Supports `${workspaceFolder}` placeholder. |
-| `rstest.configFileGlobPattern` | string[] | `["**/rstest.config.{mjs,ts,js,cjs,mts,cts}"]` | Glob patterns used to discover config files. |
+| Setting | Type | Default | Description |
+| ------------------------------ | -------------------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `rstest.rstestPackagePath` | `string` | `undefined` | The path to a `package.json` file of a Rstest executable (it's usually inside `node_modules`) in case the extension cannot find it. It will be used to resolve Rstest API paths. This should be used as a last resort fix. Supports `${workspaceFolder}` placeholder. |
+| `rstest.configFileGlobPattern` | `string[]` | `["**/rstest.config.{mjs,ts,js,cjs,mts,cts}"]` | Glob patterns used to discover config files. |
+| `rstest.testCaseCollectMethod` | `"ast" \| "runtime"` | `"ast"` | `"ast"`: Fast, only supports basic test cases.
`"runtime"`: Slow, supports all test cases, including dynamic test generation methods (each/for/extend). |
## How it works
diff --git a/packages/vscode/package.json b/packages/vscode/package.json
index b44f45c86..ed093aa89 100644
--- a/packages/vscode/package.json
+++ b/packages/vscode/package.json
@@ -41,6 +41,22 @@
"default": [
"**/rstest.config.{mjs,ts,js,cjs,mts,cts}"
]
+ },
+ "rstest.testCaseCollectMethod": {
+ "type": "string",
+ "default": "ast",
+ "enum": [
+ "ast",
+ "runtime"
+ ],
+ "enumItemLabels": [
+ "Static AST Analyze",
+ "Run Test File"
+ ],
+ "enumDescriptions": [
+ "Fast, only supports basic test cases.",
+ "Slow, supports all test cases, including dynamic test generation methods (each/for/extend)."
+ ]
}
}
},
diff --git a/packages/vscode/src/config.ts b/packages/vscode/src/config.ts
index bace8e7da..5c01efa7c 100644
--- a/packages/vscode/src/config.ts
+++ b/packages/vscode/src/config.ts
@@ -10,6 +10,10 @@ const configSchema = v.object({
configFileGlobPattern: v.fallback(v.array(v.string()), [
'**/rstest.config.{mjs,ts,js,cjs,mts,cts}',
]),
+ testCaseCollectMethod: v.fallback(
+ v.union([v.literal('ast'), v.literal('runtime')]),
+ 'ast',
+ ),
});
export type ExtensionConfig = v.InferOutput;
diff --git a/packages/vscode/src/master.ts b/packages/vscode/src/master.ts
index ec664fbbd..653e7d5b9 100644
--- a/packages/vscode/src/master.ts
+++ b/packages/vscode/src/master.ts
@@ -115,6 +115,18 @@ export class RstestApi {
return config;
}
+ public async listTests(include?: string[]) {
+ const worker = await this.createChildProcess();
+ const tests = await worker.listTests({
+ rstestPath: this.resolveRstestPath(),
+ configFilePath: this.configFilePath,
+ include,
+ includeTaskLocation: true,
+ });
+ worker.$close();
+ return tests;
+ }
+
public async runTest({
run,
token,
@@ -160,6 +172,7 @@ export class RstestApi {
kind === vscode.TestRunProfileKind.Coverage
? { enabled: true }
: undefined,
+ includeTaskLocation: true,
})
.finally(() => {
worker.$close();
diff --git a/packages/vscode/src/project.ts b/packages/vscode/src/project.ts
index 84cad3d8d..4891bf6a3 100644
--- a/packages/vscode/src/project.ts
+++ b/packages/vscode/src/project.ts
@@ -1,4 +1,5 @@
import path from 'node:path';
+import type { TestInfo } from '@rstest/core';
import picomatch from 'picomatch';
import { glob } from 'tinyglobby';
import * as vscode from 'vscode';
@@ -212,68 +213,116 @@ export class Project implements vscode.Disposable {
return matchInclude(relativePath) && !matchExclude(relativePath);
};
- const files = await glob(this.include, {
- cwd: root.fsPath,
- ignore: this.exclude,
- absolute: true,
- dot: true,
- expandDirectories: false,
- }).then((files) => files.map((file) => vscode.Uri.file(file)));
+ const watcher = watchConfigValue(
+ 'testCaseCollectMethod',
+ this.workspaceFolder,
+ async (method, token) => {
+ if (this.testItem) {
+ this.testItem.busy = true;
+ }
+ const files: { uri: vscode.Uri; tests?: TestInfo[] }[] =
+ method === 'ast'
+ ? // ast
+ await glob(this.include, {
+ cwd: root.fsPath,
+ ignore: this.exclude,
+ absolute: true,
+ dot: true,
+ expandDirectories: false,
+ }).then((files) =>
+ files.map((file) => ({ uri: vscode.Uri.file(file) })),
+ )
+ : // runtime
+ await this.api.listTests().then((files) =>
+ files.map((file) => ({
+ uri: vscode.Uri.file(file.testPath),
+ tests: file.tests,
+ })),
+ );
- if (this.cancellationSource.token.isCancellationRequested) return;
+ if (token.isCancellationRequested) return;
- const visited = new Set();
- for (const uri of files) {
- if (matchExclude(uri.fsPath)) continue;
- this.updateOrCreateFile(uri);
- visited.add(uri.toString());
- }
+ if (this.testItem) {
+ this.testItem.busy = false;
+ }
- // remove outdated items after glob configuration changed
- for (const file of this.testFiles.keys()) {
- if (!visited.has(file)) {
- this.testFiles.delete(file);
- }
- }
- this.buildTree();
+ const visited = new Set();
+ for (const { uri, tests } of files) {
+ this.updateOrCreateFile(uri, tests);
+ visited.add(uri.toString());
+ }
+
+ // remove outdated items after glob configuration changed
+ for (const file of this.testFiles.keys()) {
+ if (!visited.has(file)) {
+ this.testFiles.delete(file);
+ }
+ }
+ this.buildTree();
+
+ // start watching test file change
+ // while createFileSystemWatcher don't support same glob syntax with tinyglobby
+ // we can watch all files and filter with picomatch later
+ const watcher = vscode.workspace.createFileSystemWatcher(
+ new vscode.RelativePattern(root, '**'),
+ );
+ token.onCancellationRequested(() => watcher.dispose());
+
+ // TODO delay and batch run multiple files
+ const updateOrCreateByRuntime = (uri: vscode.Uri) => {
+ this.api.listTests([uri.fsPath]).then((files) => {
+ if (token.isCancellationRequested) return;
+ for (const { testPath, tests } of files) {
+ const uri = vscode.Uri.file(testPath);
+ this.updateOrCreateFile(uri, tests);
+ }
+ this.buildTree();
+ });
+ };
- // start watching test file change
- // while createFileSystemWatcher don't support same glob syntax with tinyglobby
- // we can watch all files and filter with picomatch later
- const watcher = vscode.workspace.createFileSystemWatcher(
- new vscode.RelativePattern(root, '**'),
+ watcher.onDidCreate((uri) => {
+ if (isInclude(uri)) {
+ if (method === 'ast') {
+ this.updateOrCreateFile(uri);
+ this.buildTree();
+ } else {
+ updateOrCreateByRuntime(uri);
+ }
+ }
+ });
+ watcher.onDidChange((uri) => {
+ if (isInclude(uri)) {
+ if (method === 'ast') {
+ this.updateOrCreateFile(uri);
+ this.buildTree();
+ } else {
+ updateOrCreateByRuntime(uri);
+ }
+ }
+ });
+ watcher.onDidDelete((uri) => {
+ if (isInclude(uri)) {
+ this.testFiles.delete(uri.toString());
+ this.buildTree();
+ }
+ });
+ },
);
this.cancellationSource.token.onCancellationRequested(() =>
watcher.dispose(),
);
- watcher.onDidCreate((uri) => {
- if (isInclude(uri)) {
- this.updateOrCreateFile(uri);
- this.buildTree();
- }
- });
- watcher.onDidChange((uri) => {
- if (isInclude(uri)) {
- this.updateOrCreateFile(uri);
- this.buildTree();
- }
- });
- watcher.onDidDelete((uri) => {
- if (isInclude(uri)) {
- this.testFiles.delete(uri.toString());
- this.buildTree();
- }
- });
}
// TODO pass cancellation token to updateFromDisk
- private updateOrCreateFile(uri: vscode.Uri) {
- const existing = this.testFiles.get(uri.toString());
- if (existing) {
- existing.updateFromDisk(this.testController);
- } else {
- const data = new TestFile(this.api, uri);
+ private updateOrCreateFile(uri: vscode.Uri, tests?: TestInfo[]) {
+ let data = this.testFiles.get(uri.toString());
+ if (!data) {
+ data = new TestFile(this.api, uri, this.testController);
this.testFiles.set(uri.toString(), data);
- data.updateFromDisk(this.testController);
+ }
+ if (tests) {
+ data.updateFromList(tests);
+ } else {
+ data.updateFromDisk();
}
}
diff --git a/packages/vscode/src/testRunReporter.ts b/packages/vscode/src/testRunReporter.ts
index b803dfc4f..d0ddd0610 100644
--- a/packages/vscode/src/testRunReporter.ts
+++ b/packages/vscode/src/testRunReporter.ts
@@ -12,6 +12,7 @@ import { parseErrorStacktrace } from '../../core/src/utils/error';
import { logger } from './logger';
import type { Project } from './project';
import type { LogLevel } from './shared/logger';
+import { TestFile, testData } from './testTree';
export class TestRunReporter implements Reporter {
constructor(
@@ -61,6 +62,17 @@ export class TestRunReporter implements Reporter {
this.run?.started(fileItem);
}
+ onTestFileReady(test: TestFileInfo) {
+ const fileTestItem = this.project?.testFiles.get(
+ vscode.Uri.file(test.testPath).toString(),
+ )?.testItem;
+ if (fileTestItem) {
+ const data = testData.get(fileTestItem);
+ if (data instanceof TestFile) {
+ data.updateFromList(test.tests);
+ }
+ }
+ }
onTestFileResult(test: TestFileResult) {
// only update test file result when explicit run itself or parent
if (this.path.length) return;
diff --git a/packages/vscode/src/testTree.ts b/packages/vscode/src/testTree.ts
index 2c9fa63ec..80f38d2ee 100644
--- a/packages/vscode/src/testTree.ts
+++ b/packages/vscode/src/testTree.ts
@@ -1,8 +1,9 @@
import { TextDecoder } from 'node:util';
+import type { TestInfo } from '@rstest/core';
import vscode from 'vscode';
+import { ROOT_SUITE_NAME } from '../../core/src/utils/constants';
import { logger } from './logger';
import type { RstestApi } from './master';
-import { parseTestFile } from './parserTest';
import type { Project, WorkspaceManager } from './project';
const textDecoder = new TextDecoder('utf-8');
@@ -53,6 +54,7 @@ export class TestFile {
constructor(
public api: RstestApi,
public uri: vscode.Uri,
+ private controller: vscode.TestController,
) {}
public setTestItem(item: vscode.TestItem) {
@@ -60,25 +62,23 @@ export class TestFile {
item.children.replace(this.children);
}
- public async updateFromDisk(controller: vscode.TestController) {
+ public async updateFromDisk() {
const content = await getContentFromFilesystem(this.uri);
- this.updateFromContents(controller, content);
+ this.updateFromContents(content);
}
/**
* Parses the tests from the input text, and updates the tests contained
* by this file to be those from the text,
*/
- public updateFromContents(
- controller: vscode.TestController,
- content: string,
- ) {
+ private async updateFromContents(content: string) {
// Maintain a stack of ancestors to build a hierarchical tree
const ancestors: { name: string; children: vscode.TestItem[] }[] = [
{ name: 'ROOT', children: [] },
];
this.didResolve = true;
+ const { parseTestFile } = await import('./parserTest');
parseTestFile(content, {
onTest: (range, name, testType) => {
const vscodeRange = new vscode.Range(
@@ -88,35 +88,28 @@ export class TestFile {
const parent = ancestors[ancestors.length - 1];
- const siblingsCount = parent.children.filter(
- (child) => child.label === name,
- ).length;
+ const parentNames = ancestors.slice(1).map((item) => item.name);
- // generate unique id to duplicated item
- let id = name;
- if (siblingsCount) id = [name, siblingsCount].join('@@@@@@');
+ const testItem = this.onTest(
+ vscodeRange,
+ name,
+ testType,
+ parent.children,
+ parentNames,
+ );
const isSuite = testType === 'describe' || testType === 'suite';
- const testItem = controller.createTestItem(id, name, this.uri);
testData.set(
testItem,
new TestCase(
this.api,
this.uri,
- ancestors.slice(1).map((item) => item.name),
+ parentNames,
isSuite ? 'suite' : 'case',
),
);
- testItem.range = vscodeRange;
-
- // warn about duplicated name
- if (siblingsCount) testItem.error = `Duplicated ${testType} name`;
-
- // Set TestCase data for both describe blocks and leaf tests
- parent.children.push(testItem);
-
if (isSuite) {
const children: vscode.TestItem[] = [];
// This becomes the new parent for subsequently discovered children
@@ -132,6 +125,75 @@ export class TestFile {
this.children = ancestors[0].children;
this.testItem?.children.replace(this.children);
}
+
+ public updateFromList(tests: TestInfo[]) {
+ const handleChild = (
+ test: TestInfo,
+ parent: vscode.TestItem[],
+ parentNames: string[],
+ ) => {
+ // vscode location is zero based
+ const line = (test.location?.line ?? 1) - 1;
+ const column = (test.location?.column ?? 1) - 1;
+ const range = new vscode.Range(line, column, line, column);
+ const testItem = this.onTest(
+ range,
+ test.name,
+ test.type === 'suite' ? 'suite' : 'test',
+ parent,
+ parentNames,
+ );
+ if (test.type === 'suite') {
+ const children: vscode.TestItem[] = [];
+ test.tests.forEach((child) => {
+ handleChild(child, children, [...parentNames, test.name]);
+ });
+ testItem.children.replace(children);
+ }
+ };
+ const children: vscode.TestItem[] = [];
+ const realTests =
+ tests[0]?.type === 'suite' && tests[0].name === ROOT_SUITE_NAME
+ ? tests[0].tests
+ : tests;
+ realTests.forEach((test) => {
+ handleChild(test, children, []);
+ });
+ this.children = children;
+ this.testItem?.children.replace(this.children);
+ }
+
+ private onTest(
+ range: vscode.Range,
+ name: string,
+ testType: 'test' | 'it' | 'suite' | 'describe',
+ parent: vscode.TestItem[],
+ parentNames: string[],
+ ) {
+ const siblingsCount = parent.filter((child) => child.label === name).length;
+
+ // generate unique id to duplicated item
+ let id = name;
+ if (siblingsCount) id = [name, siblingsCount].join('@@@@@@');
+
+ const isSuite = testType === 'describe' || testType === 'suite';
+
+ const testItem = this.controller.createTestItem(id, name, this.uri);
+ testData.set(
+ testItem,
+ new TestCase(this.api, this.uri, parentNames, isSuite ? 'suite' : 'case'),
+ );
+
+ testItem.range = range;
+
+ // warn about duplicated name
+ if (siblingsCount) testItem.error = `Duplicated ${testType} name`;
+
+ // Set TestCase data for both describe blocks and leaf tests
+ parent.push(testItem);
+
+ return testItem;
+ }
}
export class TestCase {
diff --git a/packages/vscode/src/worker/index.ts b/packages/vscode/src/worker/index.ts
index 82254371f..b343c1b88 100644
--- a/packages/vscode/src/worker/index.ts
+++ b/packages/vscode/src/worker/index.ts
@@ -80,6 +80,12 @@ export class Worker {
throw error;
}
}
+
+ public async listTests(data: WorkerInitOptions) {
+ const rstest = await this.init({ ...data, command: 'list' });
+ const res = await rstest.listTests({});
+ return res;
+ }
}
export const masterApi = createBirpc(new Worker(), {
diff --git a/packages/vscode/src/worker/reporter.ts b/packages/vscode/src/worker/reporter.ts
index 088e11e91..fbe0f7ac6 100644
--- a/packages/vscode/src/worker/reporter.ts
+++ b/packages/vscode/src/worker/reporter.ts
@@ -11,6 +11,7 @@ import { masterApi } from '.';
export class ProgressReporter implements Reporter {
onTestFileStart = masterApi.onTestFileStart.asEvent;
+ onTestFileReady = masterApi.onTestFileReady.asEvent;
onTestFileResult = masterApi.onTestFileResult.asEvent;
onTestSuiteStart = masterApi.onTestSuiteStart.asEvent;
onTestSuiteResult = masterApi.onTestSuiteResult.asEvent;
diff --git a/packages/vscode/tests/fixtures/workspace-1/test/each.test.ts b/packages/vscode/tests/fixtures/workspace-1/test/each.test.ts
new file mode 100644
index 000000000..8a3db22b3
--- /dev/null
+++ b/packages/vscode/tests/fixtures/workspace-1/test/each.test.ts
@@ -0,0 +1,9 @@
+import { describe, it } from '@rstest/core';
+
+describe('suite', () => {
+ it('case', () => {});
+});
+
+describe.each([1, 2])('suite %i', (index) => {
+ it.each([1, 2])(`suite ${index} case %i`, () => {});
+});
diff --git a/packages/vscode/tests/suite/helpers.ts b/packages/vscode/tests/suite/helpers.ts
index c3b0a58c4..f46c26ac9 100644
--- a/packages/vscode/tests/suite/helpers.ts
+++ b/packages/vscode/tests/suite/helpers.ts
@@ -70,6 +70,22 @@ export function getProjectItems(testController: vscode.TestController) {
return getTestItems(folders[0].children);
}
+export function getTestItemByLabels(
+ collection: vscode.TestItemCollection,
+ labels: string[],
+) {
+ const item = labels.reduce(
+ (item, label) =>
+ item &&
+ getTestItems(item.children).find((child) => child.label === label),
+ {
+ children: collection,
+ } as vscode.TestItem | undefined,
+ );
+ assert.ok(item);
+ return item;
+}
+
// Helper: recursively transform a TestItem into a label-only tree.
// Children are sorted by label for stable comparisons.
export function toLabelTree(
diff --git a/packages/vscode/tests/suite/progress.test.ts b/packages/vscode/tests/suite/progress.test.ts
index aa869483d..2fd81bb07 100644
--- a/packages/vscode/tests/suite/progress.test.ts
+++ b/packages/vscode/tests/suite/progress.test.ts
@@ -1,6 +1,6 @@
import * as assert from 'node:assert';
import * as vscode from 'vscode';
-import { getTestItems, waitFor } from './helpers';
+import { getTestItemByLabels, waitFor } from './helpers';
suite('Test Progress Reporting', () => {
test('reports test progress with error details and snapshots', async () => {
@@ -15,18 +15,9 @@ suite('Test Progress Reporting', () => {
rstestInstance?.testController;
assert.ok(testController, 'Test controller should be exported');
- const item = await waitFor(() => {
- const item = ['test', 'progress.test.ts'].reduce(
- (item, label) =>
- item &&
- getTestItems(item.children).find((child) => child.label === label),
- {
- children: testController.items,
- } as vscode.TestItem | undefined,
- );
- assert.ok(item);
- return item;
- });
+ const item = await waitFor(() =>
+ getTestItemByLabels(testController.items, ['test', 'progress.test.ts']),
+ );
const { promise, resolve } = Promise.withResolvers();
diff --git a/packages/vscode/tests/suite/runtimeList.test.ts b/packages/vscode/tests/suite/runtimeList.test.ts
new file mode 100644
index 000000000..fe0c609ac
--- /dev/null
+++ b/packages/vscode/tests/suite/runtimeList.test.ts
@@ -0,0 +1,161 @@
+import assert from 'node:assert';
+import * as vscode from 'vscode';
+import { getTestItemByLabels, toLabelTree, waitFor } from './helpers';
+
+suite('Runtime list suite', () => {
+ test('Extension should discover test cases from runtime', async () => {
+ // Check if the extension is activated
+ const extension = vscode.extensions.getExtension('rstack.rstest');
+ if (extension && !extension.isActive) {
+ await extension.activate();
+ }
+
+ // Get the rstest test controller that the extension should have created
+ const rstestInstance = extension?.exports;
+ const testController: vscode.TestController =
+ rstestInstance?.testController;
+
+ const config = vscode.workspace.getConfiguration('rstest');
+
+ await waitFor(() => {
+ const item = getTestItemByLabels(testController.items, [
+ 'test',
+ 'each.test.ts',
+ ]);
+ assert.deepStrictEqual(toLabelTree(item.children), [
+ {
+ children: [
+ {
+ label: 'case',
+ },
+ ],
+ label: 'suite',
+ },
+ {
+ label: 'unnamed test',
+ },
+ {
+ label: 'unnamed test',
+ },
+ ]);
+ });
+
+ // change config to runtime
+ await config.update('testCaseCollectMethod', 'runtime');
+ await waitFor(() => {
+ const item = getTestItemByLabels(testController.items, [
+ 'test',
+ 'each.test.ts',
+ ]);
+ assert.deepStrictEqual(toLabelTree(item.children), [
+ {
+ children: [
+ {
+ label: 'case',
+ },
+ ],
+ label: 'suite',
+ },
+ {
+ children: [
+ {
+ label: 'suite 1 case 1',
+ },
+ {
+ label: 'suite 1 case 2',
+ },
+ ],
+ label: 'suite 1',
+ },
+ {
+ children: [
+ {
+ label: 'suite 2 case 1',
+ },
+ {
+ label: 'suite 2 case 2',
+ },
+ ],
+ label: 'suite 2',
+ },
+ ]);
+ });
+
+ // restore config
+ await config.update('testCaseCollectMethod', undefined);
+ await waitFor(() => {
+ const item = getTestItemByLabels(testController.items, [
+ 'test',
+ 'each.test.ts',
+ ]);
+ assert.deepStrictEqual(toLabelTree(item.children), [
+ {
+ children: [
+ {
+ label: 'case',
+ },
+ ],
+ label: 'suite',
+ },
+ {
+ label: 'unnamed test',
+ },
+ {
+ label: 'unnamed test',
+ },
+ ]);
+ });
+
+ // test list should be updated after test run
+ rstestInstance.startTestRun(
+ new vscode.TestRunRequest(
+ undefined,
+ undefined,
+ rstestInstance.runProfile,
+ ),
+ new vscode.CancellationTokenSource().token,
+ false,
+ );
+ await waitFor(
+ () => {
+ const item = getTestItemByLabels(testController.items, [
+ 'test',
+ 'each.test.ts',
+ ]);
+ assert.deepStrictEqual(toLabelTree(item.children), [
+ {
+ children: [
+ {
+ label: 'case',
+ },
+ ],
+ label: 'suite',
+ },
+ {
+ children: [
+ {
+ label: 'suite 1 case 1',
+ },
+ {
+ label: 'suite 1 case 2',
+ },
+ ],
+ label: 'suite 1',
+ },
+ {
+ children: [
+ {
+ label: 'suite 2 case 1',
+ },
+ {
+ label: 'suite 2 case 2',
+ },
+ ],
+ label: 'suite 2',
+ },
+ ]);
+ },
+ { timeoutMs: 5000 },
+ );
+ });
+});
diff --git a/packages/vscode/tests/suite/workspace.test.ts b/packages/vscode/tests/suite/workspace.test.ts
index f1751d87f..c97fd7548 100644
--- a/packages/vscode/tests/suite/workspace.test.ts
+++ b/packages/vscode/tests/suite/workspace.test.ts
@@ -26,6 +26,7 @@ suite('Workspace discover suite', () => {
{
label: 'test',
children: [
+ { label: 'each.test.ts' },
{ label: 'foo.test.ts' },
{ label: 'index.test.ts' },
{ label: 'jsFile.spec.js' },
@@ -53,6 +54,7 @@ suite('Workspace discover suite', () => {
{
label: 'test',
children: [
+ { label: 'each.test.ts' },
{ label: 'foo.test.ts' },
{ label: 'index.test.ts' },
{ label: 'jsFile.spec.js' },
@@ -109,6 +111,7 @@ suite('Workspace discover suite', () => {
{
label: 'test',
children: [
+ { label: 'each.test.ts' },
{ label: 'foo.test.ts' },
{ label: 'index.test.ts' },
{ label: 'jsFile.spec.js' },
@@ -208,6 +211,7 @@ suite('Workspace discover suite', () => {
{
label: 'test',
children: [
+ { label: 'each.test.ts' },
{ label: 'foo.test.ts' },
{ label: 'index.test.ts' },
{ label: 'jsFile.spec.js' },
@@ -252,6 +256,7 @@ suite('Workspace discover suite', () => {
{
label: 'test',
children: [
+ { label: 'each.test.ts' },
{ label: 'foo.test.ts' },
{ label: 'index.test.ts' },
{ label: 'jsFile.spec.js' },