1/* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { StaticSpec } from './StaticSpec'; 17import { StaticServiceIF } from '../../interface'; 18import { TAG } from '../../Constant'; 19import { Core } from '../../core'; 20import { AssertException } from './AssertException'; 21import { ConfigService } from '../config/configService'; 22import { SpecService } from './SpecService'; 23import { SuiteService } from './SuiteService'; 24 25let tempSuiteService: SuiteService = new SuiteService({ id: '' }); 26class StaticSuite { 27 public description: string; 28 public specs: Array<StaticSpec>; 29 public childSuites: Array<StaticSuite>; 30 public beforeItSpecified: Map<string | string[], (() => void) | (() => Promise<void>)>; 31 public afterItSpecified: Map<string | string[], (() => void) | (() => Promise<void>)>; 32 public duration: int; 33 public beforeAll: Set<(() => void) | (() => Promise<void>)>; 34 public afterAll: Set<(() => void) | (() => Promise<void>)>; 35 public beforeEach: Set<(() => void) | (() => Promise<void>)>; 36 public afterEach: Set<(() => void) | (() => Promise<void>)>; 37 public hookError: Error | null; 38 public isSkip: boolean; 39 public skipReason: string; 40 public isPromiseError: boolean; 41 constructor(attrs: StaticServiceIF) { 42 this.description = attrs.description; 43 this.childSuites = new Array<StaticSuite>(); 44 this.specs = new Array<StaticSpec>(); 45 this.beforeAll = new Set<(() => void) | (() => Promise<void>)>(); 46 this.afterAll = new Set<(() => void) | (() => Promise<void>)>(); 47 this.beforeEach = new Set<(() => void) | (() => Promise<void>)>(); 48 this.afterEach = new Set<(() => void) | (() => Promise<void>)>(); 49 this.beforeItSpecified = new Map<string | string[], (() => void) | (() => Promise<void>)>(); 50 this.afterItSpecified = new Map<string | string[], (() => void) | (() => Promise<void>)>(); 51 this.duration = 0; 52 this.hookError = null; 53 this.isSkip = false; 54 this.skipReason = ''; 55 this.isPromiseError = false; 56 } 57 58 pushSpec(spec: StaticSpec) { 59 this.specs.push(spec); 60 } 61 62 removeSpec(desc: string) { 63 this.specs = this.specs.filter((item: StaticSpec) => { 64 return item.description !== desc; 65 }); 66 } 67 68 getSpecsNum() { 69 return this.specs.length; 70 } 71 72 isRun(coreContext: Core): boolean { 73 const configService = coreContext.getDefaultService('config'); 74 const specService = coreContext.getDefaultService('spec'); 75 if (configService !== null && specService !== null) { 76 const cService = configService as ConfigService; 77 const sService = specService as SpecService; 78 const breakOnError: boolean = cService.isBreakOnError(); 79 const isError: boolean = sService.getStatus(); 80 return breakOnError && isError; 81 } 82 return false; 83 } 84 85 run(coreContext: Core) { 86 const sService = coreContext.getDefaultService('suite'); 87 const cService = coreContext.getDefaultService('config'); 88 if (sService !== null && cService !== null) { 89 const suiteService = sService as SuiteService; 90 const configService = cService as ConfigService; 91 suiteService.setCurrentRunningSuite(this); 92 if (this.description !== '') { 93 coreContext.fireEvents('suite', 'suiteStart'); 94 } 95 if (this.beforeAll && this.beforeAll.size > 0) { 96 this.beforeAll.forEach((func: (() => void) | (() => Promise<void>)) => { 97 try { 98 (func as () => void)(); 99 } catch (e: Error) { 100 console.error(`${TAG}${e.stack}`); 101 } 102 }); 103 } 104 const specs = this.specs; 105 if (specs.length > 0) { 106 if (configService.isRandom()) { 107 specs.sort((a: StaticSpec, b: StaticSpec) => { 108 return Math.random() > 0.5 ? -1 : 1; 109 }); 110 } 111 for (let i = 0; i < specs.length; i++) { 112 const spec = specs[i]; 113 let isBreakOnError = this.isRun(coreContext); 114 if (isBreakOnError) { 115 break; 116 } 117 if (this.beforeEach && this.beforeEach.size > 0) { 118 this.beforeEach.forEach((func: (() => void) | (() => Promise<void>)) => { 119 try { 120 (func as () => void)(); 121 } catch (e: Error) { 122 console.error(`${TAG}${e.stack}`); 123 } 124 }); 125 } 126 spec.run(coreContext); 127 128 if (this.afterEach && this.afterEach.size > 0) { 129 this.afterEach.forEach((func: (() => void) | (() => Promise<void>)) => { 130 try { 131 (func as () => void)(); 132 } catch (e: Error) { 133 console.error(`${TAG}${e.stack}`); 134 } 135 }); 136 } 137 } 138 } 139 const clength = this.childSuites.length; 140 if (clength > 0) { 141 for (let i = 0; i < clength; i++) { 142 const suite = this.childSuites[i]; 143 let isBreakOnError = this.isRun(coreContext); 144 if (isBreakOnError) { 145 break; 146 } 147 suite.run(coreContext); 148 suiteService.setCurrentRunningSuite(suite); 149 } 150 } 151 152 if (this.afterAll && this.afterAll.size > 0) { 153 this.afterAll.forEach((func: (() => void) | (() => Promise<void>)) => { 154 try { 155 (func as () => void)(); 156 } catch (e: Error) { 157 console.error(`${TAG}${e.stack}`); 158 } 159 }); 160 } 161 if (this.description !== '') { 162 coreContext.fireEvents('suite', 'suiteDone'); 163 } 164 } 165 } 166 167 async runBeforeItSpecified( 168 beforeItSpecified: Map<string | string[], (() => void) | (() => Promise<void>)>, 169 specItem: StaticSpec 170 ) { 171 try { 172 for (const itNames of beforeItSpecified.keys()) { 173 let flag = false; 174 if (typeof itNames === 'string' && (itNames as string) === specItem.description) { 175 flag = true; 176 } else if (Array.isArray(itNames)) { 177 const itNamesArr = itNames as string[]; 178 for (const name of itNamesArr) { 179 if (name === specItem.description) { 180 flag = true; 181 break; 182 } 183 } 184 } 185 if (flag) { 186 const hookFunc = beforeItSpecified.get(itNames); 187 if (hookFunc) { 188 await (hookFunc as () => Promise<void>)(); 189 } 190 break; 191 } 192 } 193 } catch (err: Error) { 194 console.error(`${TAG}${err.stack}`); 195 } 196 } 197 198 async runAfterItSpecified( 199 afterItSpecified: Map<string | string[], (() => void) | (() => Promise<void>)>, 200 specItem: StaticSpec 201 ) { 202 try { 203 for (const itNames of afterItSpecified.keys()) { 204 let flag = false; 205 if (typeof itNames === 'string' && (itNames as string) === specItem.description) { 206 flag = true; 207 } else if (Array.isArray(itNames)) { 208 const itNamesArr = itNames as string[]; 209 for (const name of itNamesArr) { 210 if (name === specItem.description) { 211 flag = true; 212 break; 213 } 214 } 215 } 216 if (flag) { 217 const hookFunc = afterItSpecified.get(itNames); 218 if (hookFunc) { 219 await (hookFunc as () => Promise<void>)(); 220 } 221 break; 222 } 223 } 224 } catch (err: Error) { 225 console.error(`${TAG}${err.stack}`); 226 227 } 228 } 229 230 async asyncRunSpecs(coreContext: Core) { 231 const cService = coreContext.getDefaultService('config'); 232 const sService = coreContext.getDefaultService('spec'); 233 if (cService !== null && sService !== null) { 234 const configService = cService as ConfigService; 235 const specService = sService as SpecService; 236 if (configService.isRandom()) { 237 this.specs.sort((a, b) => { 238 return Math.random() > 0.5 ? -1 : 1; 239 }); 240 } 241 for (let specItem of this.specs) { 242 if (specItem) { 243 specService.setCurrentRunningSpec(specItem); 244 245 let isBreakOnError = this.isRun(coreContext); 246 if (isBreakOnError) { 247 console.info('break description :' + this.description); 248 break; 249 } 250 await coreContext.fireEvents('spec', 'specStart'); 251 try { 252 await this.runBeforeItSpecified(this.beforeItSpecified, specItem); 253 for (const hookItem of this.beforeEach) { 254 if (hookItem) { 255 try { 256 await (hookItem as () => Promise<void>)(); 257 } catch (error) { 258 if (error) { 259 const err = error as Error; 260 err.message += `, error in beforeEach function`; 261 throw err; 262 } 263 } 264 } 265 } 266 await specItem.asyncRun(coreContext); 267 await this.runAfterItSpecified(this.afterItSpecified, specItem); 268 for (const hookItem of this.afterEach) { 269 try { 270 await (hookItem as () => Promise<void>)(); 271 } catch (error) { 272 if (error) { 273 const err = error as Error; 274 err.message += `, error in afterEach function`; 275 throw err; 276 } 277 } 278 } 279 } catch (err) { 280 if (err) { 281 const e = err as Error; 282 console.error(`${TAG}stack:${e.stack}`); 283 console.error(`${TAG}stack end`); 284 if (err instanceof AssertException) { 285 specItem.fail = e; 286 } else { 287 specItem.error = e; 288 } 289 } 290 specService.setStatus(true); 291 } 292 specItem.setResult(); 293 await coreContext.fireEvents('spec', 'specDone'); 294 specService.setCurrentRunningSpec(null); 295 } 296 } 297 } 298 } 299 300 async asyncRunChildSuites(coreContext: Core) { 301 for (let i = 0; i < this.childSuites.length; i++) { 302 const isBreakOnError = this.isRun(coreContext); 303 if (isBreakOnError) { 304 console.info(`${TAG}break description : ${this.description}`); 305 break; 306 } 307 await this.childSuites[i].asyncRun(coreContext); 308 } 309 } 310 311 async asyncRun(coreContext: Core) { 312 const sService = coreContext.getDefaultService('suite'); 313 const spService = coreContext.getDefaultService('spec'); 314 if (sService !== null && spService !== null) { 315 const suiteService = sService as SuiteService; 316 const specService = spService as SpecService; 317 318 suiteService.setCurrentRunningSuite(this); 319 suiteService.suitesStack.push(this); 320 if (this.description !== '') { 321 await coreContext.fireEvents('suite', 'suiteStart'); 322 } 323 324 try { 325 for (const hookItem of this.beforeAll) { 326 try { 327 await (hookItem as () => Promise<void>)(); 328 } catch (error) { 329 if (error) { 330 const err = error as Error; 331 err.message += `, error in beforeAll function`; 332 throw err; 333 } 334 } 335 } 336 } catch (error) { 337 if (error) { 338 const err = error as Error; 339 console.error(`${TAG}${err.stack}`); 340 this.hookError = err; 341 } 342 } 343 344 if (this.hookError !== null) { 345 const err = this.hookError as Error; 346 specService.setStatus(true); 347 tempSuiteService = suiteService; 348 await tempSuiteService.setSuiteResults(this, err, coreContext); 349 } 350 if (this.specs.length > 0 && this.hookError === null) { 351 await this.asyncRunSpecs(coreContext); 352 } 353 354 if (this.childSuites.length > 0 && this.hookError === null) { 355 await this.asyncRunChildSuites(coreContext); 356 } 357 358 try { 359 for (const hookItem of this.afterAll) { 360 try { 361 await (hookItem as () => Promise<void>)(); 362 } catch (error) { 363 if (error) { 364 const err = error as Error; 365 err.message += `, error in afterAll function`; 366 throw err; 367 } 368 } 369 } 370 } catch (error) { 371 if (error) { 372 console.error(`${TAG}${(error as Error).stack}`); 373 this.hookError = error as Error; 374 specService.setStatus(true); 375 } 376 } 377 if (this.description !== '') { 378 await coreContext.fireEvents('suite', 'suiteDone'); 379 const currentRunningSuite = suiteService.suitesStack.pop(); 380 if (currentRunningSuite) { 381 const curSuite = currentRunningSuite as StaticSuite; 382 suiteService.setCurrentRunningSuite(curSuite); 383 suiteService.suitesStack.push(curSuite); 384 } 385 } 386 } 387 } 388} 389 390export { StaticSuite }; 391