feat(tstest): Enhance tstest with fluent API, suite grouping, tag filtering, fixture & snapshot testing, and parallel execution improvements
This commit is contained in:
		| @@ -15,6 +15,7 @@ export class TsTest { | ||||
|   public testDir: TestDirectory; | ||||
|   public executionMode: TestExecutionMode; | ||||
|   public logger: TsTestLogger; | ||||
|   public filterTags: string[]; | ||||
|  | ||||
|   public smartshellInstance = new plugins.smartshell.Smartshell({ | ||||
|     executor: 'bash', | ||||
| @@ -25,53 +26,81 @@ export class TsTest { | ||||
|  | ||||
|   public tsbundleInstance = new plugins.tsbundle.TsBundle(); | ||||
|  | ||||
|   constructor(cwdArg: string, testPathArg: string, executionModeArg: TestExecutionMode, logOptions: LogOptions = {}) { | ||||
|   constructor(cwdArg: string, testPathArg: string, executionModeArg: TestExecutionMode, logOptions: LogOptions = {}, tags: string[] = []) { | ||||
|     this.executionMode = executionModeArg; | ||||
|     this.testDir = new TestDirectory(cwdArg, testPathArg, executionModeArg); | ||||
|     this.logger = new TsTestLogger(logOptions); | ||||
|     this.filterTags = tags; | ||||
|   } | ||||
|  | ||||
|   async run() { | ||||
|     const fileNamesToRun: string[] = await this.testDir.getTestFilePathArray(); | ||||
|     const testGroups = await this.testDir.getTestFileGroups(); | ||||
|     const allFiles = [...testGroups.serial, ...Object.values(testGroups.parallelGroups).flat()]; | ||||
|      | ||||
|     // Log test discovery | ||||
|     this.logger.testDiscovery( | ||||
|       fileNamesToRun.length,  | ||||
|       allFiles.length,  | ||||
|       this.testDir.testPath, | ||||
|       this.executionMode | ||||
|     ); | ||||
|  | ||||
|     const tapCombinator = new TapCombinator(this.logger); // lets create the TapCombinator | ||||
|     let fileIndex = 0; | ||||
|     for (const fileNameArg of fileNamesToRun) { | ||||
|      | ||||
|     // Execute serial tests first | ||||
|     for (const fileNameArg of testGroups.serial) { | ||||
|       fileIndex++; | ||||
|       switch (true) { | ||||
|         case process.env.CI && fileNameArg.includes('.nonci.'): | ||||
|           this.logger.tapOutput(`Skipping ${fileNameArg} - marked as non-CI`); | ||||
|           break; | ||||
|         case fileNameArg.endsWith('.browser.ts') || fileNameArg.endsWith('.browser.nonci.ts'): | ||||
|           const tapParserBrowser = await this.runInChrome(fileNameArg, fileIndex, fileNamesToRun.length); | ||||
|           tapCombinator.addTapParser(tapParserBrowser); | ||||
|           break; | ||||
|         case fileNameArg.endsWith('.both.ts') || fileNameArg.endsWith('.both.nonci.ts'): | ||||
|           this.logger.sectionStart('Part 1: Chrome'); | ||||
|           const tapParserBothBrowser = await this.runInChrome(fileNameArg, fileIndex, fileNamesToRun.length); | ||||
|           tapCombinator.addTapParser(tapParserBothBrowser); | ||||
|           this.logger.sectionEnd(); | ||||
|            | ||||
|           this.logger.sectionStart('Part 2: Node'); | ||||
|           const tapParserBothNode = await this.runInNode(fileNameArg, fileIndex, fileNamesToRun.length); | ||||
|           tapCombinator.addTapParser(tapParserBothNode); | ||||
|           this.logger.sectionEnd(); | ||||
|           break; | ||||
|         default: | ||||
|           const tapParserNode = await this.runInNode(fileNameArg, fileIndex, fileNamesToRun.length); | ||||
|           tapCombinator.addTapParser(tapParserNode); | ||||
|           break; | ||||
|       await this.runSingleTest(fileNameArg, fileIndex, allFiles.length, tapCombinator); | ||||
|     } | ||||
|      | ||||
|     // Execute parallel groups sequentially | ||||
|     const groupNames = Object.keys(testGroups.parallelGroups).sort(); | ||||
|     for (const groupName of groupNames) { | ||||
|       const groupFiles = testGroups.parallelGroups[groupName]; | ||||
|        | ||||
|       if (groupFiles.length > 0) { | ||||
|         this.logger.sectionStart(`Parallel Group: ${groupName}`); | ||||
|          | ||||
|         // Run all tests in this group in parallel | ||||
|         const parallelPromises = groupFiles.map(async (fileNameArg) => { | ||||
|           fileIndex++; | ||||
|           return this.runSingleTest(fileNameArg, fileIndex, allFiles.length, tapCombinator); | ||||
|         }); | ||||
|          | ||||
|         await Promise.all(parallelPromises); | ||||
|         this.logger.sectionEnd(); | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     tapCombinator.evaluate(); | ||||
|   } | ||||
|    | ||||
|   private async runSingleTest(fileNameArg: string, fileIndex: number, totalFiles: number, tapCombinator: TapCombinator) { | ||||
|     switch (true) { | ||||
|       case process.env.CI && fileNameArg.includes('.nonci.'): | ||||
|         this.logger.tapOutput(`Skipping ${fileNameArg} - marked as non-CI`); | ||||
|         break; | ||||
|       case fileNameArg.endsWith('.browser.ts') || fileNameArg.endsWith('.browser.nonci.ts'): | ||||
|         const tapParserBrowser = await this.runInChrome(fileNameArg, fileIndex, totalFiles); | ||||
|         tapCombinator.addTapParser(tapParserBrowser); | ||||
|         break; | ||||
|       case fileNameArg.endsWith('.both.ts') || fileNameArg.endsWith('.both.nonci.ts'): | ||||
|         this.logger.sectionStart('Part 1: Chrome'); | ||||
|         const tapParserBothBrowser = await this.runInChrome(fileNameArg, fileIndex, totalFiles); | ||||
|         tapCombinator.addTapParser(tapParserBothBrowser); | ||||
|         this.logger.sectionEnd(); | ||||
|          | ||||
|         this.logger.sectionStart('Part 2: Node'); | ||||
|         const tapParserBothNode = await this.runInNode(fileNameArg, fileIndex, totalFiles); | ||||
|         tapCombinator.addTapParser(tapParserBothNode); | ||||
|         this.logger.sectionEnd(); | ||||
|         break; | ||||
|       default: | ||||
|         const tapParserNode = await this.runInNode(fileNameArg, fileIndex, totalFiles); | ||||
|         tapCombinator.addTapParser(tapParserNode); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public async runInNode(fileNameArg: string, index: number, total: number): Promise<TapParser> { | ||||
|     this.logger.testFileStart(fileNameArg, 'node.js', index, total); | ||||
| @@ -82,6 +111,11 @@ export class TsTest { | ||||
|     if (process.argv.includes('--web')) { | ||||
|       tsrunOptions += ' --web'; | ||||
|     } | ||||
|      | ||||
|     // Set filter tags as environment variable | ||||
|     if (this.filterTags.length > 0) { | ||||
|       process.env.TSTEST_FILTER_TAGS = this.filterTags.join(','); | ||||
|     } | ||||
|  | ||||
|     const execResultStreaming = await this.smartshellInstance.execStreamingSilent( | ||||
|       `tsrun ${fileNameArg}${tsrunOptions}` | ||||
|   | ||||
		Reference in New Issue
	
	Block a user