1/* 2 * Copyright (c) 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 type { SourceFile } from 'typescript'; 17import { SyntaxKind } from 'typescript'; 18import { firstCharacterToUppercase, getClassNameSet, specialType } from '../common/commonUtils'; 19import type { ReturnTypeEntity } from '../common/commonUtils'; 20import { getImportDeclarationArray } from '../declaration-node/importAndExportDeclaration'; 21import type { ImportElementEntity } from '../declaration-node/importAndExportDeclaration'; 22import type { MethodEntity } from '../declaration-node/methodDeclaration'; 23import type { FunctionEntity } from '../declaration-node/functionDeclaration'; 24import type { MethodSignatureEntity } from '../declaration-node/methodSignatureDeclaration'; 25 26const repeatReturn = `} else { 27 return { 28 done: true 29 }; 30 }`; 31 32const repeatString = `index++; 33 return { 34 value: returnValue, 35 done: false 36 }; 37 ${repeatReturn} 38 } 39 };`; 40 41const iteratorEntriesMock = ` 42 let index = 0; 43 const IteratorEntriesMock = { 44 *[Symbol.iterator]() { 45 yield ['[PC Preview] unknown paramIterMock_K', '[PC Preview] unknown paramIterMock_V']; 46 }, 47 next: () => { 48 if (index < 1) { 49 const returnValue = ['[PC Previwe] unknown paramIterMock_K', '[PC Previwe] unknown paramIterMock_V']; 50 ${repeatString} 51 return IteratorEntriesMock; 52`; 53 54const iteratorStringMock = ` 55 let index = 0; 56 const IteratorStringMock = { 57 *[Symbol.iterator]() { 58 yield '[PC Preview] unknown string'; 59 }, 60 next: () => { 61 if (index < 1) { 62 const returnValue = '[PC Previwe] unknown string'; 63 ${repeatString} 64 return IteratorStringMock; 65`; 66 67/** 68 * get warn console template 69 * @param interfaceNameOrClassName 70 * @param functionNameOrPropertyName 71 * @returns 72 */ 73export function getWarnConsole(interfaceNameOrClassName: string, functionNameOrPropertyName: string): string { 74 return `console.warn('The ${interfaceNameOrClassName}.${functionNameOrPropertyName} interface in the Previewer is a mocked implementation and may behave differently than on a real device.');\n`; 75} 76 77function handlePromiseParams(returnType: ReturnTypeEntity): string { 78 const returnKindName = returnType.returnKindName.slice(0, returnType.returnKindName.length - 1).slice(8).trim(); 79 let returnName = `return new Promise((resolve, reject) => { 80 resolve('[PC Preview] unknown type'); 81 })`; 82 Object.keys(paramsTypeStart).forEach(key => { 83 if (returnKindName.startsWith(key)) { 84 const data = paramsTypeStart[key] === '[PC Preview] unknown type' ? `'${paramsTypeStart[key]}'` : `${paramsTypeStart[key]}`; 85 returnName = `return new Promise((resolve, reject) => { 86 resolve(${data}); 87 })`; 88 } 89 }); 90 return returnName; 91} 92 93/** 94 * generate return statement; 95 * @param returnType 96 * @param sourceFile 97 * @returns 98 */ 99export function getReturnStatement(returnType: ReturnTypeEntity, sourceFile: SourceFile): string { 100 if (returnType.returnKind === SyntaxKind.TypeReference) { 101 return handleTypeReferenceReturnBody(returnType, sourceFile); 102 } else if (returnType.returnKind === SyntaxKind.UnionType) { 103 return handleUnionTypeReturnBody(returnType); 104 } 105 let returnName = returnType.returnKindName.trim(); 106 let temp = true; 107 if (returnName.endsWith(']')) { 108 returnName = '[]'; 109 temp = false; 110 } else { 111 Object.keys(paramsTypeStart).forEach(key => { 112 if (returnType.returnKindName.startsWith(key)) { 113 returnName = paramsTypeStart[key]; 114 temp = false; 115 } 116 }); 117 } 118 if (temp) { 119 return 'return \'[PC Preview] unknown type\''; 120 } 121 return `return ${returnName};`; 122} 123 124/** 125 * TypeReference return statement; 126 * @param returnType 127 * @param sourceFile 128 * @returns 129 */ 130function handleTypeReferenceReturnBody(returnType: ReturnTypeEntity, sourceFile: SourceFile): string { 131 if (returnType.returnKindName.startsWith('Promise')) { 132 return handlePromiseParams(returnType); 133 } 134 const judgmentResult = judgmentReturnBody(returnType); 135 if (judgmentResult !== '') { 136 return judgmentResult; 137 } 138 const substepResult = substepReturnBody(returnType); 139 if (substepResult !== '') { 140 return substepResult; 141 } 142 if (returnType.returnKindName.includes('<')) { 143 return returnType.returnKindName.includes(',') 144 ? 'return {};' 145 : `return new ${returnType.returnKindName.split('<')[0]}()`; 146 } else { 147 return generateReturnType(returnType, sourceFile); 148 } 149} 150 151/** 152 * generate return type 153 * @param returnType 154 * @param sourceFile 155 * @returns 156 */ 157function generateReturnType(returnType: ReturnTypeEntity, sourceFile: SourceFile): string { 158 if (getClassNameSet().has(returnType.returnKindName) && !specialType.includes(returnType.returnKindName)) { 159 return returnType.returnKindName === 'Want' 160 ? 'return mockWant().Want' 161 : `return new ${returnType.returnKindName}()`; 162 } else if (getClassNameSet().has(returnType.returnKindName) && specialType.includes(returnType.returnKindName)) { 163 return `return ${returnType.returnKindName}`; 164 } else if (propertyTypeWhiteList(returnType.returnKindName) === returnType.returnKindName) { 165 return `return ${getTheRealReferenceFromImport(sourceFile, returnType.returnKindName)}`; 166 } else { 167 return `return ${propertyTypeWhiteList(returnType.returnKindName)}`; 168 } 169} 170 171function judgmentReturnBody(returnType: ReturnTypeEntity): string { 172 if (returnType.returnKindName === 'T') { 173 return 'return \'[PC Preview] unknown type\''; 174 } else if (returnType.returnKindName === 'object' || returnType.returnKindName === 'Object') { 175 return 'return {}'; 176 } else if (returnType.returnKindName === 'Function') { 177 return 'return \'[PC Preview] unknown type\''; 178 } else if (returnType.returnKindName === 'String' || returnType.returnKindName === 'string') { 179 return `return ${returnType.returnKindName}(...args)`; 180 } else if (returnType.returnKindName === 'number' || returnType.returnKindName === 'Number') { 181 return 'return 0'; 182 } else if (returnType.returnKindName === 'boolean' || returnType.returnKindName === 'Boolean') { 183 return 'return false'; 184 } else if (returnType.returnKindName === 'ArrayBuffer') { 185 return `return new ${returnType.returnKindName}(0)`; 186 } else { 187 return ''; 188 } 189} 190 191function substepReturnBody(returnType: ReturnTypeEntity): string { 192 if (returnType.returnKindName.startsWith('Array')) { 193 if (returnType.returnKindName.includes('<') && returnType.returnKindName.includes('>')) { 194 return `return [${generateGenericTypeToMockValue(returnType.returnKindName)}]`; 195 } else { 196 return `return new ${returnType.returnKindName}()`; 197 } 198 } else if (returnType.returnKindName.startsWith('Readonly')) { 199 return `return ${returnType.returnKindName.split('<')[1].split('>')[0]}`; 200 } else if (checkIsGenericSymbol(returnType.returnKindName)) { 201 return `return '[PC Preview] unknown iterableiterator_${returnType.returnKindName}'`; 202 } else if (returnType.returnKindName.startsWith('Uint8Array')) { 203 return `return new ${returnType.returnKindName}()`; 204 } else if (returnType.returnKindName.startsWith('IterableIterator')) { 205 return returnType.returnKindName.includes(',') ? iteratorEntriesMock : iteratorStringMock; 206 } else if (returnType.returnKindName.includes('<T>')) { 207 const tmpReturn = returnType.returnKindName.split('<')[0]; 208 return tmpReturn.startsWith('Array') ? 'return []' : 'return {}'; 209 } else { 210 return ''; 211 } 212} 213 214/** 215 * UnionType return statement; 216 * @param returnType 217 * @returns 218 */ 219function handleUnionTypeReturnBody(returnType: ReturnTypeEntity): string { 220 const returnNames = returnType.returnKindName.split('|'); 221 let returnName = returnNames[0]; 222 for (let i = 0; i < returnNames.length; i++) { 223 if (!returnNames[i].includes('[]') && !returnNames[i].includes('<')) { 224 returnName = returnNames[i]; 225 break; 226 } 227 } 228 if (returnName.trim() === 'void') { 229 return ''; 230 } 231 if (getClassNameSet().has(returnName)) { 232 return `return new ${returnName}()`; 233 } else { 234 return `return ${getBaseReturnValue(returnName.trim())}`; 235 } 236} 237 238/** 239 * special property whitelist 240 * @param propertyTypeName 241 * @returns 242 */ 243export function propertyTypeWhiteList(propertyTypeName: string): boolean | number | string { 244 const whiteList = ['GLboolean', 'GLuint', 'GLenum', 'GLint', 'NotificationFlags']; 245 if (whiteList.includes(propertyTypeName)) { 246 if (propertyTypeName === 'NotificationFlags' || propertyTypeName === 'GLenum') { 247 return `'[PC Preview] unknown ${propertyTypeName}'`; 248 } else if (propertyTypeName === 'GLboolean') { 249 return true; 250 } else { 251 return 0; 252 } 253 } else { 254 return propertyTypeName; 255 } 256} 257 258/** 259 * get basic return value 260 * @param value 261 * @returns 262 */ 263export function getBaseReturnValue(value: string): string | number | boolean { 264 if (value === 'string') { 265 return '\'\''; 266 } else if (value === 'number') { 267 return 0; 268 } else if (value === 'boolean') { 269 return true; 270 } else if (value === 'Object' || value === 'object') { 271 return '{}'; 272 } else if (checkIsGenericSymbol(value)) { 273 return '\'[PC Preview] unknown type\''; 274 } else if (value === 'WebGLActiveInfo') { 275 return '{size: \'[PC Preview] unknown GLint\', type: 0, name: \'[PC Preview] unknown name\'}'; 276 } else { 277 return value; 278 } 279} 280 281/** 282 * get current sourceFile import data 283 * @param sourceFile 284 * @param typeName 285 * @returns 286 */ 287export function getTheRealReferenceFromImport(sourceFile: SourceFile, typeName: string): string { 288 const importArray = getImportDeclarationArray(sourceFile); 289 let returnName = ''; 290 let isFromImport = false; 291 let isOhos = false; 292 let mockMockName = ''; 293 importArray.forEach(value => { 294 if (typeName.includes('.') && typeName.split('.')[0] === value.importElements) { 295 isFromImport = true; 296 if (value.importPath.includes('@ohos')) { 297 isOhos = true; 298 } 299 if (value.importElements.trim() === typeName.split('.')[0]) { 300 const tmpArr = value.importPath.split('.'); 301 mockMockName = tmpArr[tmpArr.length - 1].replace(/["']/g, ''); 302 } 303 } 304 }); 305 if (isFromImport) { 306 const splitReturnKindName = typeName.split('.'); 307 let left = ''; 308 for (let i = 1; i < splitReturnKindName.length; i++) { 309 left += `.${splitReturnKindName[i]}`; 310 } 311 if (isOhos) { 312 if (mockMockName === 'observer') { 313 returnName = `${mockMockName}()${left}`; 314 } else { 315 returnName = `mock${firstCharacterToUppercase(mockMockName)}()${left}`; 316 } 317 } 318 } else { 319 returnName = getImportTypeAliasNameFromImportElements(importArray, typeName); 320 } 321 return returnName.split('<')[0]; 322} 323 324/** 325 * get return type alias, for example: {Context as _Context} return _Context 326 * @param importElementEntity 327 * @param typeName 328 * @returns 329 */ 330function getImportTypeAliasNameFromImportElements( 331 importElementEntity: ImportElementEntity[], 332 typeName: string 333): string { 334 for (let i = 0; i < importElementEntity.length; i++) { 335 if (importElementEntity[i].importElements.includes('_')) { 336 const importElements = importElementEntity[i].importElements.replace('{', '').replace('}', '').split(','); 337 typeName = getTypeName(importElements, typeName); 338 } 339 } 340 if (typeName === 'Want') { 341 typeName = 'mockWant().Want'; 342 } else if (typeName === 'InputMethodExtensionContext') { 343 typeName = 'mockInputMethodExtensionContext().InputMethodExtensionContext'; 344 } else if (typeName.includes('<') && typeName.includes(',')) { 345 typeName = '{}'; 346 } 347 return typeName; 348} 349 350/** 351 * get type name , for example: {Context as _Context} return _Context 352 * @param importElements 353 * @param typeName 354 * @returns 355 */ 356function getTypeName(importElements: string[], typeName: string): string { 357 for (let j = 0; j < importElements.length; j++) { 358 const element = importElements[j].trim(); 359 if (!element) { 360 continue; 361 } 362 if (`_${typeName}` === element.trim()) { 363 return `_${typeName}`; 364 } 365 if (element.includes(' as ') && `_${typeName}` === element.split('as')[1].trim()) { 366 return `_${typeName}`; 367 } 368 } 369 return typeName; 370} 371 372/** 373 * check is generic symbol 374 * @param type 375 * @returns 376 */ 377export function checkIsGenericSymbol(type: string): boolean { 378 const words = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; 379 return words.includes(type); 380} 381 382/** 383 * generate basic type default value 384 * @param kindName 385 * @returns 386 */ 387export function generateGenericTypeToMockValue(kindName: string): string | number | boolean { 388 const genericTypeName = kindName.split('<')[1].split('>')[0]; 389 if (genericTypeName === 'string') { 390 return '\'\''; 391 } else if (genericTypeName === 'number') { 392 return 0; 393 } else if (genericTypeName === 'boolean') { 394 return true; 395 } else if (genericTypeName === 'Object' || genericTypeName === 'object') { 396 return '{}'; 397 } else { 398 return ''; 399 } 400} 401 402export const paramsTypeStart = { 403 'void': '\'[PC Preview] unknown type\'', 404 'Array': '[]', 405 'Object': '{}', 406 'object': '{}', 407 '{': '{}', 408 'string': '""', 409 'String': '""', 410 'number': 0, 411 'Number': 0, 412 'boolean': false, 413 'Boolean': false, 414 'ArrayBuffer': 'new ArrayBuffer(0)', 415 'Uint8Array': 'new Uint8Array()', 416 'unknown': '\'[PC Preview] unknown type\'' 417}; 418 419const removeCallback = (str: string): { type: string; value: string } => { 420 const callbackParams = { 421 type: 'Callback', 422 value: '' 423 }; 424 if (str.startsWith('Callback')) { 425 if (str.lastIndexOf('>') < str.lastIndexOf(' | ')) { 426 callbackParams.value = str.slice(0, str.lastIndexOf('>')).slice(9).trim(); 427 callbackParams.type = 'Callback'; 428 } else { 429 callbackParams.value = str.slice(0, str.length - 1).slice(9).trim(); 430 callbackParams.type = 'Callback'; 431 } 432 } else if (str.startsWith('AsyncCallback')) { 433 callbackParams.value = str.slice(0, str.length - 1).slice(14).trim(); 434 callbackParams.type = 'AsyncCallback'; 435 } 436 let isHaveAnglebrackets = false; 437 if (callbackParams.value.includes('<') && callbackParams.value.includes(',')) { 438 isHaveAnglebrackets = true; 439 } 440 if (callbackParams.value.includes(',') && !isHaveAnglebrackets) { 441 callbackParams.value = callbackParams.value.split(',')[0].trim(); 442 } 443 return callbackParams; 444}; 445 446const isInImportType = (mockApi: string, value: string): string => { 447 let hasDotFirstWorld = ''; 448 if (value.includes('.')) { 449 hasDotFirstWorld = value.split('.')[0].trim(); 450 } 451 if (hasDotFirstWorld && mockApi.includes(`import { mock${firstLetterWord(hasDotFirstWorld)} `)) { 452 return 'isHasDotImportMock'; 453 } 454 if (hasDotFirstWorld && mockApi.includes(`import { ${firstLetterWord(hasDotFirstWorld)} `)) { 455 return 'isNoHasDotImportMock'; 456 } 457 if (mockApi.includes(`import { mock${firstLetterWord(value)} `)) { 458 return 'isImportMock'; 459 } 460 if (mockApi.includes(`import { ${value} `)) { 461 return 'isImport'; 462 } 463 return 'noImport'; 464}; 465 466const firstLetterWord = (word: string): string => { 467 return word.slice(0, 1).toUpperCase() + word.slice(1); 468}; 469 470const hasDotFirstWord = (str: string): string => { 471 return str.includes('.') ? str.split('.')[0] : str; 472}; 473 474function callbackHasNoImportType(callbackParams: { type: string; value: string }): string { 475 let callbackData = ''; 476 let paramsTypeHasType = true; 477 if (callbackParams.value.endsWith(']')) { 478 callbackData = '[]'; 479 } else { 480 Object.keys(paramsTypeStart).forEach(item => { 481 if (callbackParams.value.startsWith(item)) { 482 callbackData = paramsTypeStart[item]; 483 paramsTypeHasType = false; 484 } 485 }); 486 if (paramsTypeHasType) { 487 callbackData = callbackParams.value; 488 if (callbackParams.value.includes('<')) { 489 callbackData = `${callbackParams.value.split('<')[0]}`; 490 } 491 if (callbackParams.value.includes('<') && callbackParams.value.includes(',')) { 492 callbackData = '{}'; 493 } 494 } 495 if (callbackParams.value === 'Date') { 496 callbackData = 'new Date()'; 497 } 498 if (callbackParams.value === 'Uint8Array') { 499 callbackData = 'new Uint8Array()'; 500 } 501 if (callbackParams.value === 'T') { 502 callbackData = '[PC Preview] unknown type'; 503 } 504 } 505 return callbackData; 506} 507 508/** 509 * get callback parameters data 510 * @returns data: parameters data: type: AsyncCallback or Callback 511 */ 512const setCallbackData = (mockApi: string, paramTypeString: string): { data: string; type: string } => { 513 const callbackParams = removeCallback(paramTypeString); 514 let callbackData = ''; 515 let importType = ''; 516 if (callbackParams.value) { 517 importType = isInImportType(mockApi, callbackParams.value); 518 } 519 if (importType === 'isHasDotImportMock') { 520 const upperWord = firstLetterWord(callbackParams.value); // Image.PixelMap 521 const firstWord = hasDotFirstWord(upperWord); // Image 522 callbackData = `mock${firstWord}()${upperWord.slice(firstWord.length)}`; 523 } else if (importType === 'isNoHasDotImportMock') { 524 callbackData = callbackParams.value; 525 } else if (importType === 'isImportMock') { 526 callbackData = `mock${firstLetterWord(callbackParams.value)}()`; 527 } else if (importType === 'isImport') { 528 callbackData = callbackParams.value; 529 } else if (importType === 'noImport') { 530 callbackData = callbackHasNoImportType(callbackParams); 531 } else { 532 callbackData = '[PC Preview] unknown type'; 533 } 534 return { 535 data: callbackData, 536 type: callbackParams.type 537 }; 538}; 539 540/** 541 * get callback statement 542 * @returns callback statement 543 */ 544export function getCallbackStatement(mockApi: string, paramTypeString?: string): string { 545 let outPut = `if (args && typeof args[args.length - 1] === 'function') { 546 args[args.length - 1].call(this,`; 547 const callbackError = "{'code': '','data': '','name': '','message': '','stack': ''}"; 548 let callbackDataParams = { 549 type: '', 550 data: '[PC Preview] unknown type' 551 }; 552 if (paramTypeString) { 553 callbackDataParams = setCallbackData(mockApi, paramTypeString); 554 } 555 if (callbackDataParams?.type === 'AsyncCallback') { 556 outPut += ` ${callbackError},`; 557 } 558 outPut += 559 callbackDataParams.data === '[PC Preview] unknown type' 560 ? ` '${callbackDataParams.data}');\n}` 561 : ` ${callbackDataParams.data});\n}`; 562 return outPut; 563} 564 565/** 566 * get callback statement 567 * @returns callback statement 568 */ 569export function getOverloadedFunctionCallbackStatement( 570 entityArray: Array<FunctionEntity> | Array<MethodEntity> | Array<MethodSignatureEntity>, 571 sourceFile: SourceFile, 572 mockApi: string 573): string { 574 let overloadedCallbackBody = ''; 575 entityArray.forEach(functionBody => { 576 let content = ''; 577 let firstParamContent = ''; 578 let callbackParamContent = ''; 579 functionBody.args.forEach(arg => { 580 if ( 581 arg.paramTypeString.startsWith("'") && arg.paramTypeString.endsWith("'") || 582 arg.paramTypeString.startsWith('"') && arg.paramTypeString.endsWith('"') 583 ) { 584 const paramTypeStringArr = arg.paramTypeString.split('|'); 585 firstParamContent += `if (args && [${paramTypeStringArr}].includes(args[0])) {\n`; 586 } 587 if (['callback', 'observercallback', 'listener', 'synccallback'].includes(arg.paramName.toLowerCase())) { 588 callbackParamContent += getCallbackBody(mockApi, arg.paramTypeString); 589 } 590 }); 591 if (firstParamContent) { 592 content = `${firstParamContent}${callbackParamContent}\n}` + content; 593 } else { 594 content += callbackParamContent; 595 } 596 overloadedCallbackBody += content; 597 }); 598 overloadedCallbackBody += '\n'; 599 return overloadedCallbackBody; 600} 601 602/** 603 * get callback statement 604 * @returns callback statement 605 */ 606function getCallbackBody(mockApi: string, paramString: string): string { 607 let bodyInfo = `if (args && typeof args[args.length - 1] === 'function') { 608 args[args.length - 1].call(this,`; 609 const callbackError = "{'code': '','data': '','name': '','message': '','stack': ''}"; 610 if (paramString === 'ErrorCallback') { 611 bodyInfo += callbackError + ');\n}'; 612 return bodyInfo; 613 } 614 let callbackDataParams = { 615 type: '', 616 data: '[PC Preview] unknown type' 617 }; 618 if (paramString) { 619 callbackDataParams = setCallbackData(mockApi, paramString); 620 } 621 if (callbackDataParams?.type === 'AsyncCallback') { 622 bodyInfo += ` ${callbackError},`; 623 } 624 bodyInfo += 625 callbackDataParams.data === '[PC Preview] unknown type' 626 ? ` '${callbackDataParams.data}');\n` 627 : ` ${callbackDataParams.data});\n`; 628 bodyInfo += '}'; 629 return bodyInfo; 630} 631 632/** 633 * get iterator template string 634 * @param methodEntity 635 * @returns 636 */ 637export function generateSymbolIterator(methodEntity: MethodEntity): string { 638 let iteratorMethod = ''; 639 if (methodEntity.returnType.returnKindName.includes('<[')) { 640 iteratorMethod += `let index = 0; 641 const IteratorMock = { 642 next: () => { 643 if (index < 1) { 644 const returnValue = ['[PC Previwe] unknown iterableiterator_k', '[PC Previwe] unknown iterableiterator_v']; 645 index++; 646 return { 647 value: returnValue, 648 done: false 649 }; 650 } else { 651 return { 652 done: true 653 }; 654 } 655 } 656 }; 657 return IteratorMock;`; 658 } else { 659 iteratorMethod += `let index = 0; 660 const IteratorMock = { 661 next: () => { 662 if (index < 1) { 663 index++; 664 return { 665 value: '[PC Preview] unknown any', 666 done: false 667 }; 668 } 669 return { 670 done: true 671 }; 672 } 673 }; 674 return IteratorMock;`; 675 } 676 677 return iteratorMethod; 678} 679 680function handleReturnDataNoImportType(returnPromiseParams: string, returnType: ReturnTypeEntity): string { 681 let returnData = ''; 682 if (returnPromiseParams.startsWith('[') || returnPromiseParams.endsWith(']')) { 683 returnData = '[]'; 684 } else { 685 let paramsTypeHasType = true; 686 Object.keys(paramsTypeStart).forEach(item => { 687 if (returnPromiseParams.startsWith(item)) { 688 returnData = paramsTypeStart[item]; 689 paramsTypeHasType = false; 690 } 691 }); 692 if (paramsTypeHasType) { 693 returnData = returnPromiseParams; 694 if (returnPromiseParams.includes('<')) { 695 returnData = `${returnPromiseParams.split('<')[0]}`; 696 } 697 if (returnPromiseParams.includes('<') && returnPromiseParams.includes(',')) { 698 returnData = '{}'; 699 } 700 } 701 if (returnPromiseParams === 'Date') { 702 returnData = 'new Date()'; 703 } 704 if (returnPromiseParams === 'T') { 705 returnData = '"[PC Preview] unknown type"'; 706 } 707 if (returnType.returnKindName.startsWith('Readonly')) { 708 returnData = `${returnType.returnKindName.split('<')[1].split('>')[0]}`; 709 } 710 if (checkIsGenericSymbol(returnType.returnKindName)) { 711 returnData = `'[PC Preview] unknown iterableiterator_${returnType.returnKindName}'`; 712 } 713 } 714 return returnData; 715} 716 717/** 718 * generate more function name return statement; 719 * @param isReturnPromise 720 * @param returnType 721 * @param sourceFile 722 * @param mockApi 723 * @returns 724 */ 725export function getReturnData( 726 isCallBack: boolean, 727 isReturnPromise: boolean, 728 returnType: ReturnTypeEntity, 729 sourceFile: SourceFile, 730 mockApi: string 731): string { 732 // If the return value is an iterator IterableIterator, then iteratorEntriesMock is directly returned 733 if (returnType.returnKindName.startsWith('IterableIterator')) { 734 return returnType.returnKindName.includes(',') ? iteratorEntriesMock : iteratorStringMock; 735 } 736 // If it is a promise, intercept the content of x in promise<x>, which may have the following formats: 737 // fun(): y | Promise<y>、 fun(): Promise<x | y | z>、 fun(): Promise<x>、 fun(): Promise<x.y> 738 // If it is not a promise, the returned type may be x, x | y | z, x.y 739 let returnPromiseParams = returnType.returnKindName; 740 if (isReturnPromise) { 741 if (returnType.returnKind === SyntaxKind.UnionType) { 742 // fun(): y | Promise<y> 743 const returnNames = returnPromiseParams.split('|'); 744 returnPromiseParams = loopReturnNames(returnNames, returnPromiseParams); 745 } 746 // At this point, obtain the values in these formats: Promise<x | y | z>, Promise<y>, Promise<x.y>, Promise<x> 747 const kindName = returnPromiseParams; 748 returnPromiseParams = kindName.slice(0, kindName.length - 1).slice(8).trim(); 749 } 750 // At this point, the value type of param in promise<param>may be x, x | y | z, x.y 751 if (returnPromiseParams.includes('|')) { 752 returnPromiseParams = getSeparatorParam(returnPromiseParams); 753 } 754 755 // At this point, the possible types of promiseParam are x, x.y x [] Array<x> 756 // Check if it was imported 757 let returnData = '"[PC Preview] unknown type"'; 758 const importType = isInImportType(mockApi, returnPromiseParams); 759 if (importType === 'isHasDotImportMock') { 760 const upperWord = firstLetterWord(returnPromiseParams); // Image.PixelMap 761 const firstWord = hasDotFirstWord(upperWord); // Image 762 returnData = `mock${firstWord}()${upperWord.slice(firstWord.length)}`; 763 } else if (importType === 'isNoHasDotImportMock') { 764 returnData = returnPromiseParams; 765 } else if (importType === 'isImportMock') { 766 returnData = `mock${firstLetterWord(returnPromiseParams)}()`; 767 } else if (importType === 'isImport') { 768 returnData = returnPromiseParams; 769 } else if (importType === 'noImport') { 770 returnData = handleReturnDataNoImportType(returnPromiseParams, returnType); 771 } else { 772 returnData = '"[PC Preview] unknown type"'; 773 } 774 const data = 775 typeof returnData === 'string' && returnData.startsWith('[PC Preview] unknown') 776 ? `'${returnData}'` 777 : `${returnData}`; 778 if (isReturnPromise) { 779 return ` 780 return new Promise((resolve, reject) => { 781 resolve(${data}); 782 }) 783 `; 784 } else { 785 return `return ${data}`; 786 } 787} 788 789/** 790 * loop ReturnNames 791 * @param returnNames 792 * @param returnPromiseParams 793 * @returns 794 */ 795function loopReturnNames(returnNames: string[], returnPromiseParams: string): string { 796 for (let i = 0; i < returnNames.length; i++) { 797 if (returnNames[i].trim().startsWith('Promise<')) { 798 // Promise<y> 799 returnPromiseParams = returnNames[i].trim(); 800 break; 801 } 802 } 803 return returnPromiseParams; 804} 805 806/** 807 * 808 * @param returnPromiseParams 809 * @returns 810 */ 811function getSeparatorParam(returnPromiseParams: string): string { 812 let hasObj = ''; 813 let hasArr = ''; 814 let hasUint8Array = ''; 815 let hasArrayBuffer = ''; 816 let otherValue = ''; 817 const paramsArr = returnPromiseParams.split('|'); 818 for (let i = 0; i < paramsArr.length; i++) { 819 const param = paramsArr[i].trim(); 820 if (param.startsWith('{') || param.startsWith('Object')) { 821 hasObj = '{}'; 822 } else if (param.endsWith(']') || param.startsWith('[') || param.startsWith('Array<')) { 823 hasArr = '[]'; 824 } else if (param.startsWith('Uint8Array')) { 825 hasUint8Array = 'Uint8Array'; 826 } else if (param.startsWith('ArrayBuffer')) { 827 hasArrayBuffer = 'ArrayBuffer'; 828 } else { 829 if (param !== null) { 830 otherValue = param; 831 } 832 } 833 } 834 if (hasObj) { 835 return hasObj; 836 } 837 if (hasArr) { 838 return hasArr; 839 } 840 if (hasUint8Array) { 841 return hasUint8Array; 842 } 843 if (hasArrayBuffer) { 844 return hasArrayBuffer; 845 } 846 return otherValue; 847} 848 849/** 850 * 851 * @param mockName string 852 * @param sourceFile SourceFile 853 * @returns boolean 854 */ 855export function hasExportDefaultKeyword(mockName: string, sourceFile: SourceFile): boolean { 856 let fileContent = sourceFile.getText(); 857 const removeNoteRegx = /\/\*[\s\S]*?\*\//g; 858 fileContent = fileContent.replace(removeNoteRegx, ''); 859 if ( 860 fileContent.includes(`export default class ${firstCharacterToUppercase(mockName)} `) || 861 fileContent.includes(`export default interface ${firstCharacterToUppercase(mockName)} `) || 862 fileContent.includes(`export default ${firstCharacterToUppercase(mockName)}`) 863 ) { 864 return false; 865 } 866 return true; 867} 868 869export const overloadedFunctionArr = ['on', 'off']; 870 871export const needToAddBrace = ['ViewData', 'AutoFillType']; 872 873export interface MockFunctionElementEntity { 874 elementName: string; 875 type: string; 876} 877export interface ReturnDataParams { 878 mockData: string; 879 mockFunctionElements: Array<MockFunctionElementEntity>; 880} 881