1/* 2 * Copyright (c) 2021-2023 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 SysTestKit from "./module/kit/SysTestKit"; 17import {TAG} from './Constant'; 18import LogExpectError from './module/report/LogExpectError' 19 20class AssertException extends Error { 21 constructor(message) { 22 super(); 23 this.name = "AssertException"; 24 this.message = message; 25 } 26} 27 28function getFuncWithArgsZero(func, timeout, isStressTest) { 29 return new Promise(async (resolve, reject) => { 30 let timer = null; 31 if (!isStressTest) { 32 timer = setTimeout(() => { 33 reject(new Error('execute timeout ' + timeout + 'ms')); 34 }, timeout); 35 } 36 try { 37 await func(); 38 } catch (err) { 39 reject(err); 40 } 41 timer !== null ? clearTimeout(timer) : null; 42 resolve(); 43 }); 44} 45 46function getFuncWithArgsOne(func, timeout, isStressTest) { 47 return new Promise(async (resolve, reject) => { 48 let timer = null; 49 if (!isStressTest) { 50 timer = setTimeout(() => { 51 reject(new Error('execute timeout ' + timeout + 'ms')); 52 }, timeout);; 53 } 54 55 function done() { 56 timer !== null ? clearTimeout(timer) : null; 57 resolve(); 58 } 59 60 try { 61 await func(done); 62 } catch (err) { 63 timer !== null ? clearTimeout(timer) : null; 64 reject(err); 65 } 66 }); 67} 68 69function getFuncWithArgsTwo(func, timeout, paramItem, isStressTest) { 70 return new Promise(async (resolve, reject) => { 71 let timer = null; 72 if (!isStressTest) { 73 timer = setTimeout(() => { 74 reject(new Error('execute timeout ' + timeout + 'ms')); 75 }, timeout); 76 } 77 78 function done() { 79 timer !== null ? clearTimeout(timer) : null; 80 resolve(); 81 } 82 83 try { 84 await func(done, paramItem); 85 } catch (err) { 86 timer !== null ? clearTimeout(timer) : null; 87 reject(err); 88 } 89 }); 90} 91 92function processFunc(coreContext, func) { 93 let argNames = ((func || '').toString() 94 .replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg, '') 95 .match(/^(function)?\s*[^\(]*\(\s*([^\)]*)\)/m) || ['', '', ''])[2] 96 .split(',') // split parameters 97 .map(item => item.replace(/^\s*(_?)(.+?)\1\s*$/, name => name.split('=')[0].trim())) 98 .filter(String); 99 let funcLen = func.length; 100 let processedFunc; 101 const config = coreContext.getDefaultService('config'); 102 config.setSupportAsync(true); 103 const timeout = + (config.timeout === undefined ? 5000 : config.timeout); 104 const isStressTest = (coreContext.getServices('dataDriver') !== undefined || config.getStress() > 1); 105 switch (funcLen) { 106 case 0: { 107 processedFunc = function () { 108 return getFuncWithArgsZero(func, timeout, isStressTest); 109 }; 110 break; 111 } 112 case 1: { 113 if (argNames[0] === 'data') { 114 processedFunc = function (paramItem) { 115 func(paramItem); 116 }; 117 } else { 118 processedFunc = function () { 119 return getFuncWithArgsOne(func, timeout, isStressTest); 120 }; 121 } 122 break; 123 } 124 default: { 125 processedFunc = function (paramItem) { 126 return getFuncWithArgsTwo(func, timeout, paramItem, isStressTest); 127 }; 128 break; 129 } 130 } 131 return processedFunc; 132} 133 134function secureRandomNumber() { 135 return crypto.randomBytes(8).readUInt32LE() / 0xffffffff; 136} 137 138class SuiteService { 139 constructor(attr) { 140 this.id = attr.id; 141 this.rootSuite = new SuiteService.Suite({}); 142 this.currentRunningSuite = this.rootSuite; 143 this.suitesStack = [this.rootSuite]; 144 } 145 146 describe(desc, func) { 147 const configService = this.coreContext.getDefaultService('config'); 148 if (configService.filterSuite(desc)) { 149 console.info(`${TAG}filter suite : ${desc}`); 150 return; 151 } 152 const suite = new SuiteService.Suite({description: desc}); 153 if (typeof this.coreContext.getServices('dataDriver') !== 'undefined' && configService['dryRun'] !== 'true') { 154 let suiteStress = this.coreContext.getServices('dataDriver').dataDriver.getSuiteStress(desc); 155 for (let i = 1; i < suiteStress; i++) { 156 this.currentRunningSuite.childSuites.push(suite); 157 } 158 } 159 this.currentRunningSuite.childSuites.push(suite); 160 this.currentRunningSuite = suite; 161 this.suitesStack.push(suite); 162 func.call(); 163 let childSuite = this.suitesStack.pop(); 164 if (this.suitesStack.length === 0) { 165 this.currentRunningSuite = childSuite; 166 this.suitesStack.push(childSuite); 167 } 168 if (this.suitesStack.length > 1) { 169 this.currentRunningSuite = this.suitesStack.pop(); 170 } else { 171 this.currentRunningSuite = this.suitesStack.pop(); 172 this.suitesStack.push(this.currentRunningSuite); 173 } 174 } 175 176 beforeAll(func) { 177 this.currentRunningSuite.beforeAll.push(processFunc(this.coreContext, func)); 178 } 179 180 beforeEach(func) { 181 this.currentRunningSuite.beforeEach.push(processFunc(this.coreContext, func)); 182 } 183 184 beforeItSpecified(itDescs, func) { 185 this.currentRunningSuite.beforeItSpecified.set(itDescs, processFunc(this.coreContext, func)); 186 } 187 188 afterItSpecified(itDescs, func) { 189 this.currentRunningSuite.afterItSpecified.set(itDescs, processFunc(this.coreContext, func)); 190 } 191 192 afterAll(func) { 193 this.currentRunningSuite.afterAll.push(processFunc(this.coreContext, func)); 194 } 195 196 afterEach(func) { 197 this.currentRunningSuite.afterEach.push(processFunc(this.coreContext, func)); 198 } 199 200 getCurrentRunningSuite() { 201 return this.currentRunningSuite; 202 } 203 204 setCurrentRunningSuite(suite) { 205 this.currentRunningSuite = suite; 206 } 207 208 traversalResults(suite, obj, breakOnError) { 209 if (suite.childSuites.length === 0 && suite.specs.length === 0) { 210 return obj; 211 } 212 if (suite.specs.length > 0) { 213 for (const itItem of suite.specs) { 214 obj.total++; 215 if (breakOnError && (obj.error > 0 || obj.failure > 0)) { // breakOnError模式 216 continue; 217 } 218 if (itItem.error) { 219 obj.error++; 220 } else if (itItem.fail) { 221 obj.failure++; 222 } else if (itItem.pass === true) { 223 obj.pass++; 224 } 225 } 226 } 227 228 obj.duration += suite.duration; 229 230 if (suite.childSuites.length > 0) { 231 for (const suiteItem of suite.childSuites) { 232 this.traversalResults(suiteItem, obj, breakOnError); 233 } 234 } 235 } 236 237 async setSuiteResults(suite, error, coreContext) { 238 if (suite.childSuites.length === 0 && suite.specs.length === 0) { 239 return obj; 240 } 241 if (suite.specs.length > 0) { 242 const specService = coreContext.getDefaultService('spec'); 243 for (const specItem of suite.specs) { 244 specService.setCurrentRunningSpec(specItem); 245 if (error instanceof AssertException) { 246 specItem.fail = error; 247 } else { 248 specItem.error = error; 249 } 250 await coreContext.fireEvents('spec', 'specStart', specItem); 251 await coreContext.fireEvents('spec', 'specDone', specItem); 252 } 253 } 254 if (suite.childSuites.length > 0) { 255 for (const suiteItem of suite.childSuites) { 256 await this.setSuiteResults(suiteItem, error, coreContext); 257 } 258 } 259 } 260 261 getSummary() { 262 let suiteService = this.coreContext.getDefaultService('suite'); 263 let rootSuite = suiteService.rootSuite; 264 const specService = this.coreContext.getDefaultService('spec'); 265 const configService = this.coreContext.getDefaultService('config'); 266 let breakOnError = configService.isBreakOnError(); 267 let isError = specService.getStatus(); 268 let isBreaKOnError = breakOnError && isError; 269 let obj = {total: 0, failure: 0, error: 0, pass: 0, ignore: 0, duration: 0}; 270 for (const suiteItem of rootSuite.childSuites) { 271 this.traversalResults(suiteItem, obj, isBreaKOnError); 272 } 273 obj.ignore = obj.total - obj.pass - obj.failure - obj.error; 274 return obj; 275 } 276 277 init(coreContext) { 278 this.coreContext = coreContext; 279 } 280 281 traversalSuites(suite, obj, configService) { 282 if (suite.childSuites.length === 0 && suite.specs.length === 0) { 283 return []; 284 } 285 if (suite.specs.length > 0) { 286 let itArray = []; 287 for (const itItem of suite['specs']) { 288 if (!configService.filterDesc(suite.description, itItem.description, itItem.fi, null)) { 289 itArray.push({'itName': itItem.description}); 290 } 291 } 292 obj[suite.description] = itArray; 293 } 294 295 if (suite.childSuites.length > 0) { 296 let suiteArray = []; 297 for (const suiteItem of suite.childSuites) { 298 let suiteObj = {}; 299 this.traversalSuites(suiteItem, suiteObj, configService); 300 if (!configService.filterSuite(suiteItem.description)) { 301 suiteArray.push(suiteObj); 302 } 303 } 304 obj.suites = suiteArray; 305 } 306 } 307 308 async dryRun(abilityDelegator) { 309 const configService = this.coreContext.getDefaultService('config'); 310 let testSuitesObj = {}; 311 let suitesArray = []; 312 for (const suiteItem of this.rootSuite.childSuites) { 313 let obj = {}; 314 this.traversalSuites(suiteItem, obj, configService); 315 if (!configService.filterSuite(suiteItem.description)) { 316 suitesArray.push(obj); 317 } 318 } 319 testSuitesObj['suites'] = suitesArray; 320 321 let strJson = JSON.stringify(testSuitesObj); 322 let strLen = strJson.length; 323 let maxLen = 500; 324 let maxCount = Math.floor(strLen / maxLen); 325 326 for (let count = 0; count <= maxCount; count++) { 327 await SysTestKit.print(strJson.substring(count * maxLen, (count + 1) * maxLen)); 328 } 329 console.info(`${TAG}dryRun print success`); 330 abilityDelegator.finishTest('dry run finished!!!', 0, () => { }); 331 } 332 333 execute() { 334 const configService = this.coreContext.getDefaultService('config'); 335 if (configService.filterValid.length !== 0) { 336 this.coreContext.fireEvents('task', 'incorrectFormat'); 337 return; 338 } 339 340 if (configService.isRandom() && this.rootSuite.childSuites.length > 0) { 341 this.rootSuite.childSuites.sort(function () { 342 return Math.random().toFixed(1) > 0.5 ? -1 : 1; 343 }); 344 this.currentRunningSuite = this.rootSuite.childSuites[0]; 345 } 346 347 if (configService.isSupportAsync()) { 348 let asyncExecute = async () => { 349 await this.coreContext.fireEvents('task', 'taskStart'); 350 await this.rootSuite.asyncRun(this.coreContext); 351 }; 352 asyncExecute().then(async () => { 353 await this.coreContext.fireEvents('task', 'taskDone'); 354 }); 355 } else { 356 this.coreContext.fireEvents('task', 'taskStart'); 357 this.rootSuite.run(this.coreContext); 358 this.coreContext.fireEvents('task', 'taskDone'); 359 } 360 } 361 362 apis() { 363 const _this = this; 364 return { 365 describe: function (desc, func) { 366 return _this.describe(desc, func); 367 }, 368 beforeItSpecified: function (itDescs, func) { 369 return _this.beforeItSpecified(itDescs, func); 370 }, 371 afterItSpecified: function (itDescs, func) { 372 return _this.afterItSpecified(itDescs, func); 373 }, 374 beforeAll: function (func) { 375 return _this.beforeAll(func); 376 }, 377 beforeEach: function (func) { 378 return _this.beforeEach(func); 379 }, 380 afterAll: function (func) { 381 return _this.afterAll(func); 382 }, 383 afterEach: function (func) { 384 return _this.afterEach(func); 385 } 386 }; 387 } 388} 389 390SuiteService.Suite = class { 391 constructor(attrs) { 392 this.description = attrs.description || ''; 393 this.childSuites = []; 394 this.specs = []; 395 this.beforeAll = []; 396 this.afterAll = []; 397 this.beforeItSpecified = new Map(); 398 this.afterItSpecified = new Map(); 399 this.beforeEach = []; 400 this.afterEach = []; 401 this.duration = 0; 402 this.hookError = null; 403 } 404 405 pushSpec(spec) { 406 this.specs.push(spec); 407 } 408 409 removeSpec(desc) { 410 this.specs = this.specs.filter((item, index) => { 411 return item.description !== desc; 412 }); 413 } 414 415 getSpecsNum() { 416 return this.specs.length; 417 } 418 419 isRun(coreContext) { 420 const configService = coreContext.getDefaultService('config'); 421 const suiteService = coreContext.getDefaultService('suite'); 422 const specService = coreContext.getDefaultService('spec'); 423 let breakOnError = configService.isBreakOnError(); 424 let isError = specService.getStatus(); 425 return breakOnError && isError; 426 } 427 428 run(coreContext) { 429 const suiteService = coreContext.getDefaultService('suite'); 430 suiteService.setCurrentRunningSuite(this); 431 if (this.description !== '') { 432 coreContext.fireEvents('suite', 'suiteStart', this); 433 } 434 this.runHookFunc('beforeAll'); 435 if (this.specs.length > 0) { 436 const configService = coreContext.getDefaultService('config'); 437 if (configService.isRandom()) { 438 this.specs.sort(function () { 439 return Math.random().toFixed(1) > 0.5 ? -1 : 1; 440 }); 441 } 442 for (let spec in this.specs) { 443 let isBreakOnError = this.isRun(coreContext); 444 if (isBreakOnError) { 445 break; 446 } 447 this.runHookFunc('beforeEach'); 448 spec.run(coreContext); 449 this.runHookFunc('afterEach'); 450 } 451 } 452 if (this.childSuites.length > 0) { 453 for (let suite in this.childSuites) { 454 let isBreakOnError = this.isRun(coreContext); 455 if (isBreakOnError) { 456 break; 457 } 458 suite.run(coreContext); 459 suiteService.setCurrentRunningSuite(suite); 460 } 461 } 462 this.runHookFunc('afterAll'); 463 if (this.description !== '') { 464 coreContext.fireEvents('suite', 'suiteDone'); 465 } 466 } 467 468 async asyncRunSpecs(coreContext) { 469 const configService = coreContext.getDefaultService('config'); 470 if (configService.isRandom()) { 471 this.specs.sort(function () { 472 return Math.random().toFixed(1) > 0.5 ? -1 : 1; 473 }); 474 } 475 const specService = coreContext.getDefaultService('spec'); 476 for (let specItem of this.specs) { 477 specService.setCurrentRunningSpec(specItem); 478 // 遇错即停模式,发现用例有问题,直接返回,不在执行后面的it 479 let isBreakOnError = this.isRun(coreContext); 480 if (isBreakOnError) { 481 console.log("break description :" + this.description); 482 break; 483 } 484 await coreContext.fireEvents('spec', 'specStart', specItem); 485 try { 486 for (const [itNames, hookFunc] of this.beforeItSpecified) { 487 if ((Object.prototype.toString.call(itNames) === '[object Array]' && itNames.includes(specItem.description)) || 488 (Object.prototype.toString.call(itNames) === '[object String]' && itNames === specItem.description)) { 489 await Reflect.apply(hookFunc, null, []); 490 } 491 break; 492 } 493 await this.runAsyncHookFunc('beforeEach'); 494 await specItem.asyncRun(coreContext); 495 for (const [itNames, hookFunc] of this.afterItSpecified) { 496 if ((Object.prototype.toString.call(itNames) === '[object Array]' && itNames.includes(specItem.description)) || 497 (Object.prototype.toString.call(itNames) === '[object String]' && itNames === specItem.description)) { 498 await Reflect.apply(hookFunc, null, []); 499 } 500 break; 501 } 502 await this.runAsyncHookFunc('afterEach'); 503 } catch (e) { 504 console.error(`${TAG}stack:${e?.stack}`); 505 console.error(`${TAG}stack end`); 506 if (e instanceof AssertException) { 507 specItem.fail = e; 508 } else { 509 specItem.error = e; 510 } 511 specService.setStatus(true); 512 } 513 specItem.setResult(); 514 await coreContext.fireEvents('spec', 'specDone', specItem); 515 specService.setCurrentRunningSpec(null); 516 } 517 } 518 519 async asyncRunChildSuites(coreContext) { 520 for (let i = 0; i < this.childSuites.length; i++) { 521 // 遇错即停模式, 发现用例有问题,直接返回,不在执行后面的description 522 let isBreakOnError = this.isRun(coreContext); 523 if (isBreakOnError) { 524 console.log(`${TAG}break description : ${this.description}`); 525 break; 526 } 527 await this.childSuites[i].asyncRun(coreContext); 528 } 529 } 530 531 async asyncRun(coreContext) { 532 const suiteService = coreContext.getDefaultService('suite'); 533 const specService = coreContext.getDefaultService('spec'); 534 535 suiteService.setCurrentRunningSuite(this); 536 suiteService.suitesStack.push(this); 537 if (this.description !== '') { 538 await coreContext.fireEvents('suite', 'suiteStart', this); 539 } 540 541 try { 542 await this.runAsyncHookFunc('beforeAll'); 543 } catch (error) { 544 console.error(`${TAG}${error?.stack}`); 545 this.hookError = error; 546 } 547 548 if (this.hookError !== null) { 549 specService.setStatus(true); 550 await suiteService.setSuiteResults(this, this.hookError, coreContext); 551 } 552 553 if (this.specs.length > 0 && this.hookError === null) { 554 await this.asyncRunSpecs(coreContext); 555 } 556 557 if (this.childSuites.length > 0 && this.hookError === null) { 558 await this.asyncRunChildSuites(coreContext); 559 } 560 561 try { 562 await this.runAsyncHookFunc('afterAll'); 563 } catch (error) { 564 console.error(`${TAG}${error?.stack}`); 565 this.hookError = error; 566 specService.setStatus(true); 567 } 568 569 if (this.description !== '') { 570 await coreContext.fireEvents('suite', 'suiteDone'); 571 let childSuite = suiteService.suitesStack.pop(); 572 if (suiteService.suitesStack.length === 0) { 573 suiteService.suitesStack.push(childSuite); 574 } 575 if (suiteService.suitesStack.length > 1) { 576 suiteService.setCurrentRunningSuite(suiteService.suitesStack.pop()); 577 } else { 578 let currentRunningSuite = suiteService.suitesStack.pop(); 579 suiteService.setCurrentRunningSuite(currentRunningSuite); 580 suiteService.suitesStack.push(currentRunningSuite); 581 } 582 } 583 } 584 585 runHookFunc(hookName) { 586 if (this[hookName] && this[hookName].length > 0) { 587 this[hookName].forEach(func => { 588 try { 589 func(); 590 } catch (e) { 591 console.error(`${TAG}${e.stack}`); 592 } 593 }); 594 } 595 } 596 597 async runAsyncHookFunc(hookName) { 598 for (const hookItem of this[hookName]) { 599 try { 600 await hookItem(); 601 } catch (error) { 602 error['message'] += `, error in ${hookName} function`; 603 throw error; 604 } 605 606 } 607 } 608}; 609 610class SpecService { 611 constructor(attr) { 612 this.id = attr.id; 613 this.totalTest = 0; 614 this.hasError = false; 615 } 616 617 init(coreContext) { 618 this.coreContext = coreContext; 619 } 620 621 setCurrentRunningSpec(spec) { 622 this.currentRunningSpec = spec; 623 } 624 625 setStatus(obj) { 626 this.hasError = obj; 627 } 628 629 getStatus() { 630 return this.hasError; 631 } 632 633 getTestTotal() { 634 return this.totalTest; 635 } 636 637 getCurrentRunningSpec() { 638 return this.currentRunningSpec; 639 } 640 641 it(desc, filter, func) { 642 const configService = this.coreContext.getDefaultService('config'); 643 const currentSuiteName = this.coreContext.getDefaultService('suite').getCurrentRunningSuite().description; 644 if (configService.filterDesc(currentSuiteName, desc, filter, this.coreContext)) { 645 console.info(`${TAG}filter it :${desc}`); 646 } else { 647 let processedFunc = processFunc(this.coreContext, func); 648 const spec = new SpecService.Spec({description: desc, fi: filter, fn: processedFunc}); 649 const suiteService = this.coreContext.getDefaultService('suite'); 650 if (typeof this.coreContext.getServices('dataDriver') !== 'undefined' && configService['dryRun'] !== 'true') { 651 let specStress = this.coreContext.getServices('dataDriver').dataDriver.getSpecStress(desc); 652 for (let i = 1; i < specStress; i++) { 653 this.totalTest++; 654 suiteService.getCurrentRunningSuite().pushSpec(spec); 655 } 656 } 657 658 // dryRun 状态下不统计压力测试重复数据 659 if (configService['dryRun'] !== 'true') { 660 let stress = configService.getStress(); // 命令配置压力测试 661 console.info(`${TAG}stress length : ${stress}`); 662 for (let i = 1; i < stress; i++) { 663 this.totalTest++; 664 suiteService.getCurrentRunningSuite().pushSpec(spec); 665 } 666 } 667 this.totalTest++; 668 suiteService.getCurrentRunningSuite().pushSpec(spec); 669 } 670 } 671 672 apis() { 673 const _this = this; 674 return { 675 it: function (desc, filter, func) { 676 return _this.it(desc, filter, func); 677 } 678 }; 679 } 680} 681 682SpecService.Spec = class { 683 constructor(attrs) { 684 this.description = attrs.description || ''; 685 this.fi = attrs.fi; 686 this.fn = attrs.fn || function () { 687 }; 688 this.fail = undefined; 689 this.error = undefined; 690 this.duration = 0; 691 this.startTime = 0; 692 this.isExecuted = false; // 当前用例是否执行 693 } 694 695 setResult() { 696 if (this.fail) { 697 this.pass = false; 698 } else { 699 this.pass = true; 700 } 701 } 702 703 run(coreContext) { 704 const specService = coreContext.getDefaultService('spec'); 705 specService.setCurrentRunningSpec(this); 706 coreContext.fireEvents('spec', 'specStart', this); 707 this.isExecuted = true; 708 try { 709 let dataDriver = coreContext.getServices('dataDriver'); 710 if (typeof dataDriver === 'undefined') { 711 this.fn(); 712 } else { 713 let suiteParams = dataDriver.dataDriver.getSuiteParams(); 714 let specParams = dataDriver.dataDriver.getSpecParams(); 715 console.info(`${TAG}[suite params] ${JSON.stringify(suiteParams)}`); 716 console.info(`${TAG}[spec params] ${JSON.stringify(specParams)}`); 717 if (this.fn.length === 0) { 718 this.fn(); 719 } else if (specParams.length === 0) { 720 this.fn(suiteParams); 721 } else { 722 specParams.forEach(paramItem => this.fn(Object.assign({}, paramItem, suiteParams))); 723 } 724 } 725 this.setResult(); 726 } catch (e) { 727 this.error = e; 728 specService.setStatus(true); 729 } 730 coreContext.fireEvents('spec', 'specDone', this); 731 } 732 733 async asyncRun(coreContext) { 734 const dataDriver = coreContext.getServices('dataDriver'); 735 if (typeof dataDriver === 'undefined') { 736 await this.fn(); 737 } else { 738 const suiteParams = dataDriver.dataDriver.getSuiteParams(); 739 const specParams = dataDriver.dataDriver.getSpecParams(); 740 console.info(`[suite params] ${JSON.stringify(suiteParams)}`); 741 console.info(`[spec params] ${JSON.stringify(specParams)}`); 742 if (this.fn.length === 0) { 743 await this.fn(); 744 } else if (specParams.length === 0) { 745 await this.fn(suiteParams); 746 } else { 747 for (const paramItem of specParams) { 748 await this.fn(Object.assign({}, paramItem, suiteParams)); 749 } 750 } 751 } 752 753 this.isExecuted = true; 754 } 755 756 filterCheck(coreContext) { 757 const specService = coreContext.getDefaultService('spec'); 758 specService.setCurrentRunningSpec(this); 759 return true; 760 } 761}; 762 763class ExpectService { 764 constructor(attr) { 765 this.id = attr.id; 766 this.matchers = {}; 767 } 768 769 expect(actualValue) { 770 return this.wrapMatchers(actualValue); 771 } 772 773 init(coreContext) { 774 this.coreContext = coreContext; 775 this.addMatchers(this.basicMatchers()); 776 } 777 778 addMatchers(matchers) { 779 for (const matcherName in matchers) { 780 if (Object.prototype.hasOwnProperty.call(matchers, matcherName)) { 781 this.matchers[matcherName] = matchers[matcherName]; 782 } 783 } 784 } 785 786 basicMatchers() { 787 return { 788 assertTrue: function (actualValue) { 789 return { 790 pass: (actualValue) === true, 791 message: 'expect true, actualValue is ' + actualValue 792 }; 793 }, 794 assertEqual: function (actualValue, args) { 795 let msg = 'expect ' + actualValue + ' equals ' + args[0]; 796 if(actualValue == args[0]) { // 数值相同,提示数据类型 797 const aClassName = Object.prototype.toString.call(actualValue); 798 const bClassName = Object.prototype.toString.call(args[0]); 799 msg = 'expect ' + actualValue + aClassName + ' equals ' + args[0] + bClassName + "strict mode inspect type"; 800 } 801 return { 802 pass: (actualValue) === args[0], 803 expectValue: args[0], 804 message: msg 805 }; 806 }, 807 assertThrow: function (actual, args) { 808 const result = { 809 pass: false 810 }; 811 if (typeof actual !== 'function') { 812 result.message = 'toThrow\'s Actual should be a Function'; 813 } else { 814 let hasThrow = false; 815 let throwError; 816 try { 817 actual(); 818 } catch (e) { 819 hasThrow = true; 820 throwError = e; 821 } 822 if (!hasThrow) { 823 result.message = 'function did not throw an exception'; 824 } else if (throwError && throwError.message === args[0]) { 825 result.pass = true; 826 } else { 827 result.message = `expect to throw ${args[0]} , actual throw ${throwError.message}`; 828 } 829 } 830 return result; 831 } 832 }; 833 } 834 835 wrapMatchers(actualValue) { 836 const _this = this; 837 const wrappedMatchers = { 838 // 翻转标识 839 isNot: false, 840 841 // 翻转方法 842 not: function () { 843 this.isNot = true; 844 return this; 845 } 846 }; 847 const specService = _this.coreContext.getDefaultService('spec'); 848 const currentRunningSpec = specService.getCurrentRunningSpec(); 849 const currentRunningSuite = _this.coreContext.getDefaultService('suite').getCurrentRunningSuite(); 850 for (const matcherName in this.matchers) { 851 let result = Object.prototype.hasOwnProperty.call(this.matchers, matcherName); 852 if (!result) { 853 continue; 854 } 855 if (matcherName.search('assertPromise') == 0) { 856 wrappedMatchers[matcherName] = async function () { 857 await _this.matchers[matcherName](actualValue, arguments).then(function (result) { 858 if (wrappedMatchers.isNot) { 859 result.pass = !result.pass; 860 } 861 result.actualValue = actualValue; 862 result.checkFunc = matcherName; 863 if (!result.pass) { 864 const assertError = new AssertException(result.message); 865 currentRunningSpec ? currentRunningSpec.fail = assertError : currentRunningSuite.hookError = assertError; 866 throw assertError; 867 } 868 }); 869 }; 870 } else { 871 wrappedMatchers[matcherName] = function () { 872 const result = _this.matchers[matcherName](actualValue, arguments); 873 if (wrappedMatchers.isNot) { 874 result.pass = !result.pass; 875 result.message = LogExpectError.getErrorMsg(matcherName, actualValue, arguments[0], result.message); 876 } 877 result.actualValue = actualValue; 878 result.checkFunc = matcherName; 879 if (!result.pass) { 880 const assertError = new AssertException(result.message); 881 currentRunningSpec ? currentRunningSpec.fail = assertError : currentRunningSuite.hookError = assertError; 882 throw assertError; 883 } 884 }; 885 } 886 } 887 return wrappedMatchers; 888 } 889 890 apis() { 891 const _this = this; 892 return { 893 expect: function (actualValue) { 894 return _this.expect(actualValue); 895 } 896 }; 897 } 898} 899 900class ReportService { 901 constructor(attr) { 902 this.id = attr.id; 903 } 904 905 init(coreContext) { 906 this.coreContext = coreContext; 907 this.specService = this.coreContext.getDefaultService('spec'); 908 this.suiteService = this.coreContext.getDefaultService('suite'); 909 this.duration = 0; 910 } 911 912 taskStart() { 913 console.info(`${TAG}[start] start run suites`); 914 } 915 916 async suiteStart() { 917 console.info(`${TAG}[suite start]${this.suiteService.getCurrentRunningSuite().description}`); 918 } 919 920 async specStart() { 921 console.info(`${TAG}start running case '${this.specService.currentRunningSpec.description}'`); 922 this.index = this.index + 1; 923 let spec = this.specService.currentRunningSpec; 924 spec.startTime = await SysTestKit.getRealTime(); 925 } 926 927 async specDone() { 928 let msg = ''; 929 let spec = this.specService.currentRunningSpec; 930 let suite = this.suiteService.currentRunningSuite; 931 spec.duration = await SysTestKit.getRealTime() - spec.startTime; 932 suite.duration += spec.duration; 933 if (spec.error) { 934 this.formatPrint('error', spec.description + ' ; consuming ' + spec.duration + 'ms'); 935 this.formatPrint('errorDetail', spec.error); 936 } else if (spec.fail) { 937 this.formatPrint('fail', spec.description + ' ; consuming ' + spec.duration + 'ms'); 938 this.formatPrint('failDetail', spec.fail?.message); 939 } else { 940 this.formatPrint('pass', spec.description + ' ; consuming ' + spec.duration + 'ms'); 941 } 942 this.formatPrint(this.specService.currentRunningSpec.error, msg); 943 } 944 945 suiteDone() { 946 let suite = this.suiteService.currentRunningSuite; 947 let message = suite.hookError ? `, ${suite.hookError?.message}` : ''; 948 console.info(`[suite end] ${suite.description} consuming ${suite.duration} ms${message}`); 949 } 950 951 taskDone() { 952 let msg = ''; 953 let summary = this.suiteService.getSummary(); 954 msg = 'total cases:' + summary.total + ';failure ' + summary.failure + ',' + 'error ' + summary.error; 955 msg += ',pass ' + summary.pass + '; consuming ' + summary.duration + 'ms'; 956 console.info(`${TAG}${msg}`); 957 console.info(`${TAG}[end] run suites end`); 958 } 959 960 incorrectFormat() { 961 if (this.coreContext.getDefaultService('config').filterValid.length !== 0) { 962 this.coreContext.getDefaultService('config').filterValid.forEach(function (item) { 963 console.info(`${TAG}this param ${item} is invalid`); 964 }); 965 } 966 } 967 968 formatPrint(type, msg) { 969 switch (type) { 970 case 'pass': 971 console.info(`${TAG}[pass]${msg}`); 972 break; 973 case 'fail': 974 console.info(`${TAG}[fail]${msg}`); 975 break; 976 case 'failDetail': 977 console.info(`${TAG}[failDetail]${msg}`); 978 break; 979 case 'error': 980 console.info(`${TAG}[error]${msg}`); 981 break; 982 case 'errorDetail': 983 console.info(`${TAG}[errorDetail]${msg}`); 984 break; 985 } 986 } 987 988 sleep(numberMillis) { 989 var now = new Date(); 990 var exitTime = now.getTime() + numberMillis; 991 while (true) { 992 now = new Date(); 993 if (now.getTime() > exitTime) { 994 return; 995 } 996 } 997 } 998} 999 1000export { 1001 SuiteService, 1002 SpecService, 1003 ExpectService, 1004 ReportService 1005}; 1006