• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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