From 685a62c00697b8bf301fe6a9730309961f194719 Mon Sep 17 00:00:00 2001 From: mato533 Date: Tue, 24 Jun 2025 16:43:41 +0900 Subject: [PATCH 1/4] chore: update reload functionality - Change reloading to asynchronous parallel execution - Changed UI display to match initialization and display in TestExplorer. --- packages/vscode-wdio-test/src/manager.ts | 44 ++++++++++-------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/packages/vscode-wdio-test/src/manager.ts b/packages/vscode-wdio-test/src/manager.ts index 707451f..22ffca0 100644 --- a/packages/vscode-wdio-test/src/manager.ts +++ b/packages/vscode-wdio-test/src/manager.ts @@ -218,32 +218,26 @@ export class RepositoryManager extends MetadataRepository implements IRepository * Refresh WebdriverIO tests */ public async refreshTests(): Promise { - return vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: 'Reloading WebdriverIO tests...', - cancellable: false, - }, - async () => { - try { - if (!this._isInitialized) { - await this.initialize() - } - for (const repo of this._repos) { - // Clear existing tests - repo.clearTests() - // Discover tests again - await repo.discoverAllTests() - } - - vscode.window.showInformationMessage('WebdriverIO tests reloaded successfully') - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error) - log.error(`Failed to reload tests: ${errorMessage}`) - vscode.window.showErrorMessage(`Failed to reload WebdriverIO tests: ${errorMessage}`) - } + this.controller.items.replace([this._loadingTestItem]) + try { + if (!this._isInitialized) { + await this.initialize() } - ) + await Promise.all( + this.repos.map(async (repo) => { + repo.clearTests() + return await repo.discoverAllTests() + }) + ) + + this.registerToTestController() + await this.workerManager.reorganize(this.configManager.getWdioConfigPaths()) + } catch (error) { + this.controller.items.replace([]) + const errorMessage = error instanceof Error ? error.message : String(error) + log.error(`Failed to reload tests: ${errorMessage}`) + vscode.window.showErrorMessage(`Failed to reload WebdriverIO tests: ${errorMessage}`) + } } public async dispose() { From f07a3309f60d11497bfc02a054c4d758588cde7c Mon Sep 17 00:00:00 2001 From: mato533 Date: Wed, 25 Jun 2025 12:43:29 +0900 Subject: [PATCH 2/4] chore: improve processing logic about test data management --- packages/vscode-wdio-test/src/converter.ts | 51 +------ packages/vscode-wdio-test/src/manager.ts | 30 ++-- packages/vscode-wdio-test/src/repository.ts | 131 ++++++------------ .../vscode-wdio-test/tests/converter.test.ts | 70 +--------- .../vscode-wdio-test/tests/manager.test.ts | 57 +++----- .../vscode-wdio-test/tests/repository.test.ts | 109 +++++++-------- packages/vscode-wdio-types/src/test.ts | 16 +-- .../vscode-wdio-worker/tests/test.test.ts | 3 + vitest.config.ts | 14 +- 9 files changed, 153 insertions(+), 328 deletions(-) diff --git a/packages/vscode-wdio-test/src/converter.ts b/packages/vscode-wdio-test/src/converter.ts index 197894a..c6229b0 100644 --- a/packages/vscode-wdio-test/src/converter.ts +++ b/packages/vscode-wdio-test/src/converter.ts @@ -1,56 +1,9 @@ import path from 'node:path' import * as vscode from 'vscode' -import type { ReadSpecsResult } from '@vscode-wdio/types/server' -import type { TestData, SourceRange, VscodeTestData } from '@vscode-wdio/types/test' -/** - * Convert the parser's TestData to VSCode compatible TestData - * - * @param testCases Array of TestData from the parser - * @param document VSCode document for position conversion - * @returns Array of VscodeTestData - */ -export async function convertTestData(testData: ReadSpecsResult): Promise { - try { - const uri = convertPathToUri(testData.spec) - - return testData.tests.map((testCase) => _convertTestData(testCase, uri)) - } catch (error) { - throw new Error(`Failed to parse or adapt test cases: ${(error as Error).message}`) - } -} - -/** - * Convert a single TestData to use VSCode Range - * - * @param testCase TestData from the parser - * @param uri VSCode Uri for Spec file - * @returns VscodeTestData - */ -function _convertTestData(testCase: TestData, uri: vscode.Uri): VscodeTestData { - // Convert SourceRange to VSCode Range - const vsCodeRange = convertSourceRangeToVSCodeRange(testCase.range) - - // Convert children recursively - const vsCodeChildren = testCase.children.map((child) => _convertTestData(child, uri)) - - return { - type: testCase.type, - name: testCase.name, - uri, - range: vsCodeRange, - children: vsCodeChildren, - } -} +import type { SourceRange } from '@vscode-wdio/types/test' -/** - * Convert a SourceRange to a VSCode Range - * - * @param sourceRange SourceRange with offsets - * @param document VSCode document for position conversion - * @returns VSCode Range - */ -function convertSourceRangeToVSCodeRange(sourceRange: SourceRange): vscode.Range { +export function convertSourceRangeToVSCodeRange(sourceRange: SourceRange): vscode.Range { const start = new vscode.Position(sourceRange.start.line, sourceRange.start.column) const end = new vscode.Position(sourceRange.end.line, sourceRange.end.column) return new vscode.Range(start, end) diff --git a/packages/vscode-wdio-test/src/manager.ts b/packages/vscode-wdio-test/src/manager.ts index 22ffca0..44a648c 100644 --- a/packages/vscode-wdio-test/src/manager.ts +++ b/packages/vscode-wdio-test/src/manager.ts @@ -22,7 +22,7 @@ import type { IRepositoryManager, ITestRepository } from '@vscode-wdio/types/tes const LOADING_TEST_ITEM_ID = '_resolving' -export class RepositoryManager extends MetadataRepository implements IRepositoryManager { +export class RepositoryManager implements IRepositoryManager { private readonly _repos = new Set() private _loadingTestItem: vscode.TestItem private _workspaceTestItems: vscode.TestItem[] = [] @@ -33,9 +33,9 @@ export class RepositoryManager extends MetadataRepository implements IRepository constructor( public readonly controller: vscode.TestController, public readonly configManager: ExtensionConfigManager, - private readonly workerManager: IWorkerManager + private readonly workerManager: IWorkerManager, + private readonly _metadata = new MetadataRepository() ) { - super() this._loadingTestItem = this.controller.createTestItem(LOADING_TEST_ITEM_ID, 'Resolving WebdriverIO Tests...') this._loadingTestItem.sortText = '.0' // show at first line this._loadingTestItem.busy = true @@ -64,9 +64,16 @@ export class RepositoryManager extends MetadataRepository implements IRepository return Array.from(this._repos) } + public getMetadata(testItem: vscode.TestItem) { + return this._metadata.getMetadata(testItem) + } + + public getRepository(testItem: vscode.TestItem): ITestRepository { + return this._metadata.getRepository(testItem) + } + public async initialize() { const workspaces = this.configManager.workspaces - if (workspaces.length < 1) { log.info('No workspaces is detected.') return @@ -103,7 +110,7 @@ export class RepositoryManager extends MetadataRepository implements IRepository wdioConfigPath ) - const repo = this.getRepository(configTestItem) + const repo = this._metadata.getRepository(configTestItem) await repo.discoverAllTests() if (!this.configManager.isMultiWorkspace) { this.controller.items.add(configTestItem) @@ -123,7 +130,7 @@ export class RepositoryManager extends MetadataRepository implements IRepository continue } log.debug(`Remove the TestItem: ${config.id}`) - const targetRepo = this.getRepository(config) + const targetRepo = this._metadata.getRepository(config) targetRepo.dispose() this._repos.delete(targetRepo) @@ -161,7 +168,7 @@ export class RepositoryManager extends MetadataRepository implements IRepository workspaceFolder.name, workspaceFolder.uri ) - this.setMetadata(workspaceItem, { + this._metadata.setMetadata(workspaceItem, { uri: workspaceFolder.uri, isWorkspace: true, isConfigFile: false, @@ -198,7 +205,7 @@ export class RepositoryManager extends MetadataRepository implements IRepository configItem.description = relative(workspaceTestItem.uri!.fsPath, dirname(wdioConfigPath)) - this.setMetadata(configItem, { + this._metadata.setMetadata(configItem, { uri, isWorkspace: false, isConfigFile: true, @@ -225,7 +232,6 @@ export class RepositoryManager extends MetadataRepository implements IRepository } await Promise.all( this.repos.map(async (repo) => { - repo.clearTests() return await repo.discoverAllTests() }) ) @@ -241,11 +247,7 @@ export class RepositoryManager extends MetadataRepository implements IRepository } public async dispose() { - await Promise.all( - Array.from(this._repos).map(async (repo) => { - await repo.dispose() - }) - ) + await Promise.all(this.repos.map(async (repo) => repo.dispose())) this._repos.clear() this._workspaceTestItems = [] this._wdioConfigTestItems = [] diff --git a/packages/vscode-wdio-test/src/repository.ts b/packages/vscode-wdio-test/src/repository.ts index e570c86..cc88dd9 100644 --- a/packages/vscode-wdio-test/src/repository.ts +++ b/packages/vscode-wdio-test/src/repository.ts @@ -1,42 +1,33 @@ -import * as fs from 'node:fs/promises' import * as path from 'node:path' import { TEST_ID_SEPARATOR } from '@vscode-wdio/constants' import { log } from '@vscode-wdio/logger' import { getEnvOptions, normalizePath } from '@vscode-wdio/utils' +import * as vscode from 'vscode' -import { convertPathToUri, convertTestData } from './converter.js' +import { convertSourceRangeToVSCodeRange } from './converter.js' import { MetadataRepository } from './metadata.js' import { filterSpecsByPaths } from './utils.js' import type { IExtensionConfigManager } from '@vscode-wdio/types' import type { IWorkerManager, IWdioExtensionWorker } from '@vscode-wdio/types/server' -import type { VscodeTestData, ITestRepository } from '@vscode-wdio/types/test' -import type * as vscode from 'vscode' +import type { ITestRepository, TestData } from '@vscode-wdio/types/test' -class WorkerProxy extends MetadataRepository { +class WorkerMiddleware { private _worker: IWdioExtensionWorker | undefined constructor( - private workerManager: IWorkerManager, + private _workerManager: IWorkerManager, private readonly _wdioConfigPath: string - ) { - super() - } + ) {} async getWorker() { if (!this._worker) { - this._worker = await this.workerManager.getConnection(this._wdioConfigPath) - this.setListener() - } - return this._worker - } - - private setListener() { - if (this._worker) { + this._worker = await this._workerManager.getConnection(this._wdioConfigPath) this._worker.on('shutdown', () => { this._worker = undefined }) } + return this._worker } } @@ -44,7 +35,7 @@ class WorkerProxy extends MetadataRepository { * TestRepository class that manages all WebdriverIO tests at * the single WebdriverIO configuration file */ -export class TestRepository extends WorkerProxy implements ITestRepository { +export class TestRepository extends WorkerMiddleware implements ITestRepository { private _specPatterns: string[] = [] private _fileMap = new Map() private _framework: string | undefined = undefined @@ -55,11 +46,20 @@ export class TestRepository extends WorkerProxy implements ITestRepository { public readonly wdioConfigPath: string, private _wdioConfigTestItem: vscode.TestItem, workerManager: IWorkerManager, - private _workspaceFolder: vscode.WorkspaceFolder + private _workspaceFolder: vscode.WorkspaceFolder, + private readonly _metadata = new MetadataRepository() ) { super(workerManager, wdioConfigPath) } + public getMetadata(testItem: vscode.TestItem) { + return this._metadata.getMetadata(testItem) + } + + public getRepository(testItem: vscode.TestItem): ITestRepository { + return this._metadata.getRepository(testItem) + } + public get specPatterns() { return this._specPatterns } @@ -89,6 +89,10 @@ export class TestRepository extends WorkerProxy implements ITestRepository { if (!config) { return } + if (this._fileMap.size > 0) { + log.debug('Clearing all tests from repository before discover all tests') + this._fileMap.clear() + } this._framework = config.framework this._specPatterns = config.specPatterns @@ -149,33 +153,16 @@ export class TestRepository extends WorkerProxy implements ITestRepository { } log.debug(`Reloading ${specsToReload.length} spec files`) - - // Set busy state for affected test items - const affectedTestItems: vscode.TestItem[] = [] - for (const spec of specsToReload) { const testItem = this.getSpecByFilePath(spec) if (testItem) { - // Set busy state before removal testItem.busy = true - affectedTestItems.push(testItem) - - // Remove existing test items for this file - this.removeSpecFile(spec) + testItem.children.replace([]) } } // Register the updated spec files await this.resisterSpecs(specsToReload, false) - // Reset busy state for all affected items - affectedTestItems.forEach((item) => { - // Find the newly registered item with the same ID - const newItem = this._fileMap.get(item.id) - if (newItem) { - newItem.busy = false - } - }) - log.debug(`Successfully reloaded ${specsToReload.length} spec files`) } catch (error) { log.error(`Failed to reload spec files: ${this.wdioConfigPath}`) @@ -197,13 +184,14 @@ export class TestRepository extends WorkerProxy implements ITestRepository { private getTestFileId(wdioConfigTestItem: vscode.TestItem, testFilePath: string) { return [wdioConfigTestItem.id, testFilePath].join(TEST_ID_SEPARATOR) } + /** * Register spec files with the test controller * @param specs Paths to spec files - * @param clearExisting Whether to clear existing tests (default: true) + * @param replaceAllSpecFiles if all spec files to resister, this parameter must be true */ - private async resisterSpecs(specs: string[], clearExisting: boolean = true) { - if (clearExisting) { + private async resisterSpecs(specs: string[], replaceAllSpecFiles: boolean = true) { + if (replaceAllSpecFiles) { this._fileMap.clear() } log.debug(`Spec files registration is started for: ${specs.length} files.`) @@ -213,24 +201,24 @@ export class TestRepository extends WorkerProxy implements ITestRepository { specs, }) - const fileTestItems = ( + const specFileTestItems = ( await Promise.all( testData.map(async (test) => { try { // Create TestItem testFile by testFile const fileId = this.getTestFileId(this._wdioConfigTestItem, test.spec) - const testCases = await convertTestData(test) + const uri = vscode.Uri.file(test.spec) - const fileTestItem = this.resisterSpecFile(fileId, convertPathToUri(test.spec)) + const fileTestItem = this.resisterSpecFile(fileId, uri) - const testTreeCreator = (parentId: string, testCase: VscodeTestData) => { + const testTreeCreator = (parentId: string, testCase: TestData) => { const testCaseId = `${parentId}${TEST_ID_SEPARATOR}${testCase.name}` - const testCaseItem = this.controller.createTestItem(testCaseId, testCase.name, testCase.uri) + const testCaseItem = this.controller.createTestItem(testCaseId, testCase.name, uri) - this.setMetadata(testCaseItem, { - uri: testCase.uri, + this._metadata.setMetadata(testCaseItem, { + uri, isWorkspace: false, isConfigFile: false, isSpecFile: false, @@ -239,16 +227,14 @@ export class TestRepository extends WorkerProxy implements ITestRepository { type: testCase.type, }) - testCaseItem.range = testCase.range + testCaseItem.range = convertSourceRangeToVSCodeRange(testCase.range) for (const childTestCase of testCase.children) { testCaseItem.children.add(testTreeCreator(testCaseId, childTestCase)) } return testCaseItem } - - // Create TestItem testCase by testCase - for (const testCase of testCases) { + for (const testCase of test.tests) { fileTestItem.children.add(testTreeCreator(fileId, testCase)) } return fileTestItem @@ -260,21 +246,15 @@ export class TestRepository extends WorkerProxy implements ITestRepository { ) ).filter((item) => typeof item !== 'undefined') - if (clearExisting) { - // Replace all items - this._wdioConfigTestItem.children.replace(fileTestItems) + if (replaceAllSpecFiles) { + this._wdioConfigTestItem.children.replace(specFileTestItems) } else { - // Add new items while preserving existing ones - const currentItems = Array.from(this._wdioConfigTestItem.children).map(([_id, item]) => item) - - // Remove items with the same ID - const newItemIds = new Set() - fileTestItems.forEach((item) => newItemIds.add(item.id)) - - const filteredCurrentItems = currentItems.filter((item) => !newItemIds.has(item.id)) - - // Combine existing and new items - this._wdioConfigTestItem.children.replace([...filteredCurrentItems, ...fileTestItems]) + for (const specFileTestItem of specFileTestItems) { + if (this._wdioConfigTestItem.children.get(specFileTestItem.id)) { + this._wdioConfigTestItem.children.delete(specFileTestItem.id) + } + this._wdioConfigTestItem.children.add(specFileTestItem) + } } log.debug(`spec files registration is finished for: ${specs.length} files.`) } @@ -314,25 +294,6 @@ export class TestRepository extends WorkerProxy implements ITestRepository { return specs.flatMap((spec) => (Array.isArray(spec) ? spec.map((path) => path) : [spec])) } - /** - * Clear all tests from the repository - */ - public clearTests(): void { - log.debug('Clearing all tests from repository') - - // Clear internal maps - this._fileMap.clear() - } - - /** - * Read a spec file - * @param filePath Path to the spec file - * @returns File content - */ - protected async readSpecFile(filePath: string): Promise { - return await fs.readFile(filePath, { encoding: 'utf8' }) - } - /** * Register a spec file with the test controller * @param id Spec file ID @@ -344,7 +305,7 @@ export class TestRepository extends WorkerProxy implements ITestRepository { const fileTestItem = this.controller.createTestItem(id, path.basename(uri.fsPath), uri) fileTestItem.sortText = uri.fsPath - this.setMetadata(fileTestItem, { + this._metadata.setMetadata(fileTestItem, { uri, isWorkspace: false, isConfigFile: false, diff --git a/packages/vscode-wdio-test/tests/converter.test.ts b/packages/vscode-wdio-test/tests/converter.test.ts index 98f598e..3206ad9 100644 --- a/packages/vscode-wdio-test/tests/converter.test.ts +++ b/packages/vscode-wdio-test/tests/converter.test.ts @@ -3,81 +3,13 @@ import { join } from 'node:path' import { describe, it, expect, vi } from 'vitest' import * as vscode from 'vscode' -import { convertPathToUri, convertTestData, isCucumberFeatureFile } from '../src/converter.js' -import type { ReadSpecsResult } from '@vscode-wdio/types/server' +import { convertPathToUri, isCucumberFeatureFile } from '../src/converter.js' // Mock dependencies vi.mock('vscode', () => import('../../../tests/__mocks__/vscode.cjs')) vi.mock('@vscode-wdio/logger', () => import('../../../tests/__mocks__/logger.js')) describe('Converter', () => { - describe('convertTestData', () => { - it('should convert parser test data to VSCode test data format', async () => { - // Mock test data - const specFilePath = join(process.cwd(), 'path', 'to', 'spec.js') - const mockTestData: ReadSpecsResult = { - spec: specFilePath, - tests: [ - { - type: 'describe', - name: 'Root describe', - range: { - start: { line: 0, column: 0 }, - end: { line: 10, column: 1 }, - }, - children: [ - { - type: 'it', - name: 'Child test', - range: { - start: { line: 2, column: 4 }, - end: { line: 4, column: 5 }, - }, - children: [], - }, - ], - }, - ], - } - - // Expected result - const result = await convertTestData(mockTestData) - expect(result.length).toBe(1) - expect(result[0].type).toBe('describe') - expect(result[0].name).toBe('Root describe') - expect(result[0].uri.fsPath).toBe(vscode.Uri.file(specFilePath).fsPath) - expect(result[0].range).toBeInstanceOf(vscode.Range) - expect(result[0].range.start.line).toBe(0) - expect(result[0].range.start.character).toBe(0) - expect(result[0].range.end.line).toBe(10) - expect(result[0].range.end.character).toBe(1) - - // Check children - expect(result[0].children.length).toBe(1) - expect(result[0].children[0].type).toBe('it') - expect(result[0].children[0].name).toBe('Child test') - expect(result[0].children[0].range.start.line).toBe(2) - expect(result[0].children[0].range.start.character).toBe(4) - }) - - it('should throw an error when parsing fails', async () => { - // Setup test to fail - const mockTestData = { - spec: '/path/to/spec.js', - tests: null, - } as any - - // Test error handling - try { - await convertTestData(mockTestData) - expect.fail('Expected convertTestData to throw an error') - } catch (error) { - expect(error).toBeInstanceOf(Error) - expect((error as Error).message).to.include('Failed to parse or adapt test cases') - } - }) - }) - describe('convertPathToUri', () => { it('should convert file path to VSCode URI', () => { const filePath = join(process.cwd(), 'path', 'to', 'spec.js') diff --git a/packages/vscode-wdio-test/tests/manager.test.ts b/packages/vscode-wdio-test/tests/manager.test.ts index 1d15a1b..a8a52e0 100644 --- a/packages/vscode-wdio-test/tests/manager.test.ts +++ b/packages/vscode-wdio-test/tests/manager.test.ts @@ -36,11 +36,10 @@ vi.mock('../src/utils.js', () => { }) describe('RepositoryManager', () => { - let fakeWorkspaceFolder: vscode.WorkspaceFolder - let fakeWorkspaces: WorkspaceData[] + let mockWorkspaceFolder: vscode.WorkspaceFolder + let mockWorkspaces: WorkspaceData[] let workerManager: WdioWorkerManager - let clearTestsStub: ReturnType - let discoverAllTestsStub: ReturnType + let mockDiscoverAllTests: ReturnType let controller: vscode.TestController const configManager = new ExtensionConfigManager() @@ -62,16 +61,16 @@ describe('RepositoryManager', () => { } as unknown as vscode.TestController // Setup fake workspace - fakeWorkspaceFolder = { + mockWorkspaceFolder = { uri: vscode.Uri.file(workspacePath), name: 'fake-workspace', index: 0, } // Setup fake config - fakeWorkspaces = [ + mockWorkspaces = [ { - workspaceFolder: fakeWorkspaceFolder, + workspaceFolder: mockWorkspaceFolder, wdioConfigFiles: [configPath], }, ] @@ -82,15 +81,11 @@ describe('RepositoryManager', () => { on: vi.fn(), } as unknown as IWdioExtensionWorker) - // Stub configManager - vi.spyOn(configManager, 'workspaces', 'get').mockReturnValue(fakeWorkspaces) + vi.spyOn(configManager, 'workspaces', 'get').mockReturnValue(mockWorkspaces) - // Stub TestRepository - discoverAllTestsStub = vi.fn().mockResolvedValue(undefined) - clearTestsStub = vi.fn() + mockDiscoverAllTests = vi.fn().mockResolvedValue(undefined) - vi.spyOn(TestRepository.prototype, 'discoverAllTests').mockImplementation(discoverAllTestsStub) - vi.spyOn(TestRepository.prototype, 'clearTests').mockImplementation(clearTestsStub) + vi.spyOn(TestRepository.prototype, 'discoverAllTests').mockImplementation(mockDiscoverAllTests) repositoryManager = new RepositoryManager(controller, configManager, workerManager) }) @@ -101,7 +96,6 @@ describe('RepositoryManager', () => { describe('initialize', () => { it('should success to initialize when no workspace', async () => { - // Override the configManager stub to return an empty array vi.spyOn(configManager, 'workspaces', 'get').mockReturnValue([]) await repositoryManager.initialize() @@ -119,12 +113,12 @@ describe('RepositoryManager', () => { describe('registerToTestController', () => { it('should delete the loading test item', () => { - const deleteStub = vi.fn() - vi.spyOn(controller.items, 'delete').mockImplementation(deleteStub) + const mockDelete = vi.fn() + vi.spyOn(controller.items, 'delete').mockImplementation(mockDelete) repositoryManager.registerToTestController() - expect(deleteStub).toHaveBeenCalledWith('_resolving') + expect(mockDelete).toHaveBeenCalledWith('_resolving') }) it('should register wdio config test items directly for single workspace', async () => { @@ -148,9 +142,9 @@ describe('RepositoryManager', () => { index: 1, } - fakeWorkspaces = [ + mockWorkspaces = [ { - workspaceFolder: fakeWorkspaceFolder, + workspaceFolder: mockWorkspaceFolder, wdioConfigFiles: [configPath], }, { @@ -158,7 +152,7 @@ describe('RepositoryManager', () => { wdioConfigFiles: [anotherConfigPath], }, ] - vi.spyOn(configManager, 'workspaces', 'get').mockReturnValue(fakeWorkspaces) + vi.spyOn(configManager, 'workspaces', 'get').mockReturnValue(mockWorkspaces) vi.spyOn(workerManager, 'getConnection').mockResolvedValue({ on: vi.fn(), } as unknown as IWdioExtensionWorker) @@ -188,29 +182,14 @@ describe('RepositoryManager', () => { }) }) - describe('refreshTests', () => { - beforeEach(async () => { - await repositoryManager.initialize() - }) - - it('should handle errors during refresh', async () => { - repositoryManager.refreshTests() - // @ts-ignore the target function of this test is callback of vscode api - await vi.mocked(vscode.window.withProgress).mock.calls[0][1]() - - expect(clearTestsStub).toHaveBeenCalledTimes(1) - expect(discoverAllTestsStub).toHaveBeenCalledTimes(1) - }) - }) - describe('dispose', () => { it('should dispose the test controller', () => { - const disposeStub = vi.fn() - vi.spyOn(repositoryManager, 'dispose').mockImplementation(disposeStub) + const mockDispose = vi.fn() + vi.spyOn(repositoryManager, 'dispose').mockImplementation(mockDispose) repositoryManager.dispose() - expect(disposeStub).toHaveBeenCalled() + expect(mockDispose).toHaveBeenCalled() }) }) }) diff --git a/packages/vscode-wdio-test/tests/repository.test.ts b/packages/vscode-wdio-test/tests/repository.test.ts index b6fe347..8e7749a 100644 --- a/packages/vscode-wdio-test/tests/repository.test.ts +++ b/packages/vscode-wdio-test/tests/repository.test.ts @@ -7,7 +7,12 @@ import * as vscode from 'vscode' import { mockCreateTestItem, MockTestItemCollection } from '../../../tests/utils.js' import { TestRepository } from '../src/repository.js' -import type { IExtensionConfigManager } from '@vscode-wdio/types' +import type { + IExtensionConfigManager, + IMetadataRepository, + ITestRepository, + TestItemMetadata, +} from '@vscode-wdio/types' import type { IWorkerManager, WdioConfig, IWdioExtensionWorker } from '@vscode-wdio/types/server' // Mock dependencies @@ -24,6 +29,20 @@ vi.mock('@vscode-wdio/utils', async (importActual) => { } }) +class MockMetadataRepository implements IMetadataRepository { + _repo = new WeakMap() + setMetadata(testItem: vscode.TestItem, metadata: TestItemMetadata): void { + this._repo.set(testItem, metadata) + } + getMetadata(testItem: vscode.TestItem): TestItemMetadata { + return this._repo.get(testItem)! + } + getRepository(testItem: vscode.TestItem): ITestRepository { + this._repo.get(testItem)! + return this._repo.get(testItem)!.repository! + } +} + describe('TestRepository', () => { const mockWorkspaceUri = vscode.Uri.file(join(process.cwd(), 'mock', 'workspace')) const mockSpecPath = join(process.cwd(), 'mock', 'workspace', 'e2e', 'test1.spec.js') @@ -38,9 +57,8 @@ describe('TestRepository', () => { let wdioConfigTestItem: vscode.TestItem let testRepository: TestRepository let mockWorker: IWdioExtensionWorker - let readFile: ReturnType - let readSpecsStub: ReturnType - let runProfileDisposeStub: ReturnType + let mockReadSpecs: ReturnType + let mockRunProfileDispose: ReturnType let workerManager: IWorkerManager let mockWorkspaceFolder: vscode.WorkspaceFolder @@ -52,16 +70,16 @@ describe('TestRepository', () => { items: new MockTestItemCollection(), createTestItem: mockCreateTestItem, } as unknown as vscode.TestController - // Setup WdioConfigTestItem mock + wdioConfigTestItem = testController.createTestItem( `workspace:${mockWorkspaceUri.fsPath}${TEST_ID_SEPARATOR}config:${mockWdioConfigUri.fsPath}`, mockWdioConfigUri.fsPath, mockWdioConfigUri ) - runProfileDisposeStub = vi.fn() + mockRunProfileDispose = vi.fn() // Setup worker mock - readSpecsStub = vi.fn().mockResolvedValue([ + mockReadSpecs = vi.fn().mockResolvedValue([ { spec: mockSpecPath, data: 'test content 1' }, { spec: mockSpecPath2, data: 'test content 2' }, ]) @@ -77,32 +95,16 @@ describe('TestRepository', () => { framework: 'mocha', specs: [mockSpecPath, mockSpecPath2], }), - readSpecs: readSpecsStub, + readSpecs: mockReadSpecs, }, } as unknown as IWdioExtensionWorker - readFile = vi.fn() - class MockTestRepository extends TestRepository { - protected override readSpecFile() { - return readFile() - } - } - workerManager = { getConnection: vi.fn(() => mockWorker), } as unknown as IWorkerManager - // Create repository with mocked dependencies - testRepository = new MockTestRepository( - mockConfigManager, - testController, - mockWdioConfigPath, - wdioConfigTestItem, - workerManager, - mockWorkspaceFolder - ) - - testRepository.setMetadata(wdioConfigTestItem, { + const mockMetaRepo = new MockMetadataRepository() + mockMetaRepo.setMetadata(wdioConfigTestItem, { uri: mockWdioConfigUri, isWorkspace: false, isConfigFile: true, @@ -111,10 +113,21 @@ describe('TestRepository', () => { repository: {} as any, runProfiles: [ { - dispose: runProfileDisposeStub, + dispose: mockRunProfileDispose, } as unknown as vscode.TestRunProfile, ], }) + + // Create repository with mocked dependencies + testRepository = new TestRepository( + mockConfigManager, + testController, + mockWdioConfigPath, + wdioConfigTestItem, + workerManager, + mockWorkspaceFolder, + mockMetaRepo + ) }) afterEach(() => { @@ -138,18 +151,7 @@ describe('TestRepository', () => { testRepository.dispose() // Verify - expect(runProfileDisposeStub).toHaveBeenCalled() - expect(spyOnFileMapClear).toHaveBeenCalled() - }) - - it('should clear all tests from repository', () => { - // Setup spies - const spyOnFileMapClear = vi.spyOn((testRepository as any)._fileMap, 'clear') - - // Execute - testRepository.clearTests() - - // Verify + expect(mockRunProfileDispose).toHaveBeenCalled() expect(spyOnFileMapClear).toHaveBeenCalled() }) @@ -169,8 +171,7 @@ describe('TestRepository', () => { }) }) - // Group 2: Test Discovery - describe('Test Discovery', () => { + describe('discoverAllTests', () => { it('should discover and register test specs from configuration', async () => { // Execute await testRepository.discoverAllTests() @@ -232,8 +233,7 @@ describe('TestRepository', () => { }) }) - // Group 3: File Reloading - describe('File Reloading', () => { + describe('reloadSpecFiles', () => { beforeEach(() => { // Setup file map with mock spec files const fileId1 = [wdioConfigTestItem.id, mockSpecPath].join(TEST_ID_SEPARATOR) @@ -243,26 +243,20 @@ describe('TestRepository', () => { id: fileId1, busy: false, children: { - forEach: vi.fn().mockImplementation((callback) => { - callback({ id: `${fileId1}${TEST_ID_SEPARATOR}suite1` }) - }), + replace: vi.fn(), }, }) ;(testRepository as any)._fileMap.set(fileId2, { id: fileId2, busy: false, children: { - forEach: vi.fn().mockImplementation((callback) => { - callback({ id: `${fileId2}${TEST_ID_SEPARATOR}suite1` }) - }), + replace: vi.fn(), }, }) }) it('should reload specific spec files', async () => { - // Setup - const removeSpecFileSpy = vi.spyOn(testRepository, 'removeSpecFile') - + const orgItem = testRepository.getSpecByFilePath(mockSpecPath) // Execute await testRepository.reloadSpecFiles([mockSpecPath]) @@ -271,11 +265,11 @@ describe('TestRepository', () => { env: { paths: [], override: false }, configFilePath: mockWdioConfigPath, }) - expect(readSpecsStub).toHaveBeenCalledWith({ + expect(mockReadSpecs).toHaveBeenCalledWith({ env: { paths: [], override: false }, specs: [mockSpecPath], }) - expect(removeSpecFileSpy).toHaveBeenCalledWith(mockSpecPath) + expect(testRepository.getSpecByFilePath(mockSpecPath)).not.toBe(orgItem) expect(log.debug).toHaveBeenCalledWith('Reloading 1 spec files') expect(log.debug).toHaveBeenCalledWith('Successfully reloaded 1 spec files') }) @@ -287,9 +281,7 @@ describe('TestRepository', () => { id: fileId, busy: false, children: { - forEach: vi.fn().mockImplementation((callback) => { - callback({ id: `${fileId}${TEST_ID_SEPARATOR}suite1` }) - }), + replace: vi.fn(), }, } as unknown as vscode.TestItem @@ -297,7 +289,6 @@ describe('TestRepository', () => { // Execute await testRepository.reloadSpecFiles([mockSpecPath]) - // Verify - busy state should have been set initially and then reset expect((testRepository as any)._fileMap.get(fileId).busy).toBe(false) }) @@ -346,7 +337,7 @@ describe('TestRepository', () => { await testRepository.reloadSpecFiles([]) // Verify - expect(readSpecsStub).toHaveBeenCalledWith({ + expect(mockReadSpecs).toHaveBeenCalledWith({ env: { paths: [], override: false }, specs: [mockSpecPath, mockSpecPath2], }) diff --git a/packages/vscode-wdio-types/src/test.ts b/packages/vscode-wdio-types/src/test.ts index a252d69..7b560b0 100644 --- a/packages/vscode-wdio-types/src/test.ts +++ b/packages/vscode-wdio-types/src/test.ts @@ -2,7 +2,7 @@ import type * as vscode from 'vscode' import type { IExtensionConfigManager } from './config.js' import type { IWdioExtensionWorker } from './server.js' -export interface IRepositoryManager extends vscode.Disposable { +export interface IRepositoryManager extends vscode.Disposable, IMetadataRepositoryReader { readonly controller: vscode.TestController readonly configManager: IExtensionConfigManager readonly repos: ITestRepository[] @@ -14,7 +14,7 @@ export interface IRepositoryManager extends vscode.Disposable { refreshTests(): Promise } -export interface ITestRepository extends IMetadataRepository, vscode.Disposable { +export interface ITestRepository extends vscode.Disposable, IMetadataRepositoryReader { readonly controller: vscode.TestController readonly wdioConfigPath: string specPatterns: string[] @@ -23,7 +23,6 @@ export interface ITestRepository extends IMetadataRepository, vscode.Disposable discoverAllTests(): Promise reloadSpecFiles(filePaths?: string[]): Promise removeSpecFile(specPath: string): void - clearTests(): void getSpecByFilePath(specPath: string): vscode.TestItem | undefined } @@ -75,12 +74,6 @@ export interface TestData { metadata?: Record } -export type VscodeTestData = Omit & { - uri: vscode.Uri - range: vscode.Range - children: VscodeTestData[] -} - export type TestItemMetadata = { uri: vscode.Uri isWorkspace: boolean @@ -95,8 +88,11 @@ export type TestItemMetadata = { export type TestItemMetadataWithRepository = Omit & Required> -export interface IMetadataRepository { +export interface IMetadataRepositoryReader { getMetadata(testItem: vscode.TestItem): TestItemMetadata getRepository(testItem: vscode.TestItem): ITestRepository +} + +export interface IMetadataRepository extends IMetadataRepositoryReader { setMetadata(testItem: vscode.TestItem, metadata: TestItemMetadata): void } diff --git a/packages/vscode-wdio-worker/tests/test.test.ts b/packages/vscode-wdio-worker/tests/test.test.ts index 6a2f2bd..fca7941 100644 --- a/packages/vscode-wdio-worker/tests/test.test.ts +++ b/packages/vscode-wdio-worker/tests/test.test.ts @@ -46,6 +46,7 @@ describe('runTest', () => { configPath: mockConfigFile, specs: ['test.spec.js'], grep: 'test pattern', + env: { paths: [], override: false }, } // Mock temporary directories and files @@ -141,6 +142,7 @@ describe('runTest', () => { // Arrange const optionsNoSpecs = { configPath: '/path/to/wdio.conf.js', + env: { paths: [], override: false }, } // Act @@ -155,6 +157,7 @@ describe('runTest', () => { const optionsNoGrep = { configPath: '/path/to/wdio.conf.js', specs: ['test.spec.js'], + env: { paths: [], override: false }, } // Act diff --git a/vitest.config.ts b/vitest.config.ts index 15d217e..b355eb7 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,13 +1,21 @@ -import { defineConfig } from 'vitest/config' +import { defineConfig, defaultExclude } from 'vitest/config' export default defineConfig({ + // https://github.com/vitest-dev/vitest/discussions/6662 + server: { + watch: { + ignored: ['**/dist/**'], + }, + }, + test: { + projects: ['packages/*'], dangerouslyIgnoreUnhandledErrors: true, include: ['packages/**/*.test.ts'], /** * not to ESM ported packages */ - exclude: ['dist', 'out', 'coverage', '.idea', '.git', '.vscode-test', '.cache', '**/node_modules/**'], + exclude: [...defaultExclude, '**/coverage/**', '.vscode-test'], env: { WDIO_UNIT_TESTING: '1', }, @@ -17,7 +25,7 @@ export default defineConfig({ include: ['packages/*/src/**'], provider: 'v8', reportOnFailure: true, - exclude: ['**/node_modules/**', 'packages/wdio-vscode-types/src/**'], + exclude: [...defaultExclude, 'packages/wdio-vscode-types/src/**'], watermarks: { statements: [85, 90], functions: [83, 88], From 47644734f79f939b5f754c52d4ee310584b91843 Mon Sep 17 00:00:00 2001 From: mato533 Date: Thu, 26 Jun 2025 15:49:49 +0900 Subject: [PATCH 3/4] chore: refactoring test package and its unit tests --- package.json | 1 + packages/vscode-wdio-test/src/index.ts | 2 + packages/vscode-wdio-test/src/manager.ts | 24 ++--- packages/vscode-wdio-test/src/metadata.ts | 59 ++++++++++++- packages/vscode-wdio-test/src/repository.ts | 47 ++-------- packages/vscode-wdio-test/src/utils.ts | 31 ++++++- .../vscode-wdio-test/tests/converter.test.ts | 61 ++++++++----- .../vscode-wdio-test/tests/manager.test.ts | 71 ++++++++++++++- .../vscode-wdio-test/tests/metadata.test.ts | 88 +++++++++++++++++++ .../vscode-wdio-test/tests/repository.test.ts | 38 ++++---- packages/vscode-wdio-test/tests/utils.test.ts | 85 ++++++++++++++++++ .../vscode-wdio-test/tests/watcher.test.ts | 18 ++++ 12 files changed, 418 insertions(+), 107 deletions(-) create mode 100644 packages/vscode-wdio-test/tests/metadata.test.ts diff --git a/package.json b/package.json index 9537a78..66ec865 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "test:e2e": "pnpm --filter @vscode-wdio/e2e run test:e2e", "test:smoke": "pnpm --filter @vscode-wdio/e2e run test:smoke", "coverage": "vitest --run --coverage", + "coverage:ui": "pnpx sirv-cli ./coverage --single", "graph": "pnpm run build --graph assets/build.png", "version": "pnpm --filter @vscode-wdio/release run changelog && git add CHANGELOG.md", "postversion": "git show", diff --git a/packages/vscode-wdio-test/src/index.ts b/packages/vscode-wdio-test/src/index.ts index 7501a69..b9ef4f9 100644 --- a/packages/vscode-wdio-test/src/index.ts +++ b/packages/vscode-wdio-test/src/index.ts @@ -1,3 +1,5 @@ +/* c8 ignore start */ export { RepositoryManager } from './manager.js' export { TestReporter } from './reporter.js' export { TestfileWatcher } from './watcher.js' +/* c8 ignore stop */ diff --git a/packages/vscode-wdio-test/src/manager.ts b/packages/vscode-wdio-test/src/manager.ts index 44a648c..fb70230 100644 --- a/packages/vscode-wdio-test/src/manager.ts +++ b/packages/vscode-wdio-test/src/manager.ts @@ -168,13 +168,7 @@ export class RepositoryManager implements IRepositoryManager { workspaceFolder.name, workspaceFolder.uri ) - this._metadata.setMetadata(workspaceItem, { - uri: workspaceFolder.uri, - isWorkspace: true, - isConfigFile: false, - isSpecFile: false, - isTestcase: false, - }) + this._metadata.createWorkspaceMetadata(workspaceItem, { uri: workspaceFolder.uri }) return workspaceItem } @@ -193,7 +187,7 @@ export class RepositoryManager implements IRepositoryManager { workspaceTestItem.children.add(configItem) this._wdioConfigTestItems.push(configItem) - const repo = new TestRepository( + const repository = new TestRepository( this.configManager, this.controller, wdioConfigPath, @@ -201,19 +195,13 @@ export class RepositoryManager implements IRepositoryManager { this.workerManager, workspaceFolder ) - this._repos.add(repo) + this._repos.add(repository) configItem.description = relative(workspaceTestItem.uri!.fsPath, dirname(wdioConfigPath)) - this._metadata.setMetadata(configItem, { - uri, - isWorkspace: false, - isConfigFile: true, - isSpecFile: false, - isTestcase: false, - repository: repo, - runProfiles: createRunProfile.call(this, configItem, !this.isCreatedDefaultProfile), - }) + const runProfiles = createRunProfile.call(this, configItem, !this.isCreatedDefaultProfile) + + this._metadata.createWdioConfigFileMetadata(configItem, { uri, repository, runProfiles }) return configItem } diff --git a/packages/vscode-wdio-test/src/metadata.ts b/packages/vscode-wdio-test/src/metadata.ts index 2c597b6..701f84a 100644 --- a/packages/vscode-wdio-test/src/metadata.ts +++ b/packages/vscode-wdio-test/src/metadata.ts @@ -1,7 +1,7 @@ -import type { IMetadataRepository, TestItemMetadata } from '@vscode-wdio/types/test' +import type { ITestRepository, TestItemMetadata, TestType } from '@vscode-wdio/types/test' import type * as vscode from 'vscode' -export class MetadataRepository implements IMetadataRepository { +export class MetadataRepository { private static testMetadataRepository = new WeakMap() public getMetadata(testItem: vscode.TestItem) { const metadata = MetadataRepository.testMetadataRepository.get(testItem) @@ -19,7 +19,60 @@ export class MetadataRepository implements IMetadataRepository { return metadata.repository } - public setMetadata(testItem: vscode.TestItem, metadata: TestItemMetadata) { + protected setMetadata(testItem: vscode.TestItem, metadata: TestItemMetadata) { MetadataRepository.testMetadataRepository.set(testItem, metadata) } + + public createTestMetadata( + testItem: vscode.TestItem, + options: { uri: vscode.Uri; repository: ITestRepository; testType: TestType } + ) { + this.setMetadata(testItem, { + uri: options.uri, + isWorkspace: false, + isConfigFile: false, + isSpecFile: false, + isTestcase: true, + repository: options.repository, + type: options.testType, + }) + } + + public createSpecFileMetadata( + testItem: vscode.TestItem, + options: { repository: ITestRepository; uri: vscode.Uri } + ) { + this.setMetadata(testItem, { + uri: options.uri, + isWorkspace: false, + isConfigFile: false, + isSpecFile: true, + isTestcase: false, + repository: options.repository, + }) + } + public createWdioConfigFileMetadata( + testItem: vscode.TestItem, + options: { repository: ITestRepository; uri: vscode.Uri; runProfiles?: vscode.TestRunProfile[] } + ) { + this.setMetadata(testItem, { + uri: options.uri, + isWorkspace: false, + isConfigFile: true, + isSpecFile: false, + isTestcase: false, + repository: options.repository, + runProfiles: options.runProfiles, + }) + } + + public createWorkspaceMetadata(testItem: vscode.TestItem, options: { uri: vscode.Uri }) { + this.setMetadata(testItem, { + uri: options.uri, + isWorkspace: true, + isConfigFile: false, + isSpecFile: false, + isTestcase: false, + }) + } } diff --git a/packages/vscode-wdio-test/src/repository.ts b/packages/vscode-wdio-test/src/repository.ts index cc88dd9..f9b0eba 100644 --- a/packages/vscode-wdio-test/src/repository.ts +++ b/packages/vscode-wdio-test/src/repository.ts @@ -5,13 +5,10 @@ import { log } from '@vscode-wdio/logger' import { getEnvOptions, normalizePath } from '@vscode-wdio/utils' import * as vscode from 'vscode' -import { convertSourceRangeToVSCodeRange } from './converter.js' import { MetadataRepository } from './metadata.js' -import { filterSpecsByPaths } from './utils.js' -import type { IExtensionConfigManager } from '@vscode-wdio/types' +import { filterSpecsByPaths, testTreeCreator } from './utils.js' -import type { IWorkerManager, IWdioExtensionWorker } from '@vscode-wdio/types/server' -import type { ITestRepository, TestData } from '@vscode-wdio/types/test' +import type { IExtensionConfigManager, IWorkerManager, IWdioExtensionWorker, ITestRepository } from '@vscode-wdio/types' class WorkerMiddleware { private _worker: IWdioExtensionWorker | undefined @@ -208,34 +205,13 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository // Create TestItem testFile by testFile const fileId = this.getTestFileId(this._wdioConfigTestItem, test.spec) - const uri = vscode.Uri.file(test.spec) + const specFileUri = vscode.Uri.file(test.spec) - const fileTestItem = this.resisterSpecFile(fileId, uri) - - const testTreeCreator = (parentId: string, testCase: TestData) => { - const testCaseId = `${parentId}${TEST_ID_SEPARATOR}${testCase.name}` - - const testCaseItem = this.controller.createTestItem(testCaseId, testCase.name, uri) - - this._metadata.setMetadata(testCaseItem, { - uri, - isWorkspace: false, - isConfigFile: false, - isSpecFile: false, - isTestcase: true, - repository: this, - type: testCase.type, - }) - - testCaseItem.range = convertSourceRangeToVSCodeRange(testCase.range) - - for (const childTestCase of testCase.children) { - testCaseItem.children.add(testTreeCreator(testCaseId, childTestCase)) - } - return testCaseItem - } + const fileTestItem = this.resisterSpecFile(fileId, specFileUri) for (const testCase of test.tests) { - fileTestItem.children.add(testTreeCreator(fileId, testCase)) + fileTestItem.children.add( + testTreeCreator(this, this._metadata, fileId, testCase, specFileUri) + ) } return fileTestItem } catch (error) { @@ -305,14 +281,7 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository const fileTestItem = this.controller.createTestItem(id, path.basename(uri.fsPath), uri) fileTestItem.sortText = uri.fsPath - this._metadata.setMetadata(fileTestItem, { - uri, - isWorkspace: false, - isConfigFile: false, - isSpecFile: true, - isTestcase: false, - repository: this, - }) + this._metadata.createSpecFileMetadata(fileTestItem, { uri, repository: this }) this._fileMap.set(id, fileTestItem) return fileTestItem } diff --git a/packages/vscode-wdio-test/src/utils.ts b/packages/vscode-wdio-test/src/utils.ts index 9d4816c..a5dc29a 100644 --- a/packages/vscode-wdio-test/src/utils.ts +++ b/packages/vscode-wdio-test/src/utils.ts @@ -1,10 +1,14 @@ import * as path from 'node:path' +import { TEST_ID_SEPARATOR } from '@vscode-wdio/constants' import * as vscode from 'vscode' +import { convertSourceRangeToVSCodeRange } from './converter.js' import { createHandler } from './runHandler.js' -import type { IExtensionConfigManager } from '@vscode-wdio/types/config' + +import type { IExtensionConfigManager, ITestRepository, TestData } from '@vscode-wdio/types' import type { RepositoryManager } from './manager.js' +import type { MetadataRepository } from './metadata.js' /** * Filter spec files by paths @@ -86,3 +90,28 @@ function getWorkspace(uri: vscode.Uri) { } return workspace } + +export function testTreeCreator( + repository: ITestRepository, + metadata: MetadataRepository, + parentId: string, + testCase: TestData, + uri: vscode.Uri +) { + const testCaseId = `${parentId}${TEST_ID_SEPARATOR}${testCase.name}` + + const testCaseItem = repository.controller.createTestItem(testCaseId, testCase.name, uri) + + metadata.createTestMetadata(testCaseItem, { + uri, + repository, + testType: testCase.type, + }) + + testCaseItem.range = convertSourceRangeToVSCodeRange(testCase.range) + + for (const childTestCase of testCase.children) { + testCaseItem.children.add(testTreeCreator(repository, metadata, testCaseId, childTestCase, uri)) + } + return testCaseItem +} diff --git a/packages/vscode-wdio-test/tests/converter.test.ts b/packages/vscode-wdio-test/tests/converter.test.ts index 3206ad9..7ae0109 100644 --- a/packages/vscode-wdio-test/tests/converter.test.ts +++ b/packages/vscode-wdio-test/tests/converter.test.ts @@ -3,35 +3,52 @@ import { join } from 'node:path' import { describe, it, expect, vi } from 'vitest' import * as vscode from 'vscode' -import { convertPathToUri, isCucumberFeatureFile } from '../src/converter.js' +import { convertPathToUri, convertSourceRangeToVSCodeRange, isCucumberFeatureFile } from '../src/converter.js' // Mock dependencies vi.mock('vscode', () => import('../../../tests/__mocks__/vscode.cjs')) vi.mock('@vscode-wdio/logger', () => import('../../../tests/__mocks__/logger.js')) -describe('Converter', () => { - describe('convertPathToUri', () => { - it('should convert file path to VSCode URI', () => { - const filePath = join(process.cwd(), 'path', 'to', 'spec.js') - const result = convertPathToUri(filePath) +describe('convertSourceRangeToVSCodeRange', () => { + it('should convert range to VSCode.Range', () => { + const sourceRange = { + start: { + line: 1, + column: 1, + }, + end: { + line: 10, + column: 10, + }, + } + const result = convertSourceRangeToVSCodeRange(sourceRange) - expect(result.scheme).toEqual('file') - expect(result.fsPath).toEqual(vscode.Uri.file(filePath).fsPath) - }) + expect(result.start.line).toEqual(1) + expect(result.end.line).toEqual(10) + }) +}) + +describe('convertPathToUri', () => { + it('should convert file path to VSCode URI', () => { + const filePath = join(process.cwd(), 'path', 'to', 'spec.js') + const result = convertPathToUri(filePath) + + expect(result.scheme).toEqual('file') + expect(result.fsPath).toEqual(vscode.Uri.file(filePath).fsPath) + }) +}) + +describe('isCucumberFeatureFile', () => { + it('should return true for .feature files', () => { + expect(isCucumberFeatureFile('/path/to/test.feature')).toBe(true) + expect(isCucumberFeatureFile('/path/to/TEST.FEATURE')).toBe(true) + expect(isCucumberFeatureFile('test.feature')).toBe(true) }) - describe('isCucumberFeatureFile', () => { - it('should return true for .feature files', () => { - expect(isCucumberFeatureFile('/path/to/test.feature')).toBe(true) - expect(isCucumberFeatureFile('/path/to/TEST.FEATURE')).toBe(true) - expect(isCucumberFeatureFile('test.feature')).toBe(true) - }) - - it('should return false for non-feature files', () => { - expect(isCucumberFeatureFile('/path/to/test.js')).toBe(false) - expect(isCucumberFeatureFile('/path/to/test.ts')).toBe(false) - expect(isCucumberFeatureFile('/path/to/test.featurex')).toBe(false) - expect(isCucumberFeatureFile('/path/to/test')).toBe(false) - }) + it('should return false for non-feature files', () => { + expect(isCucumberFeatureFile('/path/to/test.js')).toBe(false) + expect(isCucumberFeatureFile('/path/to/test.ts')).toBe(false) + expect(isCucumberFeatureFile('/path/to/test.featurex')).toBe(false) + expect(isCucumberFeatureFile('/path/to/test')).toBe(false) }) }) diff --git a/packages/vscode-wdio-test/tests/manager.test.ts b/packages/vscode-wdio-test/tests/manager.test.ts index a8a52e0..b0fc45a 100644 --- a/packages/vscode-wdio-test/tests/manager.test.ts +++ b/packages/vscode-wdio-test/tests/manager.test.ts @@ -42,8 +42,9 @@ describe('RepositoryManager', () => { let mockDiscoverAllTests: ReturnType let controller: vscode.TestController - const configManager = new ExtensionConfigManager() + let configManager: ExtensionConfigManager let repositoryManager: RepositoryManager + let configMangerOn: any const workspacePath = join(process.cwd(), 'fake', 'workspace') const configPath = join(workspacePath, 'wdio.conf.ts') @@ -53,6 +54,7 @@ describe('RepositoryManager', () => { beforeEach(() => { vi.resetAllMocks() + configManager = new ExtensionConfigManager() controller = { items: new MockTestItemCollection(), @@ -81,6 +83,8 @@ describe('RepositoryManager', () => { on: vi.fn(), } as unknown as IWdioExtensionWorker) + configMangerOn = vi.fn() + vi.spyOn(configManager, 'on').mockImplementation(configMangerOn) vi.spyOn(configManager, 'workspaces', 'get').mockReturnValue(mockWorkspaces) mockDiscoverAllTests = vi.fn().mockResolvedValue(undefined) @@ -111,6 +115,24 @@ describe('RepositoryManager', () => { }) }) + describe('update:configFilePattern', () => { + it('should add new test item to the test controller', async () => { + await repositoryManager.initialize() + vi.spyOn(configManager, 'getWdioConfigPaths').mockReturnValue([]) + + const handler = vi.mocked(configMangerOn).mock.calls[0][1] + const configManagerInitializeSpy = vi.spyOn(configManager, 'initialize') + const repositoryManagerInitializeSpy = vi.spyOn(repositoryManager, 'initialize') + const repositoryManagerRegisterSpy = vi.spyOn(repositoryManager, 'registerToTestController') + + await handler() + + expect(configManagerInitializeSpy).toHaveBeenCalled() + expect(repositoryManagerInitializeSpy).toHaveBeenCalled() + expect(repositoryManagerRegisterSpy).toHaveBeenCalled() + }) + }) + describe('registerToTestController', () => { it('should delete the loading test item', () => { const mockDelete = vi.fn() @@ -182,6 +204,53 @@ describe('RepositoryManager', () => { }) }) + describe('addWdioConfig', () => { + it('should add new test item to the test controller', async () => { + vi.spyOn(configManager, 'isMultiWorkspace', 'get').mockReturnValue(false) + await repositoryManager.initialize() + repositoryManager.registerToTestController() + expect(controller.items.size).toBe(1) + + await repositoryManager.addWdioConfig(mockWorkspaceFolder, '/path/to/newConfig.spec.ts') + + expect(repositoryManager.controller.items.size).toBe(2) + }) + }) + + describe('removeWdioConfig', () => { + beforeEach(async () => { + vi.spyOn(configManager, 'isMultiWorkspace', 'get').mockReturnValue(false) + await repositoryManager.initialize() + repositoryManager.registerToTestController() + + await repositoryManager.addWdioConfig(mockWorkspaceFolder, '/path/to/newConfig.spec.ts') + }) + + it('should remove test item to the test controller', async () => { + repositoryManager.removeWdioConfig(mockWorkspaceFolder, '/path/to/newConfig.spec.ts') + + expect(repositoryManager.controller.items.size).toBe(1) + }) + }) + + describe('refreshTests', () => { + it('should remove test item to the test controller', async () => { + await repositoryManager.initialize() + repositoryManager.registerToTestController() + const discoverSpy = vi.spyOn(repositoryManager.repos[0], 'discoverAllTests') + const registerSpy = vi.spyOn(repositoryManager, 'registerToTestController') + const reorganizeSpy = vi.spyOn(workerManager, 'reorganize') + vi.spyOn(configManager, 'getWdioConfigPaths').mockImplementation(vi.fn()) + + await repositoryManager.refreshTests() + + expect(repositoryManager.controller.items.size).toBe(0) + expect(discoverSpy).toHaveBeenCalled() + expect(registerSpy).toHaveBeenCalled() + expect(reorganizeSpy).toHaveBeenCalled() + }) + }) + describe('dispose', () => { it('should dispose the test controller', () => { const mockDispose = vi.fn() diff --git a/packages/vscode-wdio-test/tests/metadata.test.ts b/packages/vscode-wdio-test/tests/metadata.test.ts new file mode 100644 index 0000000..273ff05 --- /dev/null +++ b/packages/vscode-wdio-test/tests/metadata.test.ts @@ -0,0 +1,88 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import * as vscode from 'vscode' + +import { mockCreateTestItem } from '../../../tests/utils.js' +import { MetadataRepository } from '../src/metadata.js' + +vi.mock('vscode', async () => import('../../../tests/__mocks__/vscode.cjs')) + +vi.mock('@vscode-wdio/logger', () => import('../../../tests/__mocks__/logger.js')) + +describe('MetadataRepository', () => { + let repo: MetadataRepository + const mockUri = vscode.Uri.file('/path/to/dummy') + + const testItem = mockCreateTestItem('dummy', mockUri.fsPath, mockUri) as unknown as vscode.TestItem + + beforeEach(() => { + repo = new MetadataRepository() + }) + + it('should create proper metadata for workspace', () => { + repo.createWorkspaceMetadata(testItem, { uri: mockUri }) + const metadata = repo.getMetadata(testItem) + + expect(metadata.uri).toBe(mockUri) + expect(metadata.isWorkspace).toBe(true) + expect(metadata.isConfigFile).toBe(false) + expect(metadata.isSpecFile).toBe(false) + expect(metadata.isTestcase).toBe(false) + }) + + it('should create proper metadata for WdioConfigFile', () => { + const dummyRepo = {} as any + const dummyProfiles = [] as any + repo.createWdioConfigFileMetadata(testItem, { repository: dummyRepo, uri: mockUri, runProfiles: dummyProfiles }) + const metadata = repo.getMetadata(testItem) + + expect(metadata.uri).toBe(mockUri) + expect(metadata.repository).toBe(dummyRepo) + expect(metadata.runProfiles).toBe(dummyProfiles) + expect(metadata.isWorkspace).toBe(false) + expect(metadata.isConfigFile).toBe(true) + expect(metadata.isSpecFile).toBe(false) + expect(metadata.isTestcase).toBe(false) + }) + + it('should create proper metadata for SpecFile', () => { + const dummyRepo = {} as any + repo.createSpecFileMetadata(testItem, { repository: dummyRepo, uri: mockUri }) + const metadata = repo.getMetadata(testItem) + + expect(metadata.uri).toBe(mockUri) + expect(metadata.repository).toBe(dummyRepo) + expect(metadata.isWorkspace).toBe(false) + expect(metadata.isConfigFile).toBe(false) + expect(metadata.isSpecFile).toBe(true) + expect(metadata.isTestcase).toBe(false) + }) + + it('should create proper metadata for Testcase', () => { + const dummyRepo = {} as any + repo.createTestMetadata(testItem, { repository: dummyRepo, uri: mockUri, testType: 'test' }) + const metadata = repo.getMetadata(testItem) + + expect(metadata.uri).toBe(mockUri) + expect(metadata.repository).toBe(dummyRepo) + expect(metadata.type).toBe('test') + expect(metadata.isWorkspace).toBe(false) + expect(metadata.isConfigFile).toBe(false) + expect(metadata.isSpecFile).toBe(false) + expect(metadata.isTestcase).toBe(true) + }) + + it('should get same metadata from different repository instance', () => { + const dummyRepo = {} as any + const otherRepo = new MetadataRepository() + + repo.createSpecFileMetadata(testItem, { repository: dummyRepo, uri: mockUri }) + const metadata1 = repo.getMetadata(testItem) + const testRepo1 = repo.getRepository(testItem) + + const metadata2 = otherRepo.getMetadata(testItem) + const testRepo2 = otherRepo.getRepository(testItem) + + expect(metadata1).toBe(metadata2) + expect(testRepo1).toBe(testRepo2) + }) +}) diff --git a/packages/vscode-wdio-test/tests/repository.test.ts b/packages/vscode-wdio-test/tests/repository.test.ts index 8e7749a..0d6c15d 100644 --- a/packages/vscode-wdio-test/tests/repository.test.ts +++ b/packages/vscode-wdio-test/tests/repository.test.ts @@ -7,13 +7,9 @@ import * as vscode from 'vscode' import { mockCreateTestItem, MockTestItemCollection } from '../../../tests/utils.js' import { TestRepository } from '../src/repository.js' -import type { - IExtensionConfigManager, - IMetadataRepository, - ITestRepository, - TestItemMetadata, -} from '@vscode-wdio/types' +import type { IExtensionConfigManager, TestItemMetadata } from '@vscode-wdio/types' import type { IWorkerManager, WdioConfig, IWdioExtensionWorker } from '@vscode-wdio/types/server' +import type { MetadataRepository } from '../src/metadata.js' // Mock dependencies vi.mock('vscode', async () => import('../../../tests/__mocks__/vscode.cjs')) @@ -29,18 +25,14 @@ vi.mock('@vscode-wdio/utils', async (importActual) => { } }) -class MockMetadataRepository implements IMetadataRepository { +class MockMetadataRepository { + createTestMetadata = vi.fn() + createSpecFileMetadata = vi.fn() + createWdioConfigFileMetadata = vi.fn() + createWorkspaceMetadata = vi.fn() _repo = new WeakMap() - setMetadata(testItem: vscode.TestItem, metadata: TestItemMetadata): void { - this._repo.set(testItem, metadata) - } - getMetadata(testItem: vscode.TestItem): TestItemMetadata { - return this._repo.get(testItem)! - } - getRepository(testItem: vscode.TestItem): ITestRepository { - this._repo.get(testItem)! - return this._repo.get(testItem)!.repository! - } + getMetadata = vi.fn() + getRepository = vi.fn() } describe('TestRepository', () => { @@ -104,13 +96,13 @@ describe('TestRepository', () => { } as unknown as IWorkerManager const mockMetaRepo = new MockMetadataRepository() - mockMetaRepo.setMetadata(wdioConfigTestItem, { + mockMetaRepo.createWdioConfigFileMetadata(wdioConfigTestItem, { uri: mockWdioConfigUri, - isWorkspace: false, - isConfigFile: true, - isSpecFile: false, - isTestcase: false, repository: {} as any, + runProfiles: [], + }) + + mockMetaRepo.getMetadata.mockReturnValue({ runProfiles: [ { dispose: mockRunProfileDispose, @@ -126,7 +118,7 @@ describe('TestRepository', () => { wdioConfigTestItem, workerManager, mockWorkspaceFolder, - mockMetaRepo + mockMetaRepo as unknown as MetadataRepository ) }) diff --git a/packages/vscode-wdio-test/tests/utils.test.ts b/packages/vscode-wdio-test/tests/utils.test.ts index ab892c5..4a9438c 100644 --- a/packages/vscode-wdio-test/tests/utils.test.ts +++ b/packages/vscode-wdio-test/tests/utils.test.ts @@ -1,6 +1,8 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' +import { mockCreateTestItem } from '../../../tests/utils.js' import * as utils from '../src/utils.js' +import { testTreeCreator } from '../src/utils.js' import type * as vscode from 'vscode' import type { RepositoryManager } from '../src/manager.js' @@ -27,6 +29,12 @@ vi.mock('../src/runHandler.js', () => { } }) +vi.mock('../src/converter.js', () => { + return { + convertSourceRangeToVSCodeRange: vi.fn(() => {}), + } +}) + describe('Test Utils', () => { describe('filterSpecsByPaths', () => { it('should filter specs correctly with exact path match', () => { @@ -140,4 +148,81 @@ describe('Test Utils', () => { expect(result.id).toBe(rootTestItem.id) }) }) + + describe('getWorkspaceFolder', () => { + const rootTestItem = { + id: 'root', + parent: undefined, + } as unknown as vscode.TestItem + const l1TestItem = { + id: 'l1', + parent: rootTestItem, + } as unknown as vscode.TestItem + const l2TestItem = { + id: 'l2', + parent: l1TestItem, + } as unknown as vscode.TestItem + + it('should return root TestItem when input the ground child item', () => { + const result = utils.getRootTestItem(l2TestItem) + expect(result.id).toBe(rootTestItem.id) + }) + + it('should return root TestItem when input the child item2', () => { + const result = utils.getRootTestItem(l1TestItem) + expect(result.id).toBe(rootTestItem.id) + }) + + it('should return root TestItem when input the root item itself', () => { + const result = utils.getRootTestItem(rootTestItem) + expect(result.id).toBe(rootTestItem.id) + }) + }) + + describe('testTreeCreator', async () => { + let mockRepository: any + let mockMetadata: any + + beforeEach(() => { + mockRepository = { + controller: { + createTestItem: mockCreateTestItem, + }, + } + mockMetadata = { + createTestMetadata: vi.fn(), + } + }) + + it('should create test tree with metadata and children', () => { + const uri = { path: '/dummy/path' } as vscode.Uri + + const testData = { + name: 'ParentTest', + type: 'test', + range: {} as any, + children: [ + { + name: 'ChildTest', + type: 'test', + range: { start: 3, end: 4 }, + children: [], + }, + ], + } + + const result = testTreeCreator(mockRepository, mockMetadata, 'root', testData as any, uri) + + expect(result.id).toBe('root#WDIO_SEP#ParentTest') + expect(result.label).toBe('ParentTest') + expect(result.uri).toBe(uri) + + expect(result.children.size).toBe(1) + const child = result.children.get('root#WDIO_SEP#ParentTest#WDIO_SEP#ChildTest') + expect(child!.label).toBe('ChildTest') + + expect(mockMetadata.createTestMetadata).toHaveBeenCalledTimes(2) + expect(mockCreateTestItem).toHaveBeenCalledTimes(2) + }) + }) }) diff --git a/packages/vscode-wdio-test/tests/watcher.test.ts b/packages/vscode-wdio-test/tests/watcher.test.ts index cce72d4..5b68510 100644 --- a/packages/vscode-wdio-test/tests/watcher.test.ts +++ b/packages/vscode-wdio-test/tests/watcher.test.ts @@ -5,6 +5,7 @@ import { FileWatcherManager } from '@vscode-wdio/utils' import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import { TestfileWatcher } from '../src/watcher.js' +import type { WatchPattern } from '@vscode-wdio/utils' import type * as vscode from 'vscode' import type { RepositoryManager } from '../src/manager.js' @@ -40,12 +41,16 @@ describe('TestfileWatcher', () => { // Create mock repositories mockRepo1 = { + wdioConfigPath: '/path/to/config1.ts', + specPatterns: ['tests/**.test.ts'], getSpecByFilePath: vi.fn(), reloadSpecFiles: vi.fn().mockResolvedValue(undefined), removeSpecFile: vi.fn(), } mockRepo2 = { + wdioConfigPath: '/path/to/config2.ts', + specPatterns: ['tests/**.spec.ts'], getSpecByFilePath: vi.fn(), reloadSpecFiles: vi.fn().mockResolvedValue(undefined), removeSpecFile: vi.fn(), @@ -78,6 +83,19 @@ describe('TestfileWatcher', () => { }) }) + describe('getFilePatterns', () => { + it('should return correct file patterns', () => { + // const mock = vi.fn() + // FileWatcherManager.prototype['getFilePatterns'] + + watcher = new TestfileWatcher(mockRepositoryManager) + const result = (watcher as any).getFilePatterns() as WatchPattern[] + expect(result.length).toBe(2) + expect(result[0].pattern).toBe('tests/**.test.ts') + expect(result[1].pattern).toBe('tests/**.spec.ts') + }) + }) + describe('handleFileChange', () => { it('should process file creation correctly', async () => { // Execute From b692430e888b8cf13bc3630920291d65d29f3637 Mon Sep 17 00:00:00 2001 From: mato533 Date: Fri, 27 Jun 2025 14:31:01 +0900 Subject: [PATCH 4/4] chore: update code styling --- packages/vscode-wdio-test/src/manager.ts | 32 ++++++++--------- packages/vscode-wdio-test/src/metadata.ts | 7 ++-- packages/vscode-wdio-test/src/reporter.ts | 18 +++++----- packages/vscode-wdio-test/src/repository.ts | 36 +++++++++---------- packages/vscode-wdio-test/src/runHandler.ts | 10 +++--- packages/vscode-wdio-test/src/watcher.ts | 8 ++--- .../vscode-wdio-test/tests/reporter.test.ts | 32 ++++++++--------- .../vscode-wdio-test/tests/repository.test.ts | 2 +- 8 files changed, 73 insertions(+), 72 deletions(-) diff --git a/packages/vscode-wdio-test/src/manager.ts b/packages/vscode-wdio-test/src/manager.ts index fb70230..7c9a7c1 100644 --- a/packages/vscode-wdio-test/src/manager.ts +++ b/packages/vscode-wdio-test/src/manager.ts @@ -28,12 +28,12 @@ export class RepositoryManager implements IRepositoryManager { private _workspaceTestItems: vscode.TestItem[] = [] private _wdioConfigTestItems: vscode.TestItem[] = [] private _isInitialized = false - private isCreatedDefaultProfile = false + private _isCreatedDefaultProfile = false constructor( public readonly controller: vscode.TestController, public readonly configManager: ExtensionConfigManager, - private readonly workerManager: IWorkerManager, + private readonly _workerManager: IWorkerManager, private readonly _metadata = new MetadataRepository() ) { this._loadingTestItem = this.controller.createTestItem(LOADING_TEST_ITEM_ID, 'Resolving WebdriverIO Tests...') @@ -56,7 +56,7 @@ export class RepositoryManager implements IRepositoryManager { .then(async () => await this.initialize()) .then(async () => await Promise.all(this.repos.map(async (repo) => await repo.discoverAllTests()))) .then(() => this.registerToTestController()) - .then(() => this.workerManager.reorganize(configManager.getWdioConfigPaths())) + .then(() => this._workerManager.reorganize(configManager.getWdioConfigPaths())) }) } @@ -86,11 +86,11 @@ export class RepositoryManager implements IRepositoryManager { this._workspaceTestItems = await Promise.all( workspaces.map(async (workspace) => { - const workspaceTestItem = this.createWorkspaceTestItem(workspace.workspaceFolder) + const workspaceTestItem = this._createWorkspaceTestItem(workspace.workspaceFolder) for (const wdioConfigFile of workspace.wdioConfigFiles) { - await this.createWdioConfigTestItem(workspace.workspaceFolder, workspaceTestItem, wdioConfigFile) + await this._createWdioConfigTestItem(workspace.workspaceFolder, workspaceTestItem, wdioConfigFile) - this.isCreatedDefaultProfile = true + this._isCreatedDefaultProfile = true } return workspaceTestItem }) @@ -104,7 +104,7 @@ export class RepositoryManager implements IRepositoryManager { return item.uri?.fsPath === workspaceFolder.uri.fsPath }) for (const workspaceTestItem of affectedWorkspaceItems) { - const configTestItem = await this.createWdioConfigTestItem( + const configTestItem = await this._createWdioConfigTestItem( workspaceFolder, workspaceTestItem, wdioConfigPath @@ -125,7 +125,7 @@ export class RepositoryManager implements IRepositoryManager { log.debug(`Remove the config file from ${affectedWorkspaceItems.length} workspace(s)`) const configUri = convertPathToUri(wdioConfigPath) for (const workspaceItem of affectedWorkspaceItems) { - const config = workspaceItem.children.get(this.generateConfigTestItemId(workspaceItem, configUri)) + const config = workspaceItem.children.get(this._generateConfigTestItemId(workspaceItem, configUri)) if (!config) { continue } @@ -141,7 +141,7 @@ export class RepositoryManager implements IRepositoryManager { this.controller.items.delete(workspaceItem.id) } } else { - const targetId = this.generateConfigTestItemId(workspaceItem, configUri) + const targetId = this._generateConfigTestItemId(workspaceItem, configUri) log.debug(`Remove Configuration from the controller: ${targetId}`) this.controller.items.delete(targetId) } @@ -162,7 +162,7 @@ export class RepositoryManager implements IRepositoryManager { log.debug('Successfully registered.') } - private createWorkspaceTestItem(workspaceFolder: vscode.WorkspaceFolder) { + private _createWorkspaceTestItem(workspaceFolder: vscode.WorkspaceFolder) { const workspaceItem = this.controller.createTestItem( `workspace:${workspaceFolder.uri.fsPath}`, workspaceFolder.name, @@ -172,14 +172,14 @@ export class RepositoryManager implements IRepositoryManager { return workspaceItem } - private async createWdioConfigTestItem( + private async _createWdioConfigTestItem( workspaceFolder: vscode.WorkspaceFolder, workspaceTestItem: vscode.TestItem, wdioConfigPath: string ) { const uri = convertPathToUri(wdioConfigPath) const configItem = this.controller.createTestItem( - this.generateConfigTestItemId(workspaceTestItem, uri), + this._generateConfigTestItemId(workspaceTestItem, uri), basename(wdioConfigPath), uri ) @@ -192,20 +192,20 @@ export class RepositoryManager implements IRepositoryManager { this.controller, wdioConfigPath, configItem, - this.workerManager, + this._workerManager, workspaceFolder ) this._repos.add(repository) configItem.description = relative(workspaceTestItem.uri!.fsPath, dirname(wdioConfigPath)) - const runProfiles = createRunProfile.call(this, configItem, !this.isCreatedDefaultProfile) + const runProfiles = createRunProfile.call(this, configItem, !this._isCreatedDefaultProfile) this._metadata.createWdioConfigFileMetadata(configItem, { uri, repository, runProfiles }) return configItem } - private generateConfigTestItemId(workspaceTestItem: vscode.TestItem, configUri: vscode.Uri) { + private _generateConfigTestItemId(workspaceTestItem: vscode.TestItem, configUri: vscode.Uri) { return [workspaceTestItem.id, `config:${configUri.fsPath}`].join(TEST_ID_SEPARATOR) } @@ -225,7 +225,7 @@ export class RepositoryManager implements IRepositoryManager { ) this.registerToTestController() - await this.workerManager.reorganize(this.configManager.getWdioConfigPaths()) + await this._workerManager.reorganize(this.configManager.getWdioConfigPaths()) } catch (error) { this.controller.items.replace([]) const errorMessage = error instanceof Error ? error.message : String(error) diff --git a/packages/vscode-wdio-test/src/metadata.ts b/packages/vscode-wdio-test/src/metadata.ts index 701f84a..960b76e 100644 --- a/packages/vscode-wdio-test/src/metadata.ts +++ b/packages/vscode-wdio-test/src/metadata.ts @@ -2,9 +2,10 @@ import type { ITestRepository, TestItemMetadata, TestType } from '@vscode-wdio/t import type * as vscode from 'vscode' export class MetadataRepository { - private static testMetadataRepository = new WeakMap() + private static _testMetadataRepository = new WeakMap() + public getMetadata(testItem: vscode.TestItem) { - const metadata = MetadataRepository.testMetadataRepository.get(testItem) + const metadata = MetadataRepository._testMetadataRepository.get(testItem) if (!metadata) { throw new Error("The metadata for TestItem is not set. This is extension's bug.") } @@ -20,7 +21,7 @@ export class MetadataRepository { } protected setMetadata(testItem: vscode.TestItem, metadata: TestItemMetadata) { - MetadataRepository.testMetadataRepository.set(testItem, metadata) + MetadataRepository._testMetadataRepository.set(testItem, metadata) } public createTestMetadata( diff --git a/packages/vscode-wdio-test/src/reporter.ts b/packages/vscode-wdio-test/src/reporter.ts index 5906bce..c335f24 100644 --- a/packages/vscode-wdio-test/src/reporter.ts +++ b/packages/vscode-wdio-test/src/reporter.ts @@ -48,11 +48,11 @@ export class TestReporter { // Process suites in this spec file for (const suite of result.suites) { - this.processHierarchicalSuite(suite, specTestItem) + this._processHierarchicalSuite(suite, specTestItem) } // Update spec file status based on overall result status - this.updateSpecFileStatus(specTestItem, result) + this._updateSpecFileStatus(specTestItem, result) } // Check if any test failed @@ -74,7 +74,7 @@ export class TestReporter { * @param suite The suite result from WebdriverIO with potential nested suites * @param parentItem The parent TestItem (spec file or parent suite) */ - private processHierarchicalSuite(suite: TestSuite, parentItem: vscode.TestItem): void { + private _processHierarchicalSuite(suite: TestSuite, parentItem: vscode.TestItem): void { // Find the suite TestItem in the repository let suiteItem: vscode.TestItem | undefined @@ -91,18 +91,18 @@ export class TestReporter { // Process tests in this suite for (const test of suite.tests) { - this.processTest(test, suiteItem) + this._processTest(test, suiteItem) } // Process nested suites if any if (suite.suites && suite.suites.length > 0) { for (const nestedSuite of suite.suites) { - this.processHierarchicalSuite(nestedSuite, suiteItem) + this._processHierarchicalSuite(nestedSuite, suiteItem) } } // Set suite status based on its tests - this.updateSuiteStatus(suiteItem, suite) + this._updateSuiteStatus(suiteItem, suite) } /** @@ -110,7 +110,7 @@ export class TestReporter { * @param test The test result from WebdriverIO * @param suiteItem The parent suite TestItem */ - private processTest(test: Test, suiteItem: vscode.TestItem): void { + private _processTest(test: Test, suiteItem: vscode.TestItem): void { // Find the test item in the children of the suite item let testItem: vscode.TestItem | undefined @@ -148,7 +148,7 @@ export class TestReporter { * @param suiteItem The suite TestItem * @param suiteResult The suite result from WebdriverIO */ - private updateSuiteStatus(suiteItem: vscode.TestItem, suiteResult: TestSuite): void { + private _updateSuiteStatus(suiteItem: vscode.TestItem, suiteResult: TestSuite): void { const hasFailedTests = suiteResult.tests.some((test) => test.state === 'failed') if (hasFailedTests || suiteResult.tests.length > 0) { @@ -168,7 +168,7 @@ export class TestReporter { * @param specItem The spec file TestItem * @param result The WebdriverIO result for this spec file */ - private updateSpecFileStatus(specItem: vscode.TestItem, result: ResultSet): void { + private _updateSpecFileStatus(specItem: vscode.TestItem, result: ResultSet): void { if (result.state.failed > 0) { const message = new vscode.TestMessage( `${result.state.failed} tests failed out of ${result.state.passed + result.state.failed + result.state.skipped}` diff --git a/packages/vscode-wdio-test/src/repository.ts b/packages/vscode-wdio-test/src/repository.ts index f9b0eba..a8e50b3 100644 --- a/packages/vscode-wdio-test/src/repository.ts +++ b/packages/vscode-wdio-test/src/repository.ts @@ -68,7 +68,7 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository return this._framework } - private async getEnvOptions() { + private async _getEnvOptions() { return await getEnvOptions(this.configManager, this._workspaceFolder) } @@ -79,7 +79,7 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository try { const worker = await this.getWorker() const config = await worker.rpc.loadWdioConfig({ - env: await this.getEnvOptions(), + env: await this._getEnvOptions(), configFilePath: this.wdioConfigPath, }) @@ -95,7 +95,7 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository this._specPatterns = config.specPatterns // Get specs from configuration - const allSpecs = this.convertPathString(config.specs) + const allSpecs = this._convertPathString(config.specs) if (allSpecs.length < 1) { log.debug('No spec files found in configuration') @@ -105,7 +105,7 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository log.debug(`Discovered ${allSpecs.length} spec files`) // Register all specs - return await this.resisterSpecs(allSpecs) + return await this._resisterSpecs(allSpecs) } catch (error) { log.error(`Failed to discover tests: ${(error as Error).message}`) log.trace(`Failed to discover tests: ${(error as Error).stack}`) @@ -120,7 +120,7 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository try { const worker = await this.getWorker() const config = await worker.rpc.loadWdioConfig({ - env: await this.getEnvOptions(), + env: await this._getEnvOptions(), configFilePath: this.wdioConfigPath, }) if (!config) { @@ -131,10 +131,10 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository this._specPatterns = config.specPatterns // Get specs from configuration - const allConfigSpecs = this.convertPathString(config.specs) + const allConfigSpecs = this._convertPathString(config.specs) if (!filePaths || filePaths.length === 0) { for (const [fileId] of this._fileMap.entries()) { - this.removeSpecFileById(fileId) + this._removeSpecFileById(fileId) } } // Filter specs to only include those that match the provided file paths @@ -158,7 +158,7 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository } } // Register the updated spec files - await this.resisterSpecs(specsToReload, false) + await this._resisterSpecs(specsToReload, false) log.debug(`Successfully reloaded ${specsToReload.length} spec files`) } catch (error) { @@ -178,7 +178,7 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository } } - private getTestFileId(wdioConfigTestItem: vscode.TestItem, testFilePath: string) { + private _getTestFileId(wdioConfigTestItem: vscode.TestItem, testFilePath: string) { return [wdioConfigTestItem.id, testFilePath].join(TEST_ID_SEPARATOR) } @@ -187,14 +187,14 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository * @param specs Paths to spec files * @param replaceAllSpecFiles if all spec files to resister, this parameter must be true */ - private async resisterSpecs(specs: string[], replaceAllSpecFiles: boolean = true) { + private async _resisterSpecs(specs: string[], replaceAllSpecFiles: boolean = true) { if (replaceAllSpecFiles) { this._fileMap.clear() } log.debug(`Spec files registration is started for: ${specs.length} files.`) const worker = await this.getWorker() const testData = await worker.rpc.readSpecs({ - env: await this.getEnvOptions(), + env: await this._getEnvOptions(), specs, }) @@ -203,11 +203,11 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository testData.map(async (test) => { try { // Create TestItem testFile by testFile - const fileId = this.getTestFileId(this._wdioConfigTestItem, test.spec) + const fileId = this._getTestFileId(this._wdioConfigTestItem, test.spec) const specFileUri = vscode.Uri.file(test.spec) - const fileTestItem = this.resisterSpecFile(fileId, specFileUri) + const fileTestItem = this._resisterSpecFile(fileId, specFileUri) for (const testCase of test.tests) { fileTestItem.children.add( testTreeCreator(this, this._metadata, fileId, testCase, specFileUri) @@ -241,11 +241,11 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository */ public removeSpecFile(specPath: string): void { const normalizedPath = normalizePath(specPath) - const fileId = this.getTestFileId(this._wdioConfigTestItem, normalizedPath) - this.removeSpecFileById(fileId, specPath) + const fileId = this._getTestFileId(this._wdioConfigTestItem, normalizedPath) + this._removeSpecFileById(fileId, specPath) } - private removeSpecFileById(fileId: string, _specPath?: string): void { + private _removeSpecFileById(fileId: string, _specPath?: string): void { const specPath = _specPath ? _specPath : fileId.split(TEST_ID_SEPARATOR)[2] // Get the TestItem for this spec file const fileItem = this._fileMap.get(fileId) @@ -266,7 +266,7 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository /** * Convert spec paths from WebdriverIO config to file system paths */ - private convertPathString(specs: (string | string[])[]) { + private _convertPathString(specs: (string | string[])[]) { return specs.flatMap((spec) => (Array.isArray(spec) ? spec.map((path) => path) : [spec])) } @@ -276,7 +276,7 @@ export class TestRepository extends WorkerMiddleware implements ITestRepository * @param uri Spec file URI * @returns TestItem for the spec file */ - private resisterSpecFile(id: string, uri: vscode.Uri) { + private _resisterSpecFile(id: string, uri: vscode.Uri) { log.trace(`[repository] spec file was registered: ${id}`) const fileTestItem = this.controller.createTestItem(id, path.basename(uri.fsPath), uri) fileTestItem.sortText = uri.fsPath diff --git a/packages/vscode-wdio-test/src/runHandler.ts b/packages/vscode-wdio-test/src/runHandler.ts index db0cbfe..5bc1ef1 100644 --- a/packages/vscode-wdio-test/src/runHandler.ts +++ b/packages/vscode-wdio-test/src/runHandler.ts @@ -11,18 +11,18 @@ import type { IExtensionConfigManager } from '@vscode-wdio/types/config' import type { RepositoryManager } from './manager.js' class TestQueue { - private queue: vscode.TestItem[] = [] + private _queue: vscode.TestItem[] = [] push(item: vscode.TestItem) { - this.queue.push(item) + this._queue.push(item) } - forEach(cb: Parameters[0]) { - this.queue.forEach(cb) + forEach(cb: Parameters[0]) { + this._queue.forEach(cb) } [Symbol.iterator]() { - return this.queue[Symbol.iterator]() + return this._queue[Symbol.iterator]() } } diff --git a/packages/vscode-wdio-test/src/watcher.ts b/packages/vscode-wdio-test/src/watcher.ts index ce076b9..5e4f90f 100644 --- a/packages/vscode-wdio-test/src/watcher.ts +++ b/packages/vscode-wdio-test/src/watcher.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode' import type { RepositoryManager } from './manager.js' export class TestfileWatcher extends FileWatcherManager { - constructor(private readonly repositoryManager: RepositoryManager) { + constructor(private readonly _repositoryManager: RepositoryManager) { super() } @@ -15,7 +15,7 @@ export class TestfileWatcher extends FileWatcherManager { } protected getFilePatterns(): WatchPattern[] { - return this.repositoryManager.repos.reduce((patterns, repo) => { + return this._repositoryManager.repos.reduce((patterns, repo) => { const configDirPath = dirname(repo.wdioConfigPath) for (const pattern of repo.specPatterns) { patterns.push({ @@ -44,7 +44,7 @@ export class TestfileWatcher extends FileWatcherManager { // If a Spec file is newly created, attempt to read in all repositories, // as it is unclear which configuration file should be reflected. - const promises = this.repositoryManager.repos.reduce((repos, repo) => { + const promises = this._repositoryManager.repos.reduce((repos, repo) => { if (!isCreated && !repo.getSpecByFilePath(specFilePath)) { return repos } @@ -61,7 +61,7 @@ export class TestfileWatcher extends FileWatcherManager { protected async handleFileDelete(uri: vscode.Uri): Promise { const specFilePath = uri.fsPath log.debug(`Test file deleted: ${specFilePath}`) - const count = this.repositoryManager.repos.reduce((counter, repo) => { + const count = this._repositoryManager.repos.reduce((counter, repo) => { if (!repo.getSpecByFilePath(specFilePath)) { return counter } diff --git a/packages/vscode-wdio-test/tests/reporter.test.ts b/packages/vscode-wdio-test/tests/reporter.test.ts index 269e84b..b918730 100644 --- a/packages/vscode-wdio-test/tests/reporter.test.ts +++ b/packages/vscode-wdio-test/tests/reporter.test.ts @@ -191,9 +191,9 @@ describe('TestReporter', () => { describe('processHierarchicalSuite', () => { it('should process tests and nested suites correctly', () => { // Setup - create a private method spy - const processHierarchicalSuiteSpy = vi.spyOn(testReporter as any, 'processHierarchicalSuite') - const processTestSpy = vi.spyOn(testReporter as any, 'processTest') - const updateSuiteStatusSpy = vi.spyOn(testReporter as any, 'updateSuiteStatus') + const processHierarchicalSuiteSpy = vi.spyOn(testReporter as any, '_processHierarchicalSuite') + const processTestSpy = vi.spyOn(testReporter as any, '_processTest') + const _updateSuiteStatusSpy = vi.spyOn(testReporter as any, '_updateSuiteStatus') // Create test structure const nestedSuiteItem = createMockTestItem('nested', 'nested') @@ -206,12 +206,12 @@ describe('TestReporter', () => { const mockSuite = createMockTestSuite('suite', [mockTest], [mockNestedSuite]) // Execute - call the private method - ;(testReporter as any).processHierarchicalSuite(mockSuite, parentItem) + ;(testReporter as any)._processHierarchicalSuite(mockSuite, parentItem) // Verify expect(processTestSpy).toHaveBeenCalledWith(mockTest, suiteItem) expect(processHierarchicalSuiteSpy).toHaveBeenCalledWith(mockNestedSuite, suiteItem) - expect(updateSuiteStatusSpy).toHaveBeenCalledWith(suiteItem, mockSuite) + expect(_updateSuiteStatusSpy).toHaveBeenCalledWith(suiteItem, mockSuite) }) it('should skip suite when not found in repository', () => { @@ -220,7 +220,7 @@ describe('TestReporter', () => { const mockSuite = createMockTestSuite('Suite', []) // Execute - ;(testReporter as any).processHierarchicalSuite(mockSuite, parentItem) + ;(testReporter as any)._processHierarchicalSuite(mockSuite, parentItem) // Verify expect(log.debug).toHaveBeenCalledWith(expect.stringContaining('Suite')) @@ -240,7 +240,7 @@ describe('TestReporter', () => { } // Execute - ;(testReporter as any).processTest(mockTest, suiteItem) + ;(testReporter as any)._processTest(mockTest, suiteItem) // Verify expect(mockTestRun.passed).toHaveBeenCalledWith(testItem, 100) @@ -258,7 +258,7 @@ describe('TestReporter', () => { } // Execute - ;(testReporter as any).processTest(mockTest, suiteItem) + ;(testReporter as any)._processTest(mockTest, suiteItem) // Verify expect(mockTestRun.failed).toHaveBeenCalledWith(testItem, expect.any(vscode.TestMessage), 100) @@ -276,7 +276,7 @@ describe('TestReporter', () => { } // Execute - ;(testReporter as any).processTest(mockTest, suiteItem) + ;(testReporter as any)._processTest(mockTest, suiteItem) // Verify expect(mockTestRun.skipped).toHaveBeenCalledWith(testItem) @@ -294,7 +294,7 @@ describe('TestReporter', () => { } // Execute - ;(testReporter as any).processTest(mockTest, suiteItem) + ;(testReporter as any)._processTest(mockTest, suiteItem) // Verify expect(mockTestRun.skipped).toHaveBeenCalledWith(testItem) @@ -311,7 +311,7 @@ describe('TestReporter', () => { } // Execute - ;(testReporter as any).processTest(mockTest, suiteItem) + ;(testReporter as any)._processTest(mockTest, suiteItem) // Verify expect(log.debug).toHaveBeenCalledWith(expect.stringContaining('Test 1')) @@ -319,7 +319,7 @@ describe('TestReporter', () => { }) }) - describe('updateSuiteStatus', () => { + describe('_updateSuiteStatus', () => { it('should mark suite as passed when there are tests in the suite', () => { // Setup const suiteItem = createMockTestItem('suite', 'Suite') @@ -327,7 +327,7 @@ describe('TestReporter', () => { const mockSuite = createMockTestSuite('Suite', [mockTest], [], 500) // Execute - ;(testReporter as any).updateSuiteStatus(suiteItem, mockSuite) + ;(testReporter as any)._updateSuiteStatus(suiteItem, mockSuite) // Verify expect(mockTestRun.passed).toHaveBeenCalledWith(suiteItem, 500) @@ -341,7 +341,7 @@ describe('TestReporter', () => { const mockResult = createMockResultSet(['/path/to/spec.js'], [], { passed: 2, failed: 0, skipped: 0 }) // Execute - ;(testReporter as any).updateSpecFileStatus(specItem, mockResult) + ;(testReporter as any)._updateSpecFileStatus(specItem, mockResult) // Verify expect(mockTestRun.passed).toHaveBeenCalledWith(specItem) @@ -353,7 +353,7 @@ describe('TestReporter', () => { const mockResult = createMockResultSet(['/path/to/spec.js'], [], { passed: 1, failed: 1, skipped: 0 }) // Execute - ;(testReporter as any).updateSpecFileStatus(specItem, mockResult) + ;(testReporter as any)._updateSpecFileStatus(specItem, mockResult) // Verify expect(mockTestRun.failed).toHaveBeenCalledWith(specItem, expect.any(vscode.TestMessage)) @@ -365,7 +365,7 @@ describe('TestReporter', () => { const mockResult = createMockResultSet(['/path/to/spec.js'], [], { passed: 0, failed: 0, skipped: 2 }) // Execute - ;(testReporter as any).updateSpecFileStatus(specItem, mockResult) + ;(testReporter as any)._updateSpecFileStatus(specItem, mockResult) // Verify expect(mockTestRun.skipped).toHaveBeenCalledWith(specItem) diff --git a/packages/vscode-wdio-test/tests/repository.test.ts b/packages/vscode-wdio-test/tests/repository.test.ts index 0d6c15d..af56dfc 100644 --- a/packages/vscode-wdio-test/tests/repository.test.ts +++ b/packages/vscode-wdio-test/tests/repository.test.ts @@ -377,7 +377,7 @@ describe('TestRepository', () => { ] // Execute - const result = (testRepository as any).convertPathString(specs) + const result = (testRepository as any)._convertPathString(specs) // Verify expect(result).toHaveLength(3)