11import _ from "lodash" ;
2+ import fs from "fs-extra" ;
23import pLimit from "p-limit" ;
4+ import * as logger from "../../../utils/logger" ;
35import type { Config } from "../../../config" ;
46import type { BrowserConfig } from "../../../config/browser-config" ;
57import type { MainRunner } from "../../../runner" ;
@@ -115,6 +117,17 @@ interface SelectivityRunnerOptions {
115117 shouldDisableSelectivity ?: boolean ;
116118}
117119
120+ interface SelectivityBrowserStats {
121+ processedCount : number ;
122+ skippedCount : number ;
123+ }
124+
125+ interface SelectivityReport {
126+ totalProcessedCount : number ;
127+ totalSkippedCount : number ;
128+ perBrowserStats : Record < string , SelectivityBrowserStats > ;
129+ }
130+
118131export class SelectivityRunner {
119132 private readonly _config : Config ;
120133 private readonly _runTestFn : ( test : Test , browserId : string ) => void ;
@@ -123,6 +136,7 @@ export class SelectivityRunner {
123136 private readonly _testsToRun : [ Test , string ] [ ] = [ ] ;
124137 private readonly _processingTestLimit = pLimit ( 16 ) ;
125138 private readonly _processingTestPromises : Array < Promise < void > > = [ ] ;
139+ private readonly _stats : Record < string , SelectivityBrowserStats > = { } ;
126140
127141 static create ( ...args : ConstructorParameters < typeof this > ) : SelectivityRunner {
128142 return new this ( ...args ) ;
@@ -177,10 +191,19 @@ export class SelectivityRunner {
177191 return ;
178192 }
179193
194+ this . _stats [ browserId ] ||= {
195+ processedCount : 0 ,
196+ skippedCount : 0 ,
197+ } ;
198+
199+ this . _stats [ browserId ] . processedCount ++ ;
200+
180201 const shouldDisableTest = await shouldDisableTestBySelectivity ( browserConfig , test ) ;
181202
182203 if ( ! shouldDisableTest ) {
183204 this . _testsToRun . push ( [ test , browserId ] ) ;
205+ } else {
206+ this . _stats [ browserId ] . skippedCount ++ ;
184207 }
185208 } ) ,
186209 ) ;
@@ -189,6 +212,10 @@ export class SelectivityRunner {
189212 async runNecessaryTests ( ) : Promise < void > {
190213 await Promise . all ( this . _processingTestPromises ) ;
191214
215+ const saveReportPromise = this . _saveSelectivityReport ( ) . catch ( reason => {
216+ logger . error ( "Couldn't save selectivity report. Reason:" , reason ) ;
217+ } ) ;
218+
192219 this . _testsToRun . forEach ( ( [ test , browserId ] ) => {
193220 this . _runTestFn ( test , browserId ) ;
194221 } ) ;
@@ -204,5 +231,46 @@ export class SelectivityRunner {
204231
205232 getHashReader ( selectivity . testDependenciesPath , selectivity . compression ) . clearCache ( ) ;
206233 } ) ;
234+
235+ await saveReportPromise ;
236+ }
237+
238+ async _saveSelectivityReport ( ) : Promise < void > {
239+ const browserReportPaths = Object . keys ( this . _stats ) . reduce ( ( acc , browserId ) => {
240+ const reportPath =
241+ process . env . TESTPLANE_SELECTIVITY_REPORT_PATH ||
242+ this . _config . forBrowser ( browserId ) . selectivity . reportPath ;
243+
244+ if ( reportPath ) {
245+ acc [ reportPath ] ||= [ ] ;
246+ acc [ reportPath ] . push ( browserId ) ;
247+ }
248+
249+ return acc ;
250+ } , { } as Record < string , string [ ] > ) ;
251+
252+ const promises = await Promise . allSettled (
253+ Object . keys ( browserReportPaths ) . map ( async reportPath => {
254+ const report : SelectivityReport = {
255+ totalProcessedCount : 0 ,
256+ totalSkippedCount : 0 ,
257+ perBrowserStats : { } ,
258+ } ;
259+
260+ for ( const browserId of browserReportPaths [ reportPath ] ) {
261+ report . totalProcessedCount += this . _stats [ browserId ] . processedCount ;
262+ report . totalSkippedCount += this . _stats [ browserId ] . skippedCount ;
263+ report . perBrowserStats [ browserId ] = this . _stats [ browserId ] ;
264+ }
265+
266+ await fs . outputJson ( reportPath , report , { spaces : 4 } ) ;
267+ } ) ,
268+ ) ;
269+
270+ const failedPromise = promises . find ( promise => promise . status === "rejected" ) ;
271+
272+ if ( failedPromise && "reason" in failedPromise ) {
273+ throw failedPromise . reason ;
274+ }
207275 }
208276}
0 commit comments