@immense/vue-pom-generator 1.0.58 → 1.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -18
- package/RELEASE_NOTES.md +75 -29
- package/class-generation/base-page.ts +6 -13
- package/class-generation/index.ts +226 -317
- package/class-generation/playwright-types.ts +1 -1
- package/click-instrumentation.ts +0 -4
- package/dist/class-generation/base-page.d.ts +1 -0
- package/dist/class-generation/base-page.d.ts.map +1 -1
- package/dist/class-generation/index.d.ts +2 -0
- package/dist/class-generation/index.d.ts.map +1 -1
- package/dist/class-generation/playwright-types.d.ts +1 -1
- package/dist/class-generation/playwright-types.d.ts.map +1 -1
- package/dist/click-instrumentation.d.ts +0 -1
- package/dist/click-instrumentation.d.ts.map +1 -1
- package/dist/index.cjs +1216 -1008
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1218 -1010
- package/dist/index.mjs.map +1 -1
- package/dist/method-generation.d.ts +4 -2
- package/dist/method-generation.d.ts.map +1 -1
- package/dist/plugin/create-vue-pom-generator-plugins.d.ts.map +1 -1
- package/dist/plugin/resolved-generation-options.d.ts +33 -0
- package/dist/plugin/resolved-generation-options.d.ts.map +1 -0
- package/dist/plugin/resolved-injection-options.d.ts +27 -0
- package/dist/plugin/resolved-injection-options.d.ts.map +1 -0
- package/dist/plugin/support/build-plugin.d.ts +2 -29
- package/dist/plugin/support/build-plugin.d.ts.map +1 -1
- package/dist/plugin/support/dev-plugin.d.ts +2 -28
- package/dist/plugin/support/dev-plugin.d.ts.map +1 -1
- package/dist/plugin/support-plugins.d.ts +2 -32
- package/dist/plugin/support-plugins.d.ts.map +1 -1
- package/dist/plugin/types.d.ts +6 -23
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/plugin/vue-plugin.d.ts.map +1 -1
- package/dist/pom-params.d.ts +40 -0
- package/dist/pom-params.d.ts.map +1 -0
- package/dist/pom-patterns.d.ts +31 -0
- package/dist/pom-patterns.d.ts.map +1 -0
- package/dist/routing/to-directive.d.ts +21 -0
- package/dist/routing/to-directive.d.ts.map +1 -1
- package/dist/tests/base-page.test.d.ts +2 -0
- package/dist/tests/base-page.test.d.ts.map +1 -0
- package/dist/tests/resolved-injection-options.test.d.ts +2 -0
- package/dist/tests/resolved-injection-options.test.d.ts.map +1 -0
- package/dist/transform.d.ts +0 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/utils.d.ts +129 -63
- package/dist/utils.d.ts.map +1 -1
- package/package.json +6 -4
- package/sequence-diagram.md +6 -6
package/dist/index.cjs
CHANGED
|
@@ -245,6 +245,49 @@ async function loadNuxtProjectDiscovery(cwd = process.cwd()) {
|
|
|
245
245
|
const nuxtOptions = await loadNuxtConfig({ cwd });
|
|
246
246
|
return resolveNuxtProjectDiscovery(nuxtOptions, getLayerDirectories, cwd);
|
|
247
247
|
}
|
|
248
|
+
function resolveGenerationSupportOptions(options) {
|
|
249
|
+
return {
|
|
250
|
+
outDir: (options.outDir ?? "tests/playwright/__generated__").trim(),
|
|
251
|
+
emitLanguages: options.emitLanguages?.length ? options.emitLanguages : ["ts"],
|
|
252
|
+
typescriptOutputStructure: options.typescriptOutputStructure ?? "aggregated",
|
|
253
|
+
csharp: options.csharp,
|
|
254
|
+
generateFixtures: options.generateFixtures,
|
|
255
|
+
customPomAttachments: options.customPomAttachments ?? [],
|
|
256
|
+
customPomDir: options.customPomDir ?? "tests/playwright/pom/custom",
|
|
257
|
+
requireCustomPomDir: options.requireCustomPomDir ?? false,
|
|
258
|
+
customPomImportAliases: options.customPomImportAliases,
|
|
259
|
+
customPomImportNameCollisionBehavior: options.customPomImportNameCollisionBehavior ?? "error",
|
|
260
|
+
nameCollisionBehavior: options.nameCollisionBehavior ?? "error",
|
|
261
|
+
existingIdBehavior: options.existingIdBehavior ?? "error",
|
|
262
|
+
testIdAttribute: (options.testIdAttribute ?? "data-testid").trim() || "data-testid",
|
|
263
|
+
routerAwarePoms: options.routerAwarePoms ?? false,
|
|
264
|
+
routerEntry: options.routerEntry,
|
|
265
|
+
routerType: options.routerType ?? "vue-router",
|
|
266
|
+
routerModuleShims: options.routerModuleShims
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function resolveInjectionSupportOptions(options) {
|
|
270
|
+
const isNuxt = options.isNuxt ?? false;
|
|
271
|
+
return {
|
|
272
|
+
pageDirs: isNuxt ? ["app/pages"] : [options.viewsDir ?? "src/views"],
|
|
273
|
+
componentDirs: isNuxt ? ["app/components"] : options.componentDirs ?? ["src/components"],
|
|
274
|
+
layoutDirs: isNuxt ? ["app/layouts"] : options.layoutDirs ?? ["src/layouts"],
|
|
275
|
+
wrapperSearchRoots: isNuxt ? [] : options.wrapperSearchRoots ?? [],
|
|
276
|
+
nativeWrappers: options.nativeWrappers ?? {},
|
|
277
|
+
excludedComponents: options.excludedComponents ?? [],
|
|
278
|
+
existingIdBehavior: options.existingIdBehavior ?? "error",
|
|
279
|
+
testIdAttribute: (options.testIdAttribute ?? "data-testid").trim() || "data-testid"
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function applyNuxtDiscoveryToInjectionOptions(options, discovery) {
|
|
283
|
+
return {
|
|
284
|
+
...options,
|
|
285
|
+
pageDirs: discovery.pageDirs.length ? discovery.pageDirs : [path.resolve(discovery.srcDir, "pages")],
|
|
286
|
+
componentDirs: discovery.componentDirs,
|
|
287
|
+
layoutDirs: discovery.layoutDirs,
|
|
288
|
+
wrapperSearchRoots: discovery.wrapperSearchRoots
|
|
289
|
+
};
|
|
290
|
+
}
|
|
248
291
|
function createTypeScriptProject() {
|
|
249
292
|
return new tsMorph.Project({
|
|
250
293
|
useInMemoryFileSystem: true,
|
|
@@ -336,16 +379,7 @@ function createClassConstructor(constructorDeclaration) {
|
|
|
336
379
|
...constructorDeclaration
|
|
337
380
|
};
|
|
338
381
|
}
|
|
339
|
-
function
|
|
340
|
-
if (!value) {
|
|
341
|
-
return value;
|
|
342
|
-
}
|
|
343
|
-
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
344
|
-
}
|
|
345
|
-
function hasParam(params, name) {
|
|
346
|
-
return Object.prototype.hasOwnProperty.call(params, name);
|
|
347
|
-
}
|
|
348
|
-
function splitTypeAndInitializer(typeExpression) {
|
|
382
|
+
function splitPomParameterTypeExpression(typeExpression) {
|
|
349
383
|
const trimmed = typeExpression.trim();
|
|
350
384
|
const initializerIndex = trimmed.lastIndexOf("=");
|
|
351
385
|
if (initializerIndex < 0) {
|
|
@@ -356,49 +390,237 @@ function splitTypeAndInitializer(typeExpression) {
|
|
|
356
390
|
initializer: trimmed.slice(initializerIndex + 1).trim()
|
|
357
391
|
};
|
|
358
392
|
}
|
|
359
|
-
function
|
|
360
|
-
const
|
|
393
|
+
function createPomParameterSpec(name, typeExpression, options = {}) {
|
|
394
|
+
const normalizedTypeExpression = typeExpression?.trim();
|
|
395
|
+
const { type, initializer } = normalizedTypeExpression ? splitPomParameterTypeExpression(normalizedTypeExpression) : { type: void 0, initializer: void 0 };
|
|
361
396
|
return {
|
|
362
397
|
name,
|
|
363
|
-
|
|
364
|
-
|
|
398
|
+
typeExpression: normalizedTypeExpression,
|
|
399
|
+
type,
|
|
400
|
+
initializer: options.initializer ?? initializer,
|
|
401
|
+
hasQuestionToken: options.hasQuestionToken,
|
|
402
|
+
isRestParameter: options.isRestParameter
|
|
365
403
|
};
|
|
366
404
|
}
|
|
367
|
-
function
|
|
368
|
-
|
|
405
|
+
function normalizePomParameters(params) {
|
|
406
|
+
if (!params) {
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
409
|
+
return params.map((param) => createPomParameterSpec(param.name, param.typeExpression ?? param.type, {
|
|
410
|
+
initializer: param.initializer,
|
|
411
|
+
hasQuestionToken: param.hasQuestionToken,
|
|
412
|
+
isRestParameter: param.isRestParameter
|
|
413
|
+
}));
|
|
369
414
|
}
|
|
370
|
-
function
|
|
415
|
+
function getPomParameterNames(params) {
|
|
416
|
+
return normalizePomParameters(params).map((param) => param.name);
|
|
417
|
+
}
|
|
418
|
+
function getPomParameter(params, name) {
|
|
419
|
+
return normalizePomParameters(params).find((param) => param.name === name);
|
|
420
|
+
}
|
|
421
|
+
function hasPomParameter(params, name) {
|
|
422
|
+
return !!getPomParameter(params, name);
|
|
423
|
+
}
|
|
424
|
+
function setPomParameter(params, name, typeExpression, options = {}) {
|
|
425
|
+
const nextParam = createPomParameterSpec(name, typeExpression, options);
|
|
426
|
+
const normalizedParams = normalizePomParameters(params);
|
|
427
|
+
const existingIndex = normalizedParams.findIndex((param) => param.name === name);
|
|
428
|
+
if (existingIndex < 0) {
|
|
429
|
+
return [...normalizedParams, nextParam];
|
|
430
|
+
}
|
|
431
|
+
const nextParams = normalizedParams.slice();
|
|
432
|
+
nextParams[existingIndex] = nextParam;
|
|
433
|
+
return nextParams;
|
|
434
|
+
}
|
|
435
|
+
function removePomParameter(params, name) {
|
|
436
|
+
return normalizePomParameters(params).filter((param) => param.name !== name);
|
|
437
|
+
}
|
|
438
|
+
function toTypeScriptPomParameterStructures(params) {
|
|
439
|
+
return normalizePomParameters(params).map((param) => ({
|
|
440
|
+
name: param.name,
|
|
441
|
+
type: param.type || void 0,
|
|
442
|
+
initializer: param.initializer,
|
|
443
|
+
hasQuestionToken: param.hasQuestionToken,
|
|
444
|
+
isRestParameter: param.isRestParameter
|
|
445
|
+
}));
|
|
446
|
+
}
|
|
447
|
+
function getPomParameterArgumentNames(params) {
|
|
448
|
+
return normalizePomParameters(params).map((param) => param.isRestParameter ? `...${param.name}` : param.name);
|
|
449
|
+
}
|
|
450
|
+
function createPomMethodSignature(parameters) {
|
|
371
451
|
return {
|
|
372
|
-
|
|
373
|
-
type: options.type,
|
|
374
|
-
initializer: options.initializer
|
|
452
|
+
parameters: normalizePomParameters(parameters)
|
|
375
453
|
};
|
|
376
454
|
}
|
|
377
|
-
function
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
455
|
+
function pomParameterSpecEquals(left, right) {
|
|
456
|
+
return left.name === right.name && left.typeExpression === right.typeExpression && left.type === right.type && left.initializer === right.initializer && left.hasQuestionToken === right.hasQuestionToken && left.isRestParameter === right.isRestParameter;
|
|
457
|
+
}
|
|
458
|
+
function pomParameterListEquals(left, right) {
|
|
459
|
+
const leftParams = normalizePomParameters(left);
|
|
460
|
+
const rightParams = normalizePomParameters(right);
|
|
461
|
+
if (leftParams.length !== rightParams.length) {
|
|
462
|
+
return false;
|
|
381
463
|
}
|
|
382
|
-
return
|
|
464
|
+
return leftParams.every((param, index) => pomParameterSpecEquals(param, rightParams[index]));
|
|
383
465
|
}
|
|
384
|
-
function
|
|
466
|
+
function pomMethodSignatureEquals(left, right) {
|
|
467
|
+
return pomParameterListEquals(left.parameters, right.parameters);
|
|
468
|
+
}
|
|
469
|
+
function isParameterizedPomPattern(kind) {
|
|
470
|
+
return kind === "parameterized";
|
|
471
|
+
}
|
|
472
|
+
function getTemplateVariables(formatted) {
|
|
385
473
|
const out = [];
|
|
386
474
|
const seen = /* @__PURE__ */ new Set();
|
|
387
|
-
|
|
388
|
-
for (const
|
|
389
|
-
|
|
475
|
+
const matches = formatted.matchAll(/\$\{(\w+)\}/g);
|
|
476
|
+
for (const match of matches) {
|
|
477
|
+
const variableName = match[1];
|
|
478
|
+
if (seen.has(variableName)) {
|
|
390
479
|
continue;
|
|
391
480
|
}
|
|
392
|
-
|
|
481
|
+
seen.add(variableName);
|
|
482
|
+
out.push(variableName);
|
|
483
|
+
}
|
|
484
|
+
return out;
|
|
485
|
+
}
|
|
486
|
+
function createPomStringPattern(formatted, patternKind) {
|
|
487
|
+
return {
|
|
488
|
+
formatted,
|
|
489
|
+
patternKind,
|
|
490
|
+
templateVariables: getTemplateVariables(formatted)
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function getPomPatternVariables(patterns, options = {}) {
|
|
494
|
+
const out = [];
|
|
495
|
+
const seen = /* @__PURE__ */ new Set();
|
|
496
|
+
const omitted = new Set(options.omit ?? []);
|
|
497
|
+
for (const pattern of patterns) {
|
|
498
|
+
for (const variableName of pattern.templateVariables) {
|
|
499
|
+
if (omitted.has(variableName) || seen.has(variableName)) {
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
seen.add(variableName);
|
|
503
|
+
out.push(variableName);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return out;
|
|
507
|
+
}
|
|
508
|
+
function orderPomPatternParameters(params, patterns, options = {}) {
|
|
509
|
+
const currentParams = normalizePomParameters(params);
|
|
510
|
+
const orderedParams = [];
|
|
511
|
+
const seen = /* @__PURE__ */ new Set();
|
|
512
|
+
const missingParams = [];
|
|
513
|
+
for (const variableName of getPomPatternVariables(patterns, options)) {
|
|
514
|
+
seen.add(variableName);
|
|
515
|
+
const existingParam = currentParams.find((param) => param.name === variableName);
|
|
516
|
+
if (!existingParam) {
|
|
517
|
+
missingParams.push(variableName);
|
|
393
518
|
continue;
|
|
394
519
|
}
|
|
395
|
-
|
|
396
|
-
|
|
520
|
+
orderedParams.push(existingParam);
|
|
521
|
+
}
|
|
522
|
+
if (missingParams.length > 0) {
|
|
523
|
+
const availableParams = currentParams.map((param) => JSON.stringify(param.name)).join(", ") || "<none>";
|
|
524
|
+
const patternSummary = patterns.map((pattern) => JSON.stringify(pattern.formatted)).join(", ");
|
|
525
|
+
throw new Error(
|
|
526
|
+
`[vue-pom-generator] Missing selector parameter(s) ${missingParams.map((name) => JSON.stringify(name)).join(", ")} for parameterized pattern(s) ${patternSummary}. Available parameters: ${availableParams}.`
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
for (const param of currentParams) {
|
|
530
|
+
if (seen.has(param.name)) {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
seen.add(param.name);
|
|
534
|
+
orderedParams.push(param);
|
|
535
|
+
}
|
|
536
|
+
return orderedParams;
|
|
537
|
+
}
|
|
538
|
+
function getIndexedPomPatternVariable(pattern) {
|
|
539
|
+
if (!isParameterizedPomPattern(pattern.patternKind)) {
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
if (pattern.templateVariables.length !== 1) {
|
|
543
|
+
throw new Error(
|
|
544
|
+
`[vue-pom-generator] Parameterized locator getters require exactly one template variable; got ${pattern.templateVariables.length} in ${JSON.stringify(pattern.formatted)}.`
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
return pattern.templateVariables[0];
|
|
548
|
+
}
|
|
549
|
+
function hasPomPatternVariables(pattern) {
|
|
550
|
+
return pattern.templateVariables.length > 0;
|
|
551
|
+
}
|
|
552
|
+
function toTypeScriptPomPatternExpression(pattern) {
|
|
553
|
+
return isParameterizedPomPattern(pattern.patternKind) ? `\`${pattern.formatted}\`` : JSON.stringify(pattern.formatted);
|
|
554
|
+
}
|
|
555
|
+
function toCSharpPomPatternExpression(pattern) {
|
|
556
|
+
if (!isParameterizedPomPattern(pattern.patternKind)) {
|
|
557
|
+
return JSON.stringify(pattern.formatted);
|
|
558
|
+
}
|
|
559
|
+
const inner = pattern.formatted.replace(/\$\{/g, "{");
|
|
560
|
+
return `$${JSON.stringify(inner)}`;
|
|
561
|
+
}
|
|
562
|
+
function bindTypeScriptPomPattern(pattern, variableName) {
|
|
563
|
+
const expression = toTypeScriptPomPatternExpression(pattern);
|
|
564
|
+
if (!isParameterizedPomPattern(pattern.patternKind)) {
|
|
565
|
+
return { expression, setupStatements: [] };
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
expression: variableName,
|
|
569
|
+
setupStatements: [`const ${variableName} = ${expression};`]
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function bindCSharpPomPattern(pattern, variableName) {
|
|
573
|
+
const expression = toCSharpPomPatternExpression(pattern);
|
|
574
|
+
if (!isParameterizedPomPattern(pattern.patternKind)) {
|
|
575
|
+
return { expression, setupStatements: [] };
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
expression: variableName,
|
|
579
|
+
setupStatements: [`var ${variableName} = ${expression};`]
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
function pomStringPatternEquals(left, right) {
|
|
583
|
+
return left.formatted === right.formatted && left.patternKind === right.patternKind;
|
|
584
|
+
}
|
|
585
|
+
function uniquePomStringPatterns(primary, alternates) {
|
|
586
|
+
const out = [];
|
|
587
|
+
const seen = /* @__PURE__ */ new Set();
|
|
588
|
+
const add = (pattern) => {
|
|
589
|
+
const key = JSON.stringify(pattern);
|
|
590
|
+
if (seen.has(key)) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
seen.add(key);
|
|
594
|
+
out.push(pattern);
|
|
595
|
+
};
|
|
596
|
+
add(primary);
|
|
597
|
+
for (const alternate of alternates ?? []) {
|
|
598
|
+
add(alternate);
|
|
397
599
|
}
|
|
398
600
|
return out;
|
|
399
601
|
}
|
|
400
|
-
function
|
|
401
|
-
|
|
602
|
+
function upperFirst$1(value) {
|
|
603
|
+
if (!value) {
|
|
604
|
+
return value;
|
|
605
|
+
}
|
|
606
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
607
|
+
}
|
|
608
|
+
function createParameters(params) {
|
|
609
|
+
return toTypeScriptPomParameterStructures(params);
|
|
610
|
+
}
|
|
611
|
+
function createInlineParameter(name, options = {}) {
|
|
612
|
+
return {
|
|
613
|
+
name,
|
|
614
|
+
type: options.type,
|
|
615
|
+
initializer: options.initializer
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function removeByKeySegment(value) {
|
|
619
|
+
const idx = value.lastIndexOf("ByKey");
|
|
620
|
+
if (idx < 0) {
|
|
621
|
+
return value;
|
|
622
|
+
}
|
|
623
|
+
return value.slice(0, idx) + value.slice(idx + "ByKey".length);
|
|
402
624
|
}
|
|
403
625
|
function createAsyncMethod(name, parameters, statements) {
|
|
404
626
|
return createClassMethod({
|
|
@@ -408,17 +630,20 @@ function createAsyncMethod(name, parameters, statements) {
|
|
|
408
630
|
statements
|
|
409
631
|
});
|
|
410
632
|
}
|
|
411
|
-
function generateClickMethod(methodName,
|
|
633
|
+
function generateClickMethod(methodName, selector, alternateSelectors, parameters) {
|
|
412
634
|
const name = `click${methodName}`;
|
|
413
635
|
const noWaitName = `${name}NoWait`;
|
|
414
|
-
const
|
|
415
|
-
const
|
|
416
|
-
const
|
|
636
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
637
|
+
const hasSelectorVariables = hasPomPatternVariables(selector);
|
|
638
|
+
const baseParameters = createParameters(selectorParams);
|
|
639
|
+
const argsForForward = getPomParameterNames(selectorParams).join(", ");
|
|
640
|
+
const alternates = uniquePomStringPatterns(selector, alternateSelectors).slice(1);
|
|
641
|
+
const primaryTestIdExpr = toTypeScriptPomPatternExpression(selector);
|
|
417
642
|
if (alternates.length > 0) {
|
|
418
|
-
const candidatesExpr = [
|
|
643
|
+
const candidatesExpr = [primaryTestIdExpr, ...alternates.map((id) => toTypeScriptPomPatternExpression(id))].join(", ");
|
|
419
644
|
const clickMethod = createAsyncMethod(
|
|
420
645
|
name,
|
|
421
|
-
|
|
646
|
+
hasSelectorVariables ? [
|
|
422
647
|
...baseParameters,
|
|
423
648
|
createInlineParameter("wait", { type: "boolean", initializer: "true" }),
|
|
424
649
|
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
@@ -447,14 +672,14 @@ function generateClickMethod(methodName, formattedDataTestId, alternateFormatted
|
|
|
447
672
|
const noWaitArgs = argsForForward ? `${argsForForward}, false, annotationText` : "false, annotationText";
|
|
448
673
|
const noWaitMethod = createAsyncMethod(
|
|
449
674
|
noWaitName,
|
|
450
|
-
|
|
675
|
+
hasSelectorVariables ? [...baseParameters, createInlineParameter("annotationText", { type: "string", initializer: '""' })] : [createInlineParameter("annotationText", { type: "string", initializer: '""' })],
|
|
451
676
|
(writer) => {
|
|
452
677
|
writer.writeLine(`await this.${name}(${noWaitArgs});`);
|
|
453
678
|
}
|
|
454
679
|
);
|
|
455
680
|
return [clickMethod, noWaitMethod];
|
|
456
681
|
}
|
|
457
|
-
if (
|
|
682
|
+
if (hasSelectorVariables) {
|
|
458
683
|
return [
|
|
459
684
|
createAsyncMethod(
|
|
460
685
|
name,
|
|
@@ -464,7 +689,7 @@ function generateClickMethod(methodName, formattedDataTestId, alternateFormatted
|
|
|
464
689
|
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
465
690
|
],
|
|
466
691
|
(writer) => {
|
|
467
|
-
writer.writeLine(`await this.clickByTestId(
|
|
692
|
+
writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
|
|
468
693
|
}
|
|
469
694
|
),
|
|
470
695
|
createAsyncMethod(
|
|
@@ -484,7 +709,7 @@ function generateClickMethod(methodName, formattedDataTestId, alternateFormatted
|
|
|
484
709
|
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
485
710
|
],
|
|
486
711
|
(writer) => {
|
|
487
|
-
writer.writeLine(`await this.clickByTestId(
|
|
712
|
+
writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
|
|
488
713
|
}
|
|
489
714
|
),
|
|
490
715
|
createAsyncMethod(
|
|
@@ -496,31 +721,25 @@ function generateClickMethod(methodName, formattedDataTestId, alternateFormatted
|
|
|
496
721
|
)
|
|
497
722
|
];
|
|
498
723
|
}
|
|
499
|
-
function generateRadioMethod(methodName,
|
|
724
|
+
function generateRadioMethod(methodName, selector, parameters) {
|
|
500
725
|
const name = `select${methodName}`;
|
|
501
|
-
const
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
505
|
-
] : [createInlineParameter("annotationText", { type: "string", initializer: '""' })];
|
|
506
|
-
const testIdExpr = hasKey ? `\`${formattedDataTestId}\`` : `"${formattedDataTestId}"`;
|
|
726
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
727
|
+
const methodParameters = createParameters(selectorParams);
|
|
728
|
+
const testIdExpr = toTypeScriptPomPatternExpression(selector);
|
|
507
729
|
return [
|
|
508
|
-
createAsyncMethod(name,
|
|
730
|
+
createAsyncMethod(name, methodParameters, (writer) => {
|
|
509
731
|
writer.writeLine(`await this.clickByTestId(${testIdExpr}, annotationText);`);
|
|
510
732
|
})
|
|
511
733
|
];
|
|
512
734
|
}
|
|
513
|
-
function generateSelectMethod(methodName,
|
|
735
|
+
function generateSelectMethod(methodName, selector, parameters) {
|
|
514
736
|
const name = `select${methodName}`;
|
|
515
|
-
const
|
|
516
|
-
const selectorExpr =
|
|
737
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
738
|
+
const selectorExpr = `this.selectorForTestId(${toTypeScriptPomPatternExpression(selector)})`;
|
|
517
739
|
return [
|
|
518
740
|
createAsyncMethod(
|
|
519
741
|
name,
|
|
520
|
-
|
|
521
|
-
createInlineParameter("value", { type: "string" }),
|
|
522
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
523
|
-
],
|
|
742
|
+
createParameters(selectorParams),
|
|
524
743
|
(writer) => {
|
|
525
744
|
writer.writeLine(`const selector = ${selectorExpr};`);
|
|
526
745
|
writer.writeLine("await this.animateCursorToElement(selector, false, 500, annotationText);");
|
|
@@ -529,33 +748,28 @@ function generateSelectMethod(methodName, formattedDataTestId) {
|
|
|
529
748
|
)
|
|
530
749
|
];
|
|
531
750
|
}
|
|
532
|
-
function generateVSelectMethod(methodName,
|
|
751
|
+
function generateVSelectMethod(methodName, selector, parameters) {
|
|
533
752
|
const name = `select${methodName}`;
|
|
753
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
534
754
|
return [
|
|
535
755
|
createAsyncMethod(
|
|
536
756
|
name,
|
|
537
|
-
|
|
538
|
-
createInlineParameter("value", { type: "string" }),
|
|
539
|
-
createInlineParameter("timeOut", { type: "number", initializer: "500" }),
|
|
540
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
541
|
-
],
|
|
757
|
+
createParameters(selectorParams),
|
|
542
758
|
(writer) => {
|
|
543
|
-
writer.writeLine(`await this.selectVSelectByTestId(
|
|
759
|
+
writer.writeLine(`await this.selectVSelectByTestId(${toTypeScriptPomPatternExpression(selector)}, value, timeOut, annotationText);`);
|
|
544
760
|
}
|
|
545
761
|
)
|
|
546
762
|
];
|
|
547
763
|
}
|
|
548
|
-
function generateTypeMethod(methodName,
|
|
764
|
+
function generateTypeMethod(methodName, selector, parameters) {
|
|
549
765
|
const name = `type${methodName}`;
|
|
766
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
550
767
|
return [
|
|
551
768
|
createAsyncMethod(
|
|
552
769
|
name,
|
|
553
|
-
|
|
554
|
-
createInlineParameter("text", { type: "string" }),
|
|
555
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
556
|
-
],
|
|
770
|
+
createParameters(selectorParams),
|
|
557
771
|
(writer) => {
|
|
558
|
-
writer.writeLine(`await this.fillInputByTestId(
|
|
772
|
+
writer.writeLine(`await this.fillInputByTestId(${toTypeScriptPomPatternExpression(selector)}, text, annotationText);`);
|
|
559
773
|
}
|
|
560
774
|
)
|
|
561
775
|
];
|
|
@@ -570,30 +784,31 @@ function isAllDigits(value) {
|
|
|
570
784
|
}
|
|
571
785
|
return true;
|
|
572
786
|
}
|
|
573
|
-
function generateGetElementByDataTestId(methodName, nativeRole,
|
|
787
|
+
function generateGetElementByDataTestId(methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
|
|
574
788
|
const roleSuffix = upperFirst$1(nativeRole || "Element");
|
|
575
789
|
const baseName = upperFirst$1(methodName);
|
|
576
790
|
const numericSuffix = baseName.startsWith(roleSuffix) ? baseName.slice(roleSuffix.length) : "";
|
|
577
791
|
const hasRoleSuffix = baseName.endsWith(roleSuffix) || baseName.startsWith(roleSuffix) && isAllDigits(numericSuffix);
|
|
578
792
|
const propertyName = hasRoleSuffix ? `${baseName}` : `${baseName}${roleSuffix}`;
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
793
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
794
|
+
const indexedVariable = getIndexedPomPatternVariable(selector);
|
|
795
|
+
if (indexedVariable) {
|
|
796
|
+
const keyType = getPomParameter(selectorParams, indexedVariable)?.typeExpression || "string";
|
|
582
797
|
const keyedPropertyName = getterNameOverride ?? removeByKeySegment(propertyName);
|
|
583
798
|
return [
|
|
584
799
|
createClassGetter({
|
|
585
800
|
name: keyedPropertyName,
|
|
586
801
|
statements: [
|
|
587
|
-
`return this.keyedLocators((
|
|
802
|
+
`return this.keyedLocators((${indexedVariable}: ${keyType}) => this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}));`
|
|
588
803
|
]
|
|
589
804
|
})
|
|
590
805
|
];
|
|
591
806
|
}
|
|
592
807
|
const finalPropertyName = getterNameOverride ?? propertyName;
|
|
593
|
-
const alternates =
|
|
808
|
+
const alternates = uniquePomStringPatterns(selector, alternateSelectors).slice(1);
|
|
594
809
|
if (alternates.length > 0) {
|
|
595
|
-
const all = [
|
|
596
|
-
const locatorExpr = all.map((id) => `this.locatorByTestId(${
|
|
810
|
+
const all = [selector, ...alternates];
|
|
811
|
+
const locatorExpr = all.map((id) => `this.locatorByTestId(${toTypeScriptPomPatternExpression(id)})`).reduce((acc, next) => `${acc}.or(${next})`);
|
|
597
812
|
return [
|
|
598
813
|
createClassGetter({
|
|
599
814
|
name: finalPropertyName,
|
|
@@ -604,21 +819,22 @@ function generateGetElementByDataTestId(methodName, nativeRole, formattedDataTes
|
|
|
604
819
|
return [
|
|
605
820
|
createClassGetter({
|
|
606
821
|
name: finalPropertyName,
|
|
607
|
-
statements: [`return this.locatorByTestId(
|
|
822
|
+
statements: [`return this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)});`]
|
|
608
823
|
})
|
|
609
824
|
];
|
|
610
825
|
}
|
|
611
826
|
function generateNavigationMethod(args) {
|
|
612
|
-
const { targetPageObjectModelClass: target, baseMethodName,
|
|
827
|
+
const { targetPageObjectModelClass: target, baseMethodName, selector, alternateSelectors, parameters } = args;
|
|
613
828
|
const methodName = baseMethodName ? `goTo${upperFirst$1(baseMethodName)}` : `goTo${target.endsWith("Page") ? target.slice(0, -"Page".length) : target}`;
|
|
614
|
-
const
|
|
615
|
-
const
|
|
616
|
-
const
|
|
829
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
830
|
+
const methodParameters = createParameters(selectorParams);
|
|
831
|
+
const alternates = uniquePomStringPatterns(selector, alternateSelectors).slice(1);
|
|
832
|
+
const candidatesExpr = [toTypeScriptPomPatternExpression(selector), ...alternates.map((id) => toTypeScriptPomPatternExpression(id))].join(", ");
|
|
617
833
|
if (alternates.length > 0) {
|
|
618
834
|
return [
|
|
619
835
|
createClassMethod({
|
|
620
836
|
name: methodName,
|
|
621
|
-
parameters,
|
|
837
|
+
parameters: methodParameters,
|
|
622
838
|
returnType: `Fluent<${target}>`,
|
|
623
839
|
statements: (writer) => {
|
|
624
840
|
writer.write("return this.fluent(async () => ").block(() => {
|
|
@@ -646,11 +862,11 @@ function generateNavigationMethod(args) {
|
|
|
646
862
|
return [
|
|
647
863
|
createClassMethod({
|
|
648
864
|
name: methodName,
|
|
649
|
-
parameters,
|
|
865
|
+
parameters: methodParameters,
|
|
650
866
|
returnType: `Fluent<${target}>`,
|
|
651
867
|
statements: (writer) => {
|
|
652
868
|
writer.write("return this.fluent(async () => ").block(() => {
|
|
653
|
-
writer.writeLine(`await this.clickByTestId(
|
|
869
|
+
writer.writeLine(`await this.clickByTestId(${toTypeScriptPomPatternExpression(selector)});`);
|
|
654
870
|
writer.writeLine(`return new ${target}(this.page);`);
|
|
655
871
|
});
|
|
656
872
|
writer.writeLine(");");
|
|
@@ -658,15 +874,15 @@ function generateNavigationMethod(args) {
|
|
|
658
874
|
})
|
|
659
875
|
];
|
|
660
876
|
}
|
|
661
|
-
function generateViewObjectModelMembers(targetPageObjectModelClass, methodName, nativeRole,
|
|
877
|
+
function generateViewObjectModelMembers(targetPageObjectModelClass, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
|
|
662
878
|
const baseMethodName = nativeRole === "radio" ? methodName || "Radio" : methodName;
|
|
663
879
|
const members = generateGetElementByDataTestId(
|
|
664
880
|
baseMethodName,
|
|
665
881
|
nativeRole,
|
|
666
|
-
|
|
667
|
-
|
|
882
|
+
selector,
|
|
883
|
+
alternateSelectors,
|
|
668
884
|
getterNameOverride,
|
|
669
|
-
|
|
885
|
+
parameters
|
|
670
886
|
);
|
|
671
887
|
if (targetPageObjectModelClass) {
|
|
672
888
|
return [
|
|
@@ -674,25 +890,25 @@ function generateViewObjectModelMembers(targetPageObjectModelClass, methodName,
|
|
|
674
890
|
...generateNavigationMethod({
|
|
675
891
|
targetPageObjectModelClass,
|
|
676
892
|
baseMethodName,
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
893
|
+
selector,
|
|
894
|
+
alternateSelectors,
|
|
895
|
+
parameters
|
|
680
896
|
})
|
|
681
897
|
];
|
|
682
898
|
}
|
|
683
899
|
if (nativeRole === "select") {
|
|
684
|
-
return [...members, ...generateSelectMethod(baseMethodName,
|
|
900
|
+
return [...members, ...generateSelectMethod(baseMethodName, selector, parameters)];
|
|
685
901
|
}
|
|
686
902
|
if (nativeRole === "vselect") {
|
|
687
|
-
return [...members, ...generateVSelectMethod(baseMethodName,
|
|
903
|
+
return [...members, ...generateVSelectMethod(baseMethodName, selector, parameters)];
|
|
688
904
|
}
|
|
689
905
|
if (nativeRole === "input") {
|
|
690
|
-
return [...members, ...generateTypeMethod(baseMethodName,
|
|
906
|
+
return [...members, ...generateTypeMethod(baseMethodName, selector, parameters)];
|
|
691
907
|
}
|
|
692
908
|
if (nativeRole === "radio") {
|
|
693
|
-
return [...members, ...generateRadioMethod(baseMethodName || "Radio",
|
|
909
|
+
return [...members, ...generateRadioMethod(baseMethodName || "Radio", selector, parameters)];
|
|
694
910
|
}
|
|
695
|
-
return [...members, ...generateClickMethod(baseMethodName,
|
|
911
|
+
return [...members, ...generateClickMethod(baseMethodName, selector, alternateSelectors, parameters)];
|
|
696
912
|
}
|
|
697
913
|
function isSimpleExpressionNode(value) {
|
|
698
914
|
return value !== null && "type" in value && value.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION;
|
|
@@ -732,43 +948,72 @@ function buildPlaceholderParams(keys) {
|
|
|
732
948
|
params[k] = "__placeholder__";
|
|
733
949
|
return params;
|
|
734
950
|
}
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
951
|
+
const isNodeType = (node, type) => {
|
|
952
|
+
return node !== null && node.type === type;
|
|
953
|
+
};
|
|
954
|
+
const isStringLiteralNode = (node) => {
|
|
955
|
+
return isNodeType(node, "StringLiteral") && typeof node.value === "string";
|
|
956
|
+
};
|
|
957
|
+
const isIdentifierNode = (node) => {
|
|
958
|
+
return isNodeType(node, "Identifier") && typeof node.name === "string";
|
|
959
|
+
};
|
|
960
|
+
const isObjectPropertyNode = (node) => {
|
|
961
|
+
if (!isNodeType(node, "ObjectProperty"))
|
|
962
|
+
return false;
|
|
963
|
+
const n = node;
|
|
964
|
+
return typeof n.key === "object" && n.key !== null && typeof n.value === "object" && n.value !== null;
|
|
965
|
+
};
|
|
966
|
+
const isObjectExpressionNode = (node) => {
|
|
967
|
+
if (!isNodeType(node, "ObjectExpression"))
|
|
968
|
+
return false;
|
|
969
|
+
const n = node;
|
|
970
|
+
return Array.isArray(n.properties);
|
|
971
|
+
};
|
|
972
|
+
function materializeResolvedRouteTarget(target, paramKeys) {
|
|
973
|
+
if (typeof target === "string")
|
|
974
|
+
return target;
|
|
975
|
+
if (!paramKeys.length)
|
|
976
|
+
return target;
|
|
977
|
+
return {
|
|
978
|
+
...target,
|
|
979
|
+
params: buildPlaceholderParams(paramKeys)
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
function analyzeToDirectiveTarget(toDirective) {
|
|
983
|
+
if (!toDirective.exp) {
|
|
984
|
+
return {
|
|
985
|
+
kind: "unsupported",
|
|
986
|
+
rawSource: null,
|
|
987
|
+
reason: "missing-expression"
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
const rawSource = compilerCore.stringifyExpression(toDirective.exp).trim();
|
|
740
991
|
let expr;
|
|
741
992
|
try {
|
|
742
993
|
expr = parser.parseExpression(rawSource, { plugins: ["typescript"] });
|
|
743
|
-
} catch {
|
|
744
|
-
return
|
|
994
|
+
} catch (error) {
|
|
995
|
+
return {
|
|
996
|
+
kind: "parse-error",
|
|
997
|
+
rawSource,
|
|
998
|
+
reason: "parse-error",
|
|
999
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1000
|
+
};
|
|
745
1001
|
}
|
|
746
|
-
const isNodeType = (node, type) => {
|
|
747
|
-
return node !== null && node.type === type;
|
|
748
|
-
};
|
|
749
|
-
const isStringLiteralNode = (node) => {
|
|
750
|
-
return isNodeType(node, "StringLiteral") && typeof node.value === "string";
|
|
751
|
-
};
|
|
752
|
-
const isIdentifierNode = (node) => {
|
|
753
|
-
return isNodeType(node, "Identifier") && typeof node.name === "string";
|
|
754
|
-
};
|
|
755
|
-
const isObjectPropertyNode = (node) => {
|
|
756
|
-
if (!isNodeType(node, "ObjectProperty"))
|
|
757
|
-
return false;
|
|
758
|
-
const n = node;
|
|
759
|
-
return typeof n.key === "object" && n.key !== null && typeof n.value === "object" && n.value !== null;
|
|
760
|
-
};
|
|
761
|
-
const isObjectExpressionNode = (node) => {
|
|
762
|
-
if (!isNodeType(node, "ObjectExpression"))
|
|
763
|
-
return false;
|
|
764
|
-
const n = node;
|
|
765
|
-
return Array.isArray(n.properties);
|
|
766
|
-
};
|
|
767
1002
|
if (isStringLiteralNode(expr)) {
|
|
768
|
-
return
|
|
1003
|
+
return {
|
|
1004
|
+
kind: "resolved",
|
|
1005
|
+
rawSource,
|
|
1006
|
+
target: expr.value,
|
|
1007
|
+
routeNameKey: null,
|
|
1008
|
+
paramKeys: []
|
|
1009
|
+
};
|
|
769
1010
|
}
|
|
770
1011
|
if (!isObjectExpressionNode(expr)) {
|
|
771
|
-
return
|
|
1012
|
+
return {
|
|
1013
|
+
kind: "unsupported",
|
|
1014
|
+
rawSource,
|
|
1015
|
+
reason: "dynamic-expression"
|
|
1016
|
+
};
|
|
772
1017
|
}
|
|
773
1018
|
const getStringField = (fieldName) => {
|
|
774
1019
|
const prop = expr.properties.find((p) => {
|
|
@@ -789,7 +1034,7 @@ function getRouteLocationLikeFromToDirective(toDirective) {
|
|
|
789
1034
|
const key = p.key;
|
|
790
1035
|
return isIdentifierNode(key) && key.name === "params" || isStringLiteralNode(key) && key.value === "params";
|
|
791
1036
|
});
|
|
792
|
-
let
|
|
1037
|
+
let paramKeys = [];
|
|
793
1038
|
if (paramsProp && isObjectPropertyNode(paramsProp) && isObjectExpressionNode(paramsProp.value)) {
|
|
794
1039
|
const keys = [];
|
|
795
1040
|
for (const prop of paramsProp.value.properties) {
|
|
@@ -801,47 +1046,50 @@ function getRouteLocationLikeFromToDirective(toDirective) {
|
|
|
801
1046
|
else if (isStringLiteralNode(key))
|
|
802
1047
|
keys.push(key.value);
|
|
803
1048
|
}
|
|
804
|
-
|
|
805
|
-
params = buildPlaceholderParams(Array.from(new Set(keys)));
|
|
806
|
-
}
|
|
1049
|
+
paramKeys = Array.from(new Set(keys));
|
|
807
1050
|
}
|
|
808
1051
|
if (name) {
|
|
809
|
-
|
|
1052
|
+
const trimmed = name.trim();
|
|
1053
|
+
if (!trimmed.length) {
|
|
1054
|
+
return {
|
|
1055
|
+
kind: "unsupported",
|
|
1056
|
+
rawSource,
|
|
1057
|
+
reason: "missing-name-or-path"
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
return {
|
|
1061
|
+
kind: "resolved",
|
|
1062
|
+
rawSource,
|
|
1063
|
+
target: { name },
|
|
1064
|
+
routeNameKey: toPascalCaseRouteKey(trimmed),
|
|
1065
|
+
paramKeys
|
|
1066
|
+
};
|
|
810
1067
|
}
|
|
811
1068
|
if (path2) {
|
|
812
|
-
return {
|
|
1069
|
+
return {
|
|
1070
|
+
kind: "resolved",
|
|
1071
|
+
rawSource,
|
|
1072
|
+
target: { path: path2 },
|
|
1073
|
+
routeNameKey: null,
|
|
1074
|
+
paramKeys
|
|
1075
|
+
};
|
|
813
1076
|
}
|
|
814
|
-
return
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
return null;
|
|
820
|
-
const name = to.name;
|
|
821
|
-
if (typeof name !== "string")
|
|
822
|
-
return null;
|
|
823
|
-
const trimmed = name.trim();
|
|
824
|
-
if (!trimmed.length)
|
|
825
|
-
return null;
|
|
826
|
-
return toPascalCaseRouteKey(trimmed);
|
|
827
|
-
}
|
|
828
|
-
function getRouteNameKeyFromToDirective(toDirective) {
|
|
829
|
-
const objectName = toDirectiveObjectFieldNameValue$1(toDirective);
|
|
830
|
-
if (objectName)
|
|
831
|
-
return objectName;
|
|
832
|
-
return null;
|
|
1077
|
+
return {
|
|
1078
|
+
kind: "unsupported",
|
|
1079
|
+
rawSource,
|
|
1080
|
+
reason: "missing-name-or-path"
|
|
1081
|
+
};
|
|
833
1082
|
}
|
|
834
1083
|
function tryResolveToDirectiveTargetComponentName(toDirective) {
|
|
835
|
-
const
|
|
836
|
-
if (
|
|
837
|
-
const resolved = resolveToComponentName(
|
|
1084
|
+
const analysis = analyzeToDirectiveTarget(toDirective);
|
|
1085
|
+
if (analysis.kind === "resolved" && resolveToComponentName) {
|
|
1086
|
+
const resolved = resolveToComponentName(materializeResolvedRouteTarget(analysis.target, analysis.paramKeys));
|
|
838
1087
|
if (resolved)
|
|
839
1088
|
return resolved;
|
|
840
1089
|
}
|
|
841
|
-
|
|
842
|
-
if (!key || !routeNameToComponentName)
|
|
1090
|
+
if (analysis.kind !== "resolved" || !analysis.routeNameKey || !routeNameToComponentName)
|
|
843
1091
|
return null;
|
|
844
|
-
return routeNameToComponentName.get(
|
|
1092
|
+
return routeNameToComponentName.get(analysis.routeNameKey) ?? null;
|
|
845
1093
|
}
|
|
846
1094
|
function getDataTestIdFromGroupOption(text) {
|
|
847
1095
|
return text.replace(/[-_]/g, " ").split(" ").filter((a) => a).map((str) => {
|
|
@@ -876,11 +1124,81 @@ function staticAttributeValue(value) {
|
|
|
876
1124
|
return { kind: "static", value };
|
|
877
1125
|
}
|
|
878
1126
|
function templateAttributeValue(template) {
|
|
879
|
-
|
|
1127
|
+
const parsedTemplate = tryParseTemplateFragment(template);
|
|
1128
|
+
if (!parsedTemplate) {
|
|
1129
|
+
throw new Error(`[vue-pom-generator] Failed to parse generated template fragment: ${template}`);
|
|
1130
|
+
}
|
|
1131
|
+
return { kind: "template", template, parsedTemplate };
|
|
880
1132
|
}
|
|
881
1133
|
function getAttributeValueText(value) {
|
|
882
1134
|
return value.kind === "static" ? value.value : value.template;
|
|
883
1135
|
}
|
|
1136
|
+
function getVueExpressionSource(expression, ...preferredViews) {
|
|
1137
|
+
if (!expression) {
|
|
1138
|
+
return "";
|
|
1139
|
+
}
|
|
1140
|
+
for (const view of preferredViews) {
|
|
1141
|
+
let value = "";
|
|
1142
|
+
switch (view) {
|
|
1143
|
+
case "content":
|
|
1144
|
+
value = expression.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION ? expression.content : "";
|
|
1145
|
+
break;
|
|
1146
|
+
case "loc":
|
|
1147
|
+
value = expression.loc?.source ?? "";
|
|
1148
|
+
break;
|
|
1149
|
+
case "compiled":
|
|
1150
|
+
try {
|
|
1151
|
+
value = compilerCore.stringifyExpression(expression);
|
|
1152
|
+
} catch {
|
|
1153
|
+
value = "";
|
|
1154
|
+
}
|
|
1155
|
+
break;
|
|
1156
|
+
default:
|
|
1157
|
+
value = "";
|
|
1158
|
+
break;
|
|
1159
|
+
}
|
|
1160
|
+
const trimmed = value.trim();
|
|
1161
|
+
if (trimmed) {
|
|
1162
|
+
return trimmed;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
return "";
|
|
1166
|
+
}
|
|
1167
|
+
function tryGetExistingVueExpressionAst(expression) {
|
|
1168
|
+
if (!expression) {
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
const ast = "ast" in expression ? expression.ast : null;
|
|
1172
|
+
return ast && "type" in ast ? ast : null;
|
|
1173
|
+
}
|
|
1174
|
+
function tryParseBabelExpressionFromSource(source, plugins) {
|
|
1175
|
+
const trimmed = source.trim();
|
|
1176
|
+
if (!trimmed) {
|
|
1177
|
+
return null;
|
|
1178
|
+
}
|
|
1179
|
+
try {
|
|
1180
|
+
return parser.parseExpression(trimmed, { plugins });
|
|
1181
|
+
} catch {
|
|
1182
|
+
return null;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
function tryGetVueExpressionAst(expression, options) {
|
|
1186
|
+
if (!expression) {
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1189
|
+
if (options?.preferExistingAst !== false) {
|
|
1190
|
+
const existingAst = tryGetExistingVueExpressionAst(expression);
|
|
1191
|
+
if (existingAst) {
|
|
1192
|
+
return existingAst;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
const source = getVueExpressionSource(expression, ...options?.preferredViews ?? ["content", "loc", "compiled"]);
|
|
1196
|
+
return source ? tryParseBabelExpressionFromSource(source, options?.plugins ?? ["typescript"]) : null;
|
|
1197
|
+
}
|
|
1198
|
+
function tryGetDirectiveBabelAst(directive, options) {
|
|
1199
|
+
const exp = directive.exp && (directive.exp.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION || directive.exp.type === compilerCore.NodeTypes.COMPOUND_EXPRESSION) ? directive.exp : null;
|
|
1200
|
+
return tryGetVueExpressionAst(exp, options);
|
|
1201
|
+
}
|
|
884
1202
|
function toPascalCase(str) {
|
|
885
1203
|
const cleaned = (str ?? "").replace(/\$\{[^}]*\}/g, " ").replace(/[^a-z0-9]+/gi, " ").trim();
|
|
886
1204
|
if (!cleaned) {
|
|
@@ -922,32 +1240,14 @@ function tryGetClickDirective(node) {
|
|
|
922
1240
|
function nodeHasClickDirective(node) {
|
|
923
1241
|
return tryGetClickDirective(node) !== void 0;
|
|
924
1242
|
}
|
|
925
|
-
function
|
|
1243
|
+
function findTemplateSlotScopeExpression(node) {
|
|
926
1244
|
if (node.tag !== "template") {
|
|
927
1245
|
return null;
|
|
928
1246
|
}
|
|
929
1247
|
const slotProp = node.props.find((prop) => {
|
|
930
1248
|
return prop.type === compilerCore.NodeTypes.DIRECTIVE && prop.name === "slot";
|
|
931
1249
|
});
|
|
932
|
-
|
|
933
|
-
if (slotProp.exp.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION) {
|
|
934
|
-
return slotProp.exp.content;
|
|
935
|
-
}
|
|
936
|
-
if (slotProp.exp.type === compilerCore.NodeTypes.COMPOUND_EXPRESSION) {
|
|
937
|
-
return compilerCore.stringifyExpression(slotProp.exp);
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
return null;
|
|
941
|
-
}
|
|
942
|
-
function isSimpleScopeIdentifier(value) {
|
|
943
|
-
if (!value) {
|
|
944
|
-
return false;
|
|
945
|
-
}
|
|
946
|
-
try {
|
|
947
|
-
return types.isIdentifier(parser.parseExpression(value, { plugins: ["typescript"] }));
|
|
948
|
-
} catch {
|
|
949
|
-
return false;
|
|
950
|
-
}
|
|
1250
|
+
return slotProp?.exp && (slotProp.exp.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION || slotProp.exp.type === compilerCore.NodeTypes.COMPOUND_EXPRESSION) ? slotProp.exp : null;
|
|
951
1251
|
}
|
|
952
1252
|
function buildSlotScopeFallbackKeyExpression(identifier) {
|
|
953
1253
|
return `${identifier}.key ?? ${identifier}.data?.id ?? ${identifier}.id ?? ${identifier}.value ?? ${identifier}`;
|
|
@@ -1049,43 +1349,64 @@ function tryGetSlotScopeKeyCandidate(node) {
|
|
|
1049
1349
|
}
|
|
1050
1350
|
return best;
|
|
1051
1351
|
}
|
|
1052
|
-
function
|
|
1053
|
-
const
|
|
1054
|
-
if (
|
|
1352
|
+
function tryGetTemplateSlotScopeBindingNode(expression) {
|
|
1353
|
+
const ast = tryGetExistingVueExpressionAst(expression);
|
|
1354
|
+
if (ast) {
|
|
1355
|
+
if (types.isArrowFunctionExpression(ast)) {
|
|
1356
|
+
return ast.params[0] ?? null;
|
|
1357
|
+
}
|
|
1358
|
+
return ast;
|
|
1359
|
+
}
|
|
1360
|
+
const rawSource = getVueExpressionSource(expression, "content", "loc", "compiled");
|
|
1361
|
+
if (!rawSource) {
|
|
1055
1362
|
return null;
|
|
1056
1363
|
}
|
|
1057
|
-
|
|
1058
|
-
return
|
|
1364
|
+
try {
|
|
1365
|
+
return parser.parseExpression(rawSource, { plugins: ["typescript"] });
|
|
1366
|
+
} catch {
|
|
1059
1367
|
}
|
|
1060
1368
|
try {
|
|
1061
|
-
const parsed = parser.parse(`(${
|
|
1369
|
+
const parsed = parser.parse(`(${rawSource}) => {}`, {
|
|
1062
1370
|
sourceType: "module",
|
|
1063
1371
|
plugins: ["typescript"]
|
|
1064
1372
|
});
|
|
1065
1373
|
const statement = parsed.program.body[0];
|
|
1066
1374
|
if (statement && types.isExpressionStatement(statement) && types.isArrowFunctionExpression(statement.expression)) {
|
|
1067
|
-
return
|
|
1375
|
+
return statement.expression.params[0] ?? null;
|
|
1068
1376
|
}
|
|
1069
1377
|
} catch {
|
|
1378
|
+
return null;
|
|
1070
1379
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1380
|
+
return null;
|
|
1381
|
+
}
|
|
1382
|
+
function toResolvedTemplateFragment(source) {
|
|
1383
|
+
const templateLiteral = tryUnwrapTemplateLiteralSource(source);
|
|
1384
|
+
if (templateLiteral) {
|
|
1385
|
+
return {
|
|
1386
|
+
template: templateLiteral.template,
|
|
1387
|
+
rawExpression: null
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
return toInterpolatedTemplateFragment(source);
|
|
1391
|
+
}
|
|
1392
|
+
function toResolvedKeyInfo(selectorSource, runtimeSource = selectorSource) {
|
|
1393
|
+
const selectorFragment = selectorSource ? toResolvedTemplateFragment(selectorSource) : null;
|
|
1394
|
+
const runtimeFragment = runtimeSource ? toResolvedTemplateFragment(runtimeSource) : null;
|
|
1395
|
+
const selectorTemplate = selectorFragment?.template ?? runtimeFragment?.template ?? null;
|
|
1396
|
+
const runtimeTemplate = runtimeFragment?.template ?? selectorFragment?.template ?? null;
|
|
1397
|
+
if (!selectorTemplate || !runtimeTemplate) {
|
|
1398
|
+
return null;
|
|
1087
1399
|
}
|
|
1088
|
-
return
|
|
1400
|
+
return {
|
|
1401
|
+
selectorFragment: selectorTemplate,
|
|
1402
|
+
runtimeFragment: runtimeTemplate,
|
|
1403
|
+
rawExpression: runtimeFragment?.rawExpression ?? selectorFragment?.rawExpression ?? null
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
function tryGetTemplateSlotScopeKeyInfo(expression) {
|
|
1407
|
+
const bindingNode = tryGetTemplateSlotScopeBindingNode(expression);
|
|
1408
|
+
const candidateExpression = bindingNode ? tryGetSlotScopeKeyCandidate(bindingNode)?.expression ?? null : null;
|
|
1409
|
+
return candidateExpression ? toResolvedKeyInfo(candidateExpression) : null;
|
|
1089
1410
|
}
|
|
1090
1411
|
function nodeHasToDirective(node) {
|
|
1091
1412
|
const toDirective = findDirectiveByName(node, "bind", "to");
|
|
@@ -1094,51 +1415,160 @@ function nodeHasToDirective(node) {
|
|
|
1094
1415
|
}
|
|
1095
1416
|
return void 0;
|
|
1096
1417
|
}
|
|
1097
|
-
function nodeHasForDirective(node) {
|
|
1098
|
-
return node.props.some(
|
|
1099
|
-
(attr) => attr.type === compilerCore.NodeTypes.DIRECTIVE && attr.name === "for"
|
|
1100
|
-
);
|
|
1101
|
-
}
|
|
1102
1418
|
function getKeyDirective(node) {
|
|
1103
1419
|
return findDirectiveByName(node, "bind", "key") ?? null;
|
|
1104
1420
|
}
|
|
1105
|
-
function
|
|
1106
|
-
const
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
return `\${${rawSource}}`;
|
|
1421
|
+
function tryUnwrapTemplateLiteralSource(source) {
|
|
1422
|
+
const rawSource = source.trim();
|
|
1423
|
+
if (!rawSource) {
|
|
1424
|
+
return null;
|
|
1110
1425
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1426
|
+
let ast = null;
|
|
1427
|
+
try {
|
|
1428
|
+
ast = parser.parseExpression(rawSource, { plugins: ["typescript"] });
|
|
1429
|
+
} catch {
|
|
1430
|
+
return null;
|
|
1431
|
+
}
|
|
1432
|
+
if (!ast || !types.isTemplateLiteral(ast)) {
|
|
1433
|
+
return null;
|
|
1434
|
+
}
|
|
1435
|
+
const cooked = ast.quasis.map((quasi) => quasi.value.cooked ?? "").join("");
|
|
1436
|
+
try {
|
|
1437
|
+
const start = typeof ast.start === "number" ? ast.start + 1 : 1;
|
|
1438
|
+
const end = typeof ast.end === "number" ? ast.end - 1 : rawSource.length - 1;
|
|
1439
|
+
return {
|
|
1440
|
+
template: rawSource.slice(start, end) || cooked,
|
|
1441
|
+
expressionCount: ast.expressions.length
|
|
1442
|
+
};
|
|
1443
|
+
} catch {
|
|
1444
|
+
return {
|
|
1445
|
+
template: cooked,
|
|
1446
|
+
expressionCount: ast.expressions.length
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
function tryUnwrapTemplateLiteralExpressionSource(expression) {
|
|
1451
|
+
const rawSource = getVueExpressionSource(expression, "loc", "compiled");
|
|
1452
|
+
return rawSource ? tryUnwrapTemplateLiteralSource(rawSource) : null;
|
|
1453
|
+
}
|
|
1454
|
+
function tryParseTemplateFragment(fragment) {
|
|
1455
|
+
if (!fragment) {
|
|
1456
|
+
return null;
|
|
1457
|
+
}
|
|
1458
|
+
try {
|
|
1459
|
+
const source = `\`${fragment}\``;
|
|
1460
|
+
const ast = parser.parseExpression(source, { plugins: ["typescript"] });
|
|
1461
|
+
return types.isTemplateLiteral(ast) ? { source, templateLiteral: ast } : null;
|
|
1462
|
+
} catch {
|
|
1463
|
+
return null;
|
|
1115
1464
|
}
|
|
1116
|
-
|
|
1465
|
+
}
|
|
1466
|
+
function getTemplateExpressionSource(parsedTemplate, index) {
|
|
1467
|
+
const expression = parsedTemplate.templateLiteral.expressions[index];
|
|
1468
|
+
if (!expression) {
|
|
1469
|
+
return null;
|
|
1470
|
+
}
|
|
1471
|
+
const start = typeof expression.start === "number" ? expression.start : null;
|
|
1472
|
+
const end = typeof expression.end === "number" ? expression.end : null;
|
|
1473
|
+
if (start === null || end === null) {
|
|
1474
|
+
return null;
|
|
1475
|
+
}
|
|
1476
|
+
return parsedTemplate.source.slice(start, end);
|
|
1477
|
+
}
|
|
1478
|
+
function getSingleExpressionTemplateFragment(parsedTemplate) {
|
|
1479
|
+
const { templateLiteral } = parsedTemplate;
|
|
1480
|
+
if (templateLiteral.expressions.length !== 1 || templateLiteral.quasis.length !== 2) {
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1483
|
+
const expressionSource = getTemplateExpressionSource(parsedTemplate, 0);
|
|
1484
|
+
if (expressionSource === null) {
|
|
1485
|
+
return null;
|
|
1486
|
+
}
|
|
1487
|
+
return {
|
|
1488
|
+
prefix: templateLiteral.quasis[0]?.value.raw ?? "",
|
|
1489
|
+
expressionSource,
|
|
1490
|
+
suffix: templateLiteral.quasis[1]?.value.raw ?? ""
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
function templateFragmentContainsSingleExpression(container, candidate) {
|
|
1494
|
+
const containerFragment = getSingleExpressionTemplateFragment(container);
|
|
1495
|
+
const candidateFragment = getSingleExpressionTemplateFragment(candidate);
|
|
1496
|
+
if (!containerFragment || !candidateFragment) {
|
|
1497
|
+
return false;
|
|
1498
|
+
}
|
|
1499
|
+
return containerFragment.expressionSource === candidateFragment.expressionSource && containerFragment.prefix.endsWith(candidateFragment.prefix) && containerFragment.suffix.startsWith(candidateFragment.suffix);
|
|
1500
|
+
}
|
|
1501
|
+
function hasTemplateInterpolationExpressions(fragment) {
|
|
1502
|
+
return (tryParseTemplateFragment(fragment)?.templateLiteral.expressions.length ?? 0) > 0;
|
|
1503
|
+
}
|
|
1504
|
+
function toInterpolatedTemplateFragment(fragment) {
|
|
1505
|
+
if (!fragment) {
|
|
1506
|
+
return null;
|
|
1507
|
+
}
|
|
1508
|
+
if (hasTemplateInterpolationExpressions(fragment)) {
|
|
1509
|
+
return { template: fragment, rawExpression: null };
|
|
1510
|
+
}
|
|
1511
|
+
return {
|
|
1512
|
+
template: `\${${fragment}}`,
|
|
1513
|
+
rawExpression: fragment
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
function renderTemplateLiteralExpression(templateValue) {
|
|
1517
|
+
const templateLiteralSource = templateValue.parsedTemplate.source;
|
|
1518
|
+
const templateLiteral = templateValue.parsedTemplate.templateLiteral;
|
|
1519
|
+
const writer = createTypeScriptWriter();
|
|
1520
|
+
writer.write("`");
|
|
1521
|
+
for (let i = 0; i < templateLiteral.quasis.length; i += 1) {
|
|
1522
|
+
writer.write(templateLiteral.quasis[i]?.value.raw ?? "");
|
|
1523
|
+
const interpolation = templateLiteral.expressions[i];
|
|
1524
|
+
if (!interpolation) {
|
|
1525
|
+
continue;
|
|
1526
|
+
}
|
|
1527
|
+
const start = typeof interpolation.start === "number" ? interpolation.start : null;
|
|
1528
|
+
const end = typeof interpolation.end === "number" ? interpolation.end : null;
|
|
1529
|
+
if (start === null || end === null) {
|
|
1530
|
+
return templateLiteralSource;
|
|
1531
|
+
}
|
|
1532
|
+
writer.write("${");
|
|
1533
|
+
writer.write(templateLiteralSource.slice(start, end));
|
|
1534
|
+
writer.write("}");
|
|
1535
|
+
}
|
|
1536
|
+
writer.write("`");
|
|
1537
|
+
return writer.toString();
|
|
1538
|
+
}
|
|
1539
|
+
function getKeyDirectiveExpression(node) {
|
|
1540
|
+
const keyDirective = getKeyDirective(node);
|
|
1541
|
+
return keyDirective?.exp && (keyDirective.exp.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION || keyDirective.exp.type === compilerCore.NodeTypes.COMPOUND_EXPRESSION) ? keyDirective.exp : null;
|
|
1542
|
+
}
|
|
1543
|
+
function getKeyDirectiveInfo(node) {
|
|
1544
|
+
const keyExpression = getKeyDirectiveExpression(node);
|
|
1545
|
+
if (!keyExpression) {
|
|
1546
|
+
return null;
|
|
1547
|
+
}
|
|
1548
|
+
const selectorSource = getVueExpressionSource(keyExpression, "compiled", "loc");
|
|
1549
|
+
const runtimeSource = getVueExpressionSource(keyExpression, "loc", "compiled");
|
|
1550
|
+
return toResolvedKeyInfo(selectorSource, runtimeSource);
|
|
1117
1551
|
}
|
|
1118
1552
|
function getModelBindingValues(node) {
|
|
1119
1553
|
let vModel = "";
|
|
1120
1554
|
const vModelDirective = findDirectiveByName(node, "model");
|
|
1121
|
-
if (vModelDirective?.exp
|
|
1122
|
-
vModel = toPascalCase(vModelDirective.exp
|
|
1555
|
+
if (vModelDirective?.exp && (vModelDirective.exp.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION || vModelDirective.exp.type === compilerCore.NodeTypes.COMPOUND_EXPRESSION)) {
|
|
1556
|
+
vModel = toPascalCase(getVueExpressionSource(vModelDirective.exp, "loc", "content"));
|
|
1123
1557
|
}
|
|
1124
1558
|
let modelValue = null;
|
|
1125
1559
|
const modelValueDirective = findDirectiveByName(node, "bind", "modelValue");
|
|
1126
|
-
|
|
1127
|
-
|
|
1560
|
+
const modelValueAst = modelValueDirective ? tryGetDirectiveBabelAst(modelValueDirective, {
|
|
1561
|
+
preferredViews: ["loc", "compiled"],
|
|
1562
|
+
plugins: ["typescript"],
|
|
1563
|
+
preferExistingAst: false
|
|
1564
|
+
}) : null;
|
|
1565
|
+
if (modelValueAst) {
|
|
1566
|
+
const { name: mv } = getClickHandlerNameFromAst(modelValueAst);
|
|
1128
1567
|
modelValue = mv;
|
|
1129
1568
|
}
|
|
1130
1569
|
return { vModel, modelValue };
|
|
1131
1570
|
}
|
|
1132
|
-
function
|
|
1133
|
-
if (node.isSelfClosing) {
|
|
1134
|
-
const hasForDirective = nodeHasForDirective(node);
|
|
1135
|
-
if (hasForDirective) {
|
|
1136
|
-
return getKeyDirectiveValue(node);
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
return null;
|
|
1140
|
-
}
|
|
1141
|
-
function getIdOrName(node) {
|
|
1571
|
+
function getStaticIdOrNameHint(node) {
|
|
1142
1572
|
let idAttr = findAttributeByKey(node, "id");
|
|
1143
1573
|
if (!idAttr) {
|
|
1144
1574
|
idAttr = findAttributeByKey(node, "name");
|
|
@@ -1146,8 +1576,9 @@ function getIdOrName(node) {
|
|
|
1146
1576
|
let identifier = idAttr?.value?.content ?? "";
|
|
1147
1577
|
if (!identifier) {
|
|
1148
1578
|
const dynamicIdAttr = findDirectiveByName(node, "bind", "id");
|
|
1149
|
-
|
|
1150
|
-
|
|
1579
|
+
const dynamicNameAttr = findDirectiveByName(node, "bind", "name");
|
|
1580
|
+
if (dynamicIdAttr?.exp || dynamicNameAttr?.exp) {
|
|
1581
|
+
return "";
|
|
1151
1582
|
}
|
|
1152
1583
|
}
|
|
1153
1584
|
if (identifier.includes("-")) {
|
|
@@ -1158,20 +1589,20 @@ function getIdOrName(node) {
|
|
|
1158
1589
|
}
|
|
1159
1590
|
return identifier;
|
|
1160
1591
|
}
|
|
1161
|
-
function
|
|
1592
|
+
function getContainedInSlotDataKeyInfo(node, hierarchyMap2) {
|
|
1162
1593
|
let parent = getParent(hierarchyMap2, node);
|
|
1163
1594
|
while (parent) {
|
|
1164
1595
|
if (parent.type === compilerCore.NodeTypes.ELEMENT && parent.tag === "template") {
|
|
1165
|
-
const
|
|
1166
|
-
if (
|
|
1167
|
-
return
|
|
1596
|
+
const slotScopeExpression = findTemplateSlotScopeExpression(parent);
|
|
1597
|
+
if (slotScopeExpression) {
|
|
1598
|
+
return tryGetTemplateSlotScopeKeyInfo(slotScopeExpression);
|
|
1168
1599
|
}
|
|
1169
1600
|
}
|
|
1170
1601
|
parent = getParent(hierarchyMap2, parent);
|
|
1171
1602
|
}
|
|
1172
1603
|
return null;
|
|
1173
1604
|
}
|
|
1174
|
-
function
|
|
1605
|
+
function getContainedInVForDirectiveKeyInfo(context, node, hierarchyMap2) {
|
|
1175
1606
|
if (!context.scopes.vFor || context.scopes.vFor === 0) {
|
|
1176
1607
|
return null;
|
|
1177
1608
|
}
|
|
@@ -1180,8 +1611,7 @@ function getContainedInVForDirectiveKeyValue(context, node, hierarchyMap2) {
|
|
|
1180
1611
|
if (parent.type === compilerCore.NodeTypes.ELEMENT) {
|
|
1181
1612
|
const forDirective = findDirectiveByName(parent, "for");
|
|
1182
1613
|
if (forDirective) {
|
|
1183
|
-
|
|
1184
|
-
return keyValue;
|
|
1614
|
+
return getKeyDirectiveInfo(parent);
|
|
1185
1615
|
}
|
|
1186
1616
|
}
|
|
1187
1617
|
parent = getParent(hierarchyMap2, parent);
|
|
@@ -1207,13 +1637,7 @@ function tryGetContainedInStaticVForSourceLiteralValues(context, _node, _hierarc
|
|
|
1207
1637
|
if (simpleSourceExp.constType === compilerCore.ConstantTypes.NOT_CONSTANT) {
|
|
1208
1638
|
return null;
|
|
1209
1639
|
}
|
|
1210
|
-
const iterableRaw = (
|
|
1211
|
-
try {
|
|
1212
|
-
return compilerCore.stringifyExpression(simpleSourceExp).trim();
|
|
1213
|
-
} catch {
|
|
1214
|
-
return (simpleSourceExp.loc?.source ?? "").trim();
|
|
1215
|
-
}
|
|
1216
|
-
})();
|
|
1640
|
+
const iterableRaw = getVueExpressionSource(simpleSourceExp, "compiled", "loc");
|
|
1217
1641
|
if (!iterableRaw) {
|
|
1218
1642
|
return null;
|
|
1219
1643
|
}
|
|
@@ -1269,91 +1693,95 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1269
1693
|
return null;
|
|
1270
1694
|
}
|
|
1271
1695
|
const exp = handlerDirective.exp;
|
|
1272
|
-
const source = (exp
|
|
1696
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
1273
1697
|
if (!source) {
|
|
1274
1698
|
return null;
|
|
1275
1699
|
}
|
|
1276
1700
|
const mergeKey = `handler:expr:${source}`;
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1701
|
+
const expr = tryGetDirectiveBabelAst(handlerDirective, {
|
|
1702
|
+
preferredViews: ["content", "compiled"],
|
|
1703
|
+
plugins: ["typescript", "jsx"],
|
|
1704
|
+
// Vue's compiler AST can encode `_ctx.foo` as an Identifier name instead of a MemberExpression.
|
|
1705
|
+
// That is fine for Vue codegen, but our semantic-name extraction needs a normal Babel parse tree.
|
|
1706
|
+
preferExistingAst: false
|
|
1707
|
+
});
|
|
1708
|
+
if (!expr) {
|
|
1281
1709
|
return null;
|
|
1282
1710
|
}
|
|
1283
|
-
const
|
|
1711
|
+
const isNodeType2 = (node2, type) => {
|
|
1284
1712
|
return node2 !== null && node2.type === type;
|
|
1285
1713
|
};
|
|
1286
|
-
const
|
|
1287
|
-
return
|
|
1714
|
+
const isIdentifierNode2 = (node2) => {
|
|
1715
|
+
return isNodeType2(node2, "Identifier") && typeof node2.name === "string";
|
|
1288
1716
|
};
|
|
1289
|
-
const
|
|
1290
|
-
return
|
|
1717
|
+
const isStringLiteralNode2 = (node2) => {
|
|
1718
|
+
return isNodeType2(node2, "StringLiteral") && typeof node2.value === "string";
|
|
1291
1719
|
};
|
|
1292
1720
|
const isBooleanLiteralNode = (node2) => {
|
|
1293
|
-
return
|
|
1721
|
+
return isNodeType2(node2, "BooleanLiteral") && typeof node2.value === "boolean";
|
|
1294
1722
|
};
|
|
1295
1723
|
const isNumericLiteralNode = (node2) => {
|
|
1296
|
-
return
|
|
1724
|
+
return isNodeType2(node2, "NumericLiteral") && typeof node2.value === "number";
|
|
1297
1725
|
};
|
|
1298
1726
|
const isNullLiteralNode = (node2) => {
|
|
1299
|
-
return
|
|
1727
|
+
return isNodeType2(node2, "NullLiteral");
|
|
1300
1728
|
};
|
|
1301
1729
|
const isMemberExpressionNode = (node2) => {
|
|
1302
|
-
if (!
|
|
1730
|
+
if (!isNodeType2(node2, "MemberExpression"))
|
|
1303
1731
|
return false;
|
|
1304
1732
|
const n = node2;
|
|
1305
1733
|
return typeof n.computed === "boolean" && typeof n.object === "object" && n.object !== null && typeof n.property === "object" && n.property !== null;
|
|
1306
1734
|
};
|
|
1307
1735
|
const isCallExpressionNode = (node2) => {
|
|
1308
|
-
if (!
|
|
1736
|
+
if (!isNodeType2(node2, "CallExpression"))
|
|
1309
1737
|
return false;
|
|
1310
1738
|
const n = node2;
|
|
1311
1739
|
return typeof n.callee === "object" && n.callee !== null && Array.isArray(n.arguments);
|
|
1312
1740
|
};
|
|
1313
1741
|
const isAwaitExpressionNode = (node2) => {
|
|
1314
|
-
if (!
|
|
1742
|
+
if (!isNodeType2(node2, "AwaitExpression"))
|
|
1315
1743
|
return false;
|
|
1316
1744
|
const n = node2;
|
|
1317
1745
|
return typeof n.argument === "object" && n.argument !== null;
|
|
1318
1746
|
};
|
|
1319
1747
|
const isAssignmentExpressionNode = (node2) => {
|
|
1320
|
-
if (!
|
|
1748
|
+
if (!isNodeType2(node2, "AssignmentExpression"))
|
|
1321
1749
|
return false;
|
|
1322
1750
|
const n = node2;
|
|
1323
1751
|
return typeof n.left === "object" && n.left !== null && typeof n.right === "object" && n.right !== null;
|
|
1324
1752
|
};
|
|
1325
1753
|
const isArrowFunctionExpressionNode = (node2) => {
|
|
1326
|
-
if (!
|
|
1754
|
+
if (!isNodeType2(node2, "ArrowFunctionExpression"))
|
|
1327
1755
|
return false;
|
|
1328
1756
|
const n = node2;
|
|
1329
1757
|
return typeof n.body === "object" && n.body !== null;
|
|
1330
1758
|
};
|
|
1331
1759
|
const isBlockStatementNode = (node2) => {
|
|
1332
|
-
if (!
|
|
1760
|
+
if (!isNodeType2(node2, "BlockStatement"))
|
|
1333
1761
|
return false;
|
|
1334
1762
|
const n = node2;
|
|
1335
1763
|
return Array.isArray(n.body);
|
|
1336
1764
|
};
|
|
1337
1765
|
const isExpressionStatementNode = (node2) => {
|
|
1338
|
-
if (!
|
|
1766
|
+
if (!isNodeType2(node2, "ExpressionStatement"))
|
|
1339
1767
|
return false;
|
|
1340
1768
|
const n = node2;
|
|
1341
1769
|
return typeof n.expression === "object" && n.expression !== null;
|
|
1342
1770
|
};
|
|
1343
1771
|
const isReturnStatementNode = (node2) => {
|
|
1344
|
-
if (!
|
|
1772
|
+
if (!isNodeType2(node2, "ReturnStatement"))
|
|
1345
1773
|
return false;
|
|
1346
1774
|
const n = node2;
|
|
1347
1775
|
return typeof n.argument === "object" || n.argument === null;
|
|
1348
1776
|
};
|
|
1349
|
-
const
|
|
1350
|
-
if (!
|
|
1777
|
+
const isObjectExpressionNode2 = (node2) => {
|
|
1778
|
+
if (!isNodeType2(node2, "ObjectExpression"))
|
|
1351
1779
|
return false;
|
|
1352
1780
|
const n = node2;
|
|
1353
1781
|
return Array.isArray(n.properties);
|
|
1354
1782
|
};
|
|
1355
|
-
const
|
|
1356
|
-
if (!
|
|
1783
|
+
const isObjectPropertyNode2 = (node2) => {
|
|
1784
|
+
if (!isNodeType2(node2, "ObjectProperty"))
|
|
1357
1785
|
return false;
|
|
1358
1786
|
const n = node2;
|
|
1359
1787
|
return typeof n.computed === "boolean" && typeof n.key === "object" && n.key !== null && typeof n.value === "object" && n.value !== null;
|
|
@@ -1361,16 +1789,16 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1361
1789
|
const getLastIdentifierFromMemberChain = (node2) => {
|
|
1362
1790
|
if (!node2)
|
|
1363
1791
|
return null;
|
|
1364
|
-
if (
|
|
1792
|
+
if (isIdentifierNode2(node2))
|
|
1365
1793
|
return node2.name;
|
|
1366
1794
|
if (isMemberExpressionNode(node2)) {
|
|
1367
1795
|
const prop = node2.property;
|
|
1368
1796
|
if (node2.computed === false) {
|
|
1369
|
-
if (
|
|
1797
|
+
if (isIdentifierNode2(prop))
|
|
1370
1798
|
return prop.name;
|
|
1371
1799
|
}
|
|
1372
1800
|
if (node2.computed === true) {
|
|
1373
|
-
if (
|
|
1801
|
+
if (isStringLiteralNode2(prop))
|
|
1374
1802
|
return prop.value;
|
|
1375
1803
|
}
|
|
1376
1804
|
}
|
|
@@ -1380,7 +1808,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1380
1808
|
if (!node2) {
|
|
1381
1809
|
return null;
|
|
1382
1810
|
}
|
|
1383
|
-
if (
|
|
1811
|
+
if (isIdentifierNode2(node2)) {
|
|
1384
1812
|
return node2.name;
|
|
1385
1813
|
}
|
|
1386
1814
|
if (isMemberExpressionNode(node2)) {
|
|
@@ -1392,11 +1820,11 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1392
1820
|
if (!lhs) {
|
|
1393
1821
|
return null;
|
|
1394
1822
|
}
|
|
1395
|
-
if (
|
|
1823
|
+
if (isIdentifierNode2(lhs)) {
|
|
1396
1824
|
return lhs.name;
|
|
1397
1825
|
}
|
|
1398
1826
|
if (isMemberExpressionNode(lhs)) {
|
|
1399
|
-
if (lhs.computed === false &&
|
|
1827
|
+
if (lhs.computed === false && isIdentifierNode2(lhs.property) && lhs.property.name === "value") {
|
|
1400
1828
|
return getLastIdentifierFromMemberChain(lhs.object);
|
|
1401
1829
|
}
|
|
1402
1830
|
return getLastIdentifierFromMemberChain(lhs);
|
|
@@ -1404,7 +1832,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1404
1832
|
return null;
|
|
1405
1833
|
};
|
|
1406
1834
|
const isTemplateLiteralNode = (node2) => {
|
|
1407
|
-
if (!
|
|
1835
|
+
if (!isNodeType2(node2, "TemplateLiteral")) {
|
|
1408
1836
|
return false;
|
|
1409
1837
|
}
|
|
1410
1838
|
const n = node2;
|
|
@@ -1423,7 +1851,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1423
1851
|
if (isNullLiteralNode(arg)) {
|
|
1424
1852
|
return "Null";
|
|
1425
1853
|
}
|
|
1426
|
-
if (
|
|
1854
|
+
if (isStringLiteralNode2(arg)) {
|
|
1427
1855
|
const cleaned = (arg.value ?? "").trim();
|
|
1428
1856
|
if (!cleaned) {
|
|
1429
1857
|
return null;
|
|
@@ -1449,7 +1877,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1449
1877
|
return toPascalCase(stableName.slice(0, 24));
|
|
1450
1878
|
}
|
|
1451
1879
|
}
|
|
1452
|
-
if (
|
|
1880
|
+
if (isIdentifierNode2(arg)) {
|
|
1453
1881
|
const firstChar = arg.name.charAt(0);
|
|
1454
1882
|
const isUpperAlpha = firstChar !== "" && firstChar === firstChar.toUpperCase() && firstChar !== firstChar.toLowerCase();
|
|
1455
1883
|
if (isUpperAlpha) {
|
|
@@ -1461,7 +1889,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1461
1889
|
const getStableSuffixFromCall = (call) => {
|
|
1462
1890
|
const args = call.arguments ?? [];
|
|
1463
1891
|
const first = args.length > 0 ? args[0] : null;
|
|
1464
|
-
if (!
|
|
1892
|
+
if (!isObjectExpressionNode2(first)) {
|
|
1465
1893
|
const parts2 = [];
|
|
1466
1894
|
for (const arg of args.slice(0, 4)) {
|
|
1467
1895
|
const w = stableWordFromValue(arg ?? null);
|
|
@@ -1480,20 +1908,20 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1480
1908
|
}
|
|
1481
1909
|
const parts = [];
|
|
1482
1910
|
for (const prop of first.properties ?? []) {
|
|
1483
|
-
if (!
|
|
1911
|
+
if (!isObjectPropertyNode2(prop)) {
|
|
1484
1912
|
continue;
|
|
1485
1913
|
}
|
|
1486
1914
|
if (prop.computed) {
|
|
1487
1915
|
continue;
|
|
1488
1916
|
}
|
|
1489
|
-
const keyName =
|
|
1917
|
+
const keyName = isIdentifierNode2(prop.key) ? prop.key.name : isStringLiteralNode2(prop.key) ? prop.key.value : null;
|
|
1490
1918
|
if (!keyName) {
|
|
1491
1919
|
continue;
|
|
1492
1920
|
}
|
|
1493
1921
|
let valueWord = null;
|
|
1494
1922
|
if (isBooleanLiteralNode(prop.value)) {
|
|
1495
1923
|
valueWord = prop.value.value ? "True" : "False";
|
|
1496
|
-
} else if (
|
|
1924
|
+
} else if (isStringLiteralNode2(prop.value)) {
|
|
1497
1925
|
const cleaned = (prop.value.value ?? "").trim();
|
|
1498
1926
|
if (cleaned) {
|
|
1499
1927
|
valueWord = toPascalCase(cleaned.slice(0, 24));
|
|
@@ -1622,13 +2050,18 @@ function getDataTestIdValueFromValueAttribute(node, actualFileName, attributeKey
|
|
|
1622
2050
|
return staticAttributeValue(`${actualFileName}-${value}-${role}`);
|
|
1623
2051
|
}
|
|
1624
2052
|
const attrDynamic = findDirectiveByName(node, "bind", attributeKey);
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
2053
|
+
const attrDynamicAst = attrDynamic ? tryGetDirectiveBabelAst(attrDynamic, {
|
|
2054
|
+
preferredViews: ["loc", "compiled"],
|
|
2055
|
+
plugins: ["typescript"],
|
|
2056
|
+
preferExistingAst: false
|
|
2057
|
+
}) : null;
|
|
2058
|
+
if (attrDynamic?.exp && attrDynamicAst) {
|
|
2059
|
+
let value = getVueExpressionSource(attrDynamic.exp, "loc", "compiled");
|
|
2060
|
+
if (types.isMemberExpression(attrDynamicAst) || types.isOptionalMemberExpression(attrDynamicAst)) {
|
|
1628
2061
|
return staticAttributeValue(`${actualFileName}-${value.replaceAll(".", "")}-${role}`);
|
|
1629
2062
|
}
|
|
1630
|
-
if (
|
|
1631
|
-
value =
|
|
2063
|
+
if (types.isCallExpression(attrDynamicAst) || types.isOptionalCallExpression(attrDynamicAst)) {
|
|
2064
|
+
value = getVueExpressionSource(attrDynamic.exp, "compiled", "loc");
|
|
1632
2065
|
return templateAttributeValue(`${actualFileName}-\${${value}}-${role}`);
|
|
1633
2066
|
}
|
|
1634
2067
|
return staticAttributeValue(`${actualFileName}-${value}-${role}`);
|
|
@@ -1636,16 +2069,16 @@ function getDataTestIdValueFromValueAttribute(node, actualFileName, attributeKey
|
|
|
1636
2069
|
return null;
|
|
1637
2070
|
}
|
|
1638
2071
|
function generateToDirectiveDataTestId(componentName, node, toDirective, context, hierarchyMap2, nativeWrappers) {
|
|
1639
|
-
const
|
|
1640
|
-
if (
|
|
1641
|
-
return templateAttributeValue(`${componentName}-${
|
|
2072
|
+
const keyInfo = getKeyDirectiveInfo(node) || getContainedInVForDirectiveKeyInfo(context, node, hierarchyMap2);
|
|
2073
|
+
if (keyInfo) {
|
|
2074
|
+
return templateAttributeValue(`${componentName}-${keyInfo.selectorFragment}-${formatTagName(node, nativeWrappers)}`);
|
|
1642
2075
|
} else {
|
|
1643
2076
|
let name = toDirectiveObjectFieldNameValue(toDirective);
|
|
1644
2077
|
if (!name) {
|
|
1645
2078
|
if (toDirective.exp == null) {
|
|
1646
2079
|
return null;
|
|
1647
2080
|
}
|
|
1648
|
-
const source =
|
|
2081
|
+
const source = getVueExpressionSource(toDirective.exp, "compiled", "loc");
|
|
1649
2082
|
const toAst = toDirective.exp.ast;
|
|
1650
2083
|
const interpolated = toAst !== void 0 && toAst !== null && toAst !== false && types.isTemplateLiteral(toAst);
|
|
1651
2084
|
return templateAttributeValue(`${componentName}-\${${source}${interpolated ? ".replaceAll(' ', '')" : "?.name?.replaceAll(' ', '') ?? ''"}}${formatTagName(node, nativeWrappers)}`);
|
|
@@ -1669,44 +2102,46 @@ function toDirectiveObjectFieldNameValue(node) {
|
|
|
1669
2102
|
if (!node.exp || node.exp.type !== compilerCore.NodeTypes.COMPOUND_EXPRESSION && node.exp.type !== compilerCore.NodeTypes.SIMPLE_EXPRESSION) {
|
|
1670
2103
|
return null;
|
|
1671
2104
|
}
|
|
1672
|
-
const
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
const isStringLiteralNode = (n) => {
|
|
1679
|
-
return isNodeType(n, "StringLiteral") && typeof n.value === "string";
|
|
1680
|
-
};
|
|
1681
|
-
const isIdentifierNode = (n) => {
|
|
1682
|
-
return isNodeType(n, "Identifier") && typeof n.name === "string";
|
|
1683
|
-
};
|
|
1684
|
-
const isObjectPropertyNode = (n) => {
|
|
1685
|
-
if (!isNodeType(n, "ObjectProperty"))
|
|
1686
|
-
return false;
|
|
1687
|
-
const nn = n;
|
|
1688
|
-
return typeof nn.key === "object" && nn.key !== null && typeof nn.value === "object" && nn.value !== null;
|
|
1689
|
-
};
|
|
1690
|
-
const isObjectExpressionNode = (n) => {
|
|
1691
|
-
if (!isNodeType(n, "ObjectExpression"))
|
|
1692
|
-
return false;
|
|
1693
|
-
const nn = n;
|
|
1694
|
-
return Array.isArray(nn.properties);
|
|
1695
|
-
};
|
|
1696
|
-
if (!isObjectExpressionNode(expr))
|
|
1697
|
-
return null;
|
|
1698
|
-
const nameProp = expr.properties.find((p) => {
|
|
1699
|
-
if (!isObjectPropertyNode(p))
|
|
1700
|
-
return false;
|
|
1701
|
-
const key = p.key;
|
|
1702
|
-
return isIdentifierNode(key) && key.name === "name" || isStringLiteralNode(key) && key.value === "name";
|
|
1703
|
-
});
|
|
1704
|
-
if (!nameProp || !isObjectPropertyNode(nameProp) || !isStringLiteralNode(nameProp.value))
|
|
1705
|
-
return null;
|
|
1706
|
-
return toPascalCase(nameProp.value.value);
|
|
1707
|
-
} catch {
|
|
2105
|
+
const expr = tryGetDirectiveBabelAst(node, {
|
|
2106
|
+
preferredViews: ["loc", "compiled"],
|
|
2107
|
+
plugins: ["typescript"],
|
|
2108
|
+
preferExistingAst: false
|
|
2109
|
+
});
|
|
2110
|
+
if (!expr) {
|
|
1708
2111
|
return null;
|
|
1709
2112
|
}
|
|
2113
|
+
const isNodeType2 = (n, type) => {
|
|
2114
|
+
return n !== null && n.type === type;
|
|
2115
|
+
};
|
|
2116
|
+
const isStringLiteralNode2 = (n) => {
|
|
2117
|
+
return isNodeType2(n, "StringLiteral") && typeof n.value === "string";
|
|
2118
|
+
};
|
|
2119
|
+
const isIdentifierNode2 = (n) => {
|
|
2120
|
+
return isNodeType2(n, "Identifier") && typeof n.name === "string";
|
|
2121
|
+
};
|
|
2122
|
+
const isObjectPropertyNode2 = (n) => {
|
|
2123
|
+
if (!isNodeType2(n, "ObjectProperty"))
|
|
2124
|
+
return false;
|
|
2125
|
+
const nn = n;
|
|
2126
|
+
return typeof nn.key === "object" && nn.key !== null && typeof nn.value === "object" && nn.value !== null;
|
|
2127
|
+
};
|
|
2128
|
+
const isObjectExpressionNode2 = (n) => {
|
|
2129
|
+
if (!isNodeType2(n, "ObjectExpression"))
|
|
2130
|
+
return false;
|
|
2131
|
+
const nn = n;
|
|
2132
|
+
return Array.isArray(nn.properties);
|
|
2133
|
+
};
|
|
2134
|
+
if (!isObjectExpressionNode2(expr))
|
|
2135
|
+
return null;
|
|
2136
|
+
const nameProp = expr.properties.find((p) => {
|
|
2137
|
+
if (!isObjectPropertyNode2(p))
|
|
2138
|
+
return false;
|
|
2139
|
+
const key = p.key;
|
|
2140
|
+
return isIdentifierNode2(key) && key.name === "name" || isStringLiteralNode2(key) && key.value === "name";
|
|
2141
|
+
});
|
|
2142
|
+
if (!nameProp || !isObjectPropertyNode2(nameProp) || !isStringLiteralNode2(nameProp.value))
|
|
2143
|
+
return null;
|
|
2144
|
+
return toPascalCase(nameProp.value.value);
|
|
1710
2145
|
}
|
|
1711
2146
|
function getComposedClickHandlerContent(node, _context, innerText, clickDirective, _options = {}) {
|
|
1712
2147
|
const click = clickDirective ?? tryGetClickDirective(node);
|
|
@@ -1716,7 +2151,7 @@ function getComposedClickHandlerContent(node, _context, innerText, clickDirectiv
|
|
|
1716
2151
|
let handlerName = "";
|
|
1717
2152
|
if (click.exp) {
|
|
1718
2153
|
const exp = click.exp;
|
|
1719
|
-
const source = (exp
|
|
2154
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
1720
2155
|
if (source) {
|
|
1721
2156
|
const parsed = tryParseBabelAstFromHandlerSource(source);
|
|
1722
2157
|
if (parsed) {
|
|
@@ -1737,9 +2172,9 @@ function tryParseBabelAstFromHandlerSource(source) {
|
|
|
1737
2172
|
const trimmed = source.trim();
|
|
1738
2173
|
if (!trimmed)
|
|
1739
2174
|
return null;
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
2175
|
+
const expressionAst = tryParseBabelExpressionFromSource(trimmed, ["typescript", "jsx"]);
|
|
2176
|
+
if (expressionAst) {
|
|
2177
|
+
return expressionAst;
|
|
1743
2178
|
}
|
|
1744
2179
|
try {
|
|
1745
2180
|
return parser.parse(trimmed, { sourceType: "module", plugins: ["typescript", "jsx"] });
|
|
@@ -2078,23 +2513,24 @@ function tryGetExistingElementDataTestId(node, attributeName = "data-testid") {
|
|
|
2078
2513
|
return null;
|
|
2079
2514
|
}
|
|
2080
2515
|
const simpleExp = exp;
|
|
2081
|
-
const ast = simpleExp
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
const
|
|
2088
|
-
const unwrappedTemplate = raw2.startsWith("`") && raw2.endsWith("`") && raw2.length >= 2 ? raw2.slice(1, -1) : cooked;
|
|
2516
|
+
const ast = tryGetVueExpressionAst(simpleExp, {
|
|
2517
|
+
preferredViews: ["content", "loc", "compiled"],
|
|
2518
|
+
plugins: ["typescript"]
|
|
2519
|
+
});
|
|
2520
|
+
const unwrappedTemplateLiteral = tryUnwrapTemplateLiteralExpressionSource(simpleExp);
|
|
2521
|
+
if (unwrappedTemplateLiteral) {
|
|
2522
|
+
const isStatic = unwrappedTemplateLiteral.expressionCount === 0;
|
|
2089
2523
|
if (isStatic) {
|
|
2090
|
-
return { value:
|
|
2524
|
+
return { value: unwrappedTemplateLiteral.template, isDynamic: false, isStaticLiteral: true };
|
|
2091
2525
|
}
|
|
2526
|
+
const templateValue = templateAttributeValue(unwrappedTemplateLiteral.template);
|
|
2092
2527
|
return {
|
|
2093
|
-
value:
|
|
2528
|
+
value: templateValue.template,
|
|
2094
2529
|
isDynamic: true,
|
|
2095
2530
|
isStaticLiteral: false,
|
|
2096
|
-
template:
|
|
2097
|
-
|
|
2531
|
+
template: templateValue.template,
|
|
2532
|
+
parsedTemplate: templateValue.parsedTemplate,
|
|
2533
|
+
templateExpressionCount: unwrappedTemplateLiteral.expressionCount
|
|
2098
2534
|
};
|
|
2099
2535
|
}
|
|
2100
2536
|
if (ast && typeof ast === "object" && "type" in ast && ast.type === "StringLiteral") {
|
|
@@ -2107,59 +2543,27 @@ function tryGetExistingElementDataTestId(node, attributeName = "data-testid") {
|
|
|
2107
2543
|
}
|
|
2108
2544
|
const preservableReference = tryGetPreservableDynamicReferenceExpression(ast);
|
|
2109
2545
|
if (preservableReference) {
|
|
2546
|
+
const templateValue = templateAttributeValue(`\${${preservableReference}}`);
|
|
2110
2547
|
return {
|
|
2111
2548
|
value: preservableReference,
|
|
2112
|
-
isDynamic: true,
|
|
2113
|
-
isStaticLiteral: false,
|
|
2114
|
-
template:
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
try {
|
|
2124
|
-
const ast2 = parser.parseExpression(raw, { plugins: ["typescript"] });
|
|
2125
|
-
if (ast2 && typeof ast2 === "object" && "type" in ast2 && ast2.type === "TemplateLiteral") {
|
|
2126
|
-
const tl = ast2;
|
|
2127
|
-
const cooked = (tl.quasis ?? []).map((q) => q.value?.cooked ?? "").join("");
|
|
2128
|
-
const expressionCount = (tl.expressions ?? []).length;
|
|
2129
|
-
const isStatic = expressionCount === 0;
|
|
2130
|
-
const unwrappedTemplate = raw.startsWith("`") && raw.endsWith("`") && raw.length >= 2 ? raw.slice(1, -1) : cooked;
|
|
2131
|
-
if (isStatic) {
|
|
2132
|
-
return { value: unwrappedTemplate, isDynamic: false, isStaticLiteral: true };
|
|
2133
|
-
}
|
|
2134
|
-
return {
|
|
2135
|
-
value: unwrappedTemplate,
|
|
2136
|
-
isDynamic: true,
|
|
2137
|
-
isStaticLiteral: false,
|
|
2138
|
-
template: unwrappedTemplate,
|
|
2139
|
-
templateExpressionCount: expressionCount
|
|
2140
|
-
};
|
|
2141
|
-
}
|
|
2142
|
-
if (ast2 && typeof ast2 === "object" && "type" in ast2 && ast2.type === "StringLiteral") {
|
|
2143
|
-
const sl = ast2;
|
|
2144
|
-
return { value: sl.value ?? "", isDynamic: false, isStaticLiteral: true };
|
|
2145
|
-
}
|
|
2146
|
-
const preservableReference2 = tryGetPreservableDynamicReferenceExpression(ast2);
|
|
2147
|
-
if (preservableReference2) {
|
|
2148
|
-
return {
|
|
2149
|
-
value: preservableReference2,
|
|
2150
|
-
isDynamic: true,
|
|
2151
|
-
isStaticLiteral: false,
|
|
2152
|
-
template: `\${${preservableReference2}}`,
|
|
2153
|
-
templateExpressionCount: 1,
|
|
2154
|
-
rawExpression: preservableReference2
|
|
2155
|
-
};
|
|
2156
|
-
}
|
|
2157
|
-
} catch {
|
|
2549
|
+
isDynamic: true,
|
|
2550
|
+
isStaticLiteral: false,
|
|
2551
|
+
template: templateValue.template,
|
|
2552
|
+
parsedTemplate: templateValue.parsedTemplate,
|
|
2553
|
+
templateExpressionCount: 1,
|
|
2554
|
+
rawExpression: preservableReference
|
|
2555
|
+
};
|
|
2556
|
+
}
|
|
2557
|
+
const raw = (simpleExp.content ?? "").trim();
|
|
2558
|
+
if (!raw) {
|
|
2559
|
+
return null;
|
|
2158
2560
|
}
|
|
2159
2561
|
return { value: raw, isDynamic: true, isStaticLiteral: false, rawExpression: raw };
|
|
2160
2562
|
}
|
|
2161
2563
|
function isTemplatePlaceholder(part) {
|
|
2162
|
-
|
|
2564
|
+
const parsedTemplate = tryParseTemplateFragment(part);
|
|
2565
|
+
const templateFragment = parsedTemplate ? getSingleExpressionTemplateFragment(parsedTemplate) : null;
|
|
2566
|
+
return !!templateFragment && templateFragment.prefix === "" && templateFragment.suffix === "";
|
|
2163
2567
|
}
|
|
2164
2568
|
function isAllCapsOrDigits(value) {
|
|
2165
2569
|
if (value.length <= 1) {
|
|
@@ -2212,33 +2616,14 @@ function safeMethodNameFromParts(parts) {
|
|
|
2212
2616
|
}
|
|
2213
2617
|
return name;
|
|
2214
2618
|
}
|
|
2215
|
-
function
|
|
2619
|
+
function toPomKeyPattern(templateValue) {
|
|
2620
|
+
const { templateLiteral } = templateValue.parsedTemplate;
|
|
2216
2621
|
let out = "";
|
|
2217
|
-
let i = 0;
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
out += template.slice(i);
|
|
2222
|
-
break;
|
|
2223
|
-
}
|
|
2224
|
-
out += template.slice(i, start);
|
|
2225
|
-
let depth = 1;
|
|
2226
|
-
let j = start + 2;
|
|
2227
|
-
while (j < template.length && depth > 0) {
|
|
2228
|
-
if (template[j] === "{") {
|
|
2229
|
-
depth++;
|
|
2230
|
-
} else if (template[j] === "}") {
|
|
2231
|
-
depth--;
|
|
2232
|
-
}
|
|
2233
|
-
j++;
|
|
2234
|
-
}
|
|
2235
|
-
const end = depth === 0 ? j - 1 : -1;
|
|
2236
|
-
if (end < 0) {
|
|
2237
|
-
out += template.slice(start);
|
|
2238
|
-
break;
|
|
2622
|
+
for (let i = 0; i < templateLiteral.quasis.length; i += 1) {
|
|
2623
|
+
out += templateLiteral.quasis[i]?.value.raw ?? "";
|
|
2624
|
+
if (templateLiteral.expressions[i]) {
|
|
2625
|
+
out += "${key}";
|
|
2239
2626
|
}
|
|
2240
|
-
out += "${key}";
|
|
2241
|
-
i = end + 1;
|
|
2242
2627
|
}
|
|
2243
2628
|
return out;
|
|
2244
2629
|
}
|
|
@@ -2246,8 +2631,8 @@ function applyResolvedDataTestId(args) {
|
|
|
2246
2631
|
const addHtmlAttribute = args.addHtmlAttribute ?? true;
|
|
2247
2632
|
const entryOverrides = args.entryOverrides ?? {};
|
|
2248
2633
|
const testIdAttribute = args.testIdAttribute ?? "data-testid";
|
|
2249
|
-
const existingIdBehavior = args.existingIdBehavior ?? "
|
|
2250
|
-
const nameCollisionBehavior = args.nameCollisionBehavior ?? "
|
|
2634
|
+
const existingIdBehavior = args.existingIdBehavior ?? "error";
|
|
2635
|
+
const nameCollisionBehavior = args.nameCollisionBehavior ?? "error";
|
|
2251
2636
|
const warn = args.warn;
|
|
2252
2637
|
const getBestKeyAccessCandidates = (expr) => {
|
|
2253
2638
|
if (!expr) {
|
|
@@ -2256,7 +2641,10 @@ function applyResolvedDataTestId(args) {
|
|
|
2256
2641
|
return splitNullishCoalescingExpression(expr);
|
|
2257
2642
|
};
|
|
2258
2643
|
let dataTestId = args.preferredGeneratedValue;
|
|
2644
|
+
let runtimeDataTestId = args.preferredRuntimeValue ?? args.preferredGeneratedValue;
|
|
2259
2645
|
let fromExisting = false;
|
|
2646
|
+
const bestKeyPreservePlaceholder = args.keyInfo?.runtimeFragment ?? null;
|
|
2647
|
+
const bestKeyVariable = args.keyInfo?.rawExpression ?? null;
|
|
2260
2648
|
const existing = tryGetExistingElementDataTestId(args.element, testIdAttribute);
|
|
2261
2649
|
if (existing) {
|
|
2262
2650
|
const loc = args.element.loc?.start;
|
|
@@ -2277,8 +2665,10 @@ Bulk cleanup: run ESLint with the @immense/vue-pom-generator/remove-existing-tes
|
|
|
2277
2665
|
if (existingIdBehavior === "preserve") {
|
|
2278
2666
|
if (existing.isDynamic) {
|
|
2279
2667
|
if (existing.template) {
|
|
2280
|
-
const
|
|
2281
|
-
|
|
2668
|
+
const existingTemplateValue = existing.parsedTemplate ? { kind: "template", template: existing.template, parsedTemplate: existing.parsedTemplate } : templateAttributeValue(existing.template);
|
|
2669
|
+
const existingTemplateFragment = getSingleExpressionTemplateFragment(existingTemplateValue.parsedTemplate);
|
|
2670
|
+
const requiredKeyTemplateValue = bestKeyPreservePlaceholder ? templateAttributeValue(bestKeyPreservePlaceholder) : null;
|
|
2671
|
+
if ((existing.templateExpressionCount ?? 0) !== 1 || !existingTemplateFragment) {
|
|
2282
2672
|
throw new Error(
|
|
2283
2673
|
`[vue-pom-generator] Existing ${attrLabel} is a template literal with multiple interpolations and cannot be preserved safely.
|
|
2284
2674
|
Component: ${args.componentName}
|
|
@@ -2288,20 +2678,21 @@ Existing ${attrLabel}: ${JSON.stringify(existing.value)}
|
|
|
2288
2678
|
Fix: reduce the template to a single key-based interpolation, or remove the explicit ${attrLabel} so it can be auto-generated.`
|
|
2289
2679
|
);
|
|
2290
2680
|
}
|
|
2291
|
-
const hasExact =
|
|
2292
|
-
const hasVarAccess = getBestKeyAccessCandidates(
|
|
2293
|
-
if (!hasExact && !hasVarAccess &&
|
|
2681
|
+
const hasExact = requiredKeyTemplateValue ? templateFragmentContainsSingleExpression(existingTemplateValue.parsedTemplate, requiredKeyTemplateValue.parsedTemplate) : false;
|
|
2682
|
+
const hasVarAccess = getBestKeyAccessCandidates(bestKeyVariable).some((candidate) => existingTemplateFragment.expressionSource === candidate);
|
|
2683
|
+
if (!hasExact && !hasVarAccess && bestKeyPreservePlaceholder) {
|
|
2294
2684
|
throw new Error(
|
|
2295
2685
|
`[vue-pom-generator] Existing ${attrLabel} appears to be missing the key placeholder needed to keep it unique.
|
|
2296
2686
|
Component: ${args.componentName}
|
|
2297
2687
|
File: ${file}:${locationHint}
|
|
2298
2688
|
Existing ${attrLabel}: ${JSON.stringify(existing.value)}
|
|
2299
|
-
Required placeholder: ${JSON.stringify(
|
|
2689
|
+
Required placeholder: ${JSON.stringify(bestKeyPreservePlaceholder)}${bestKeyVariable ? ` or an access on "${bestKeyVariable}"` : ""}
|
|
2300
2690
|
|
|
2301
|
-
Fix: either (1) include ${
|
|
2691
|
+
Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} template literal, or (2) remove the explicit ${attrLabel} so it can be auto-generated.`
|
|
2302
2692
|
);
|
|
2303
2693
|
}
|
|
2304
|
-
dataTestId =
|
|
2694
|
+
dataTestId = existingTemplateValue;
|
|
2695
|
+
runtimeDataTestId = existingTemplateValue;
|
|
2305
2696
|
fromExisting = true;
|
|
2306
2697
|
} else {
|
|
2307
2698
|
throw new Error(
|
|
@@ -2315,18 +2706,19 @@ If you really need a computed id, do not set existingIdBehavior="preserve".`
|
|
|
2315
2706
|
);
|
|
2316
2707
|
}
|
|
2317
2708
|
} else {
|
|
2318
|
-
if (
|
|
2709
|
+
if (bestKeyPreservePlaceholder && existing.isStaticLiteral) {
|
|
2319
2710
|
throw new Error(
|
|
2320
2711
|
`[vue-pom-generator] Existing ${attrLabel} appears to be missing the key placeholder needed to keep it unique.
|
|
2321
2712
|
Component: ${args.componentName}
|
|
2322
2713
|
File: ${file}:${locationHint}
|
|
2323
2714
|
Existing ${attrLabel}: ${JSON.stringify(existing.value)}
|
|
2324
|
-
Required placeholder: ${JSON.stringify(
|
|
2715
|
+
Required placeholder: ${JSON.stringify(bestKeyPreservePlaceholder)}${bestKeyVariable ? ` or an access on "${bestKeyVariable}"` : ""}
|
|
2325
2716
|
|
|
2326
|
-
Fix: either (1) include ${
|
|
2717
|
+
Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} template literal, or (2) remove the explicit ${attrLabel} so it can be auto-generated.`
|
|
2327
2718
|
);
|
|
2328
2719
|
}
|
|
2329
2720
|
dataTestId = staticAttributeValue(existing.value);
|
|
2721
|
+
runtimeDataTestId = staticAttributeValue(existing.value);
|
|
2330
2722
|
fromExisting = true;
|
|
2331
2723
|
}
|
|
2332
2724
|
}
|
|
@@ -2356,8 +2748,10 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2356
2748
|
};
|
|
2357
2749
|
const normalizedRole = normalizeNativeRole(args.nativeRole) ?? "button";
|
|
2358
2750
|
const targetPageObjectModelClass = entryOverrides.targetPageObjectModelClass;
|
|
2359
|
-
const formattedDataTestIdForPom = dataTestId.kind === "template" ?
|
|
2360
|
-
const
|
|
2751
|
+
const formattedDataTestIdForPom = dataTestId.kind === "template" ? toPomKeyPattern(dataTestId) : dataTestId.value;
|
|
2752
|
+
const selectorPatternKind = dataTestId.kind === "template" ? "parameterized" : "static";
|
|
2753
|
+
const selectorPattern = createPomStringPattern(formattedDataTestIdForPom, selectorPatternKind);
|
|
2754
|
+
const selectorIsParameterized = selectorPatternKind === "parameterized";
|
|
2361
2755
|
const deriveBaseMethodNameFromHint = (hint) => {
|
|
2362
2756
|
const hintRaw = (hint ?? "").trim();
|
|
2363
2757
|
const trimEdgeSeparators = (value) => {
|
|
@@ -2405,13 +2799,13 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2405
2799
|
const roleSuffix = upperFirst(normalizedRole || "Element");
|
|
2406
2800
|
const baseName = upperFirst(primaryMethodName);
|
|
2407
2801
|
const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
|
|
2408
|
-
return
|
|
2802
|
+
return selectorIsParameterized ? removeByKeySegment2(propertyName) : propertyName;
|
|
2409
2803
|
};
|
|
2410
2804
|
const getPrimaryGetterNameCandidates = (primaryMethodName) => {
|
|
2411
2805
|
const roleSuffix = upperFirst(normalizedRole || "Element");
|
|
2412
2806
|
const baseName = upperFirst(primaryMethodName);
|
|
2413
2807
|
const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
|
|
2414
|
-
if (!
|
|
2808
|
+
if (!selectorIsParameterized) {
|
|
2415
2809
|
return { primary: propertyName };
|
|
2416
2810
|
}
|
|
2417
2811
|
const stripped = removeByKeySegment2(propertyName);
|
|
@@ -2473,11 +2867,11 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2473
2867
|
return false;
|
|
2474
2868
|
}
|
|
2475
2869
|
const existingSelectors = [
|
|
2476
|
-
existingPom.
|
|
2477
|
-
...existingPom.
|
|
2870
|
+
existingPom.selector,
|
|
2871
|
+
...existingPom.alternateSelectors ?? []
|
|
2478
2872
|
];
|
|
2479
|
-
const sharesSelectorIdentity = existingSelectors.
|
|
2480
|
-
if (
|
|
2873
|
+
const sharesSelectorIdentity = existingSelectors.some((existingSelector) => pomStringPatternEquals(existingSelector, selectorPattern));
|
|
2874
|
+
if (selectorIsParameterized && !sharesSelectorIdentity) {
|
|
2481
2875
|
return false;
|
|
2482
2876
|
}
|
|
2483
2877
|
if (!mergeKey && !sharesSelectorIdentity) {
|
|
@@ -2492,12 +2886,15 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2492
2886
|
if ((existingEntry.targetPageObjectModelClass ?? null) !== (targetPageObjectModelClass ?? null)) {
|
|
2493
2887
|
return false;
|
|
2494
2888
|
}
|
|
2495
|
-
if (existingPom.
|
|
2496
|
-
existingPom.
|
|
2497
|
-
if (!existingPom.
|
|
2498
|
-
existingPom.
|
|
2889
|
+
if (!pomStringPatternEquals(existingPom.selector, selectorPattern)) {
|
|
2890
|
+
existingPom.alternateSelectors ??= [];
|
|
2891
|
+
if (!(existingPom.alternateSelectors ?? []).some((existingSelector) => pomStringPatternEquals(existingSelector, selectorPattern))) {
|
|
2892
|
+
existingPom.alternateSelectors.push(selectorPattern);
|
|
2499
2893
|
}
|
|
2500
2894
|
}
|
|
2895
|
+
if (selectorIsParameterized && !existingPom.parameters.some((param) => param.name === "key")) {
|
|
2896
|
+
existingPom.parameters = [createPomParameterSpec("key", keyTypeFromValues), ...existingPom.parameters];
|
|
2897
|
+
}
|
|
2501
2898
|
return true;
|
|
2502
2899
|
};
|
|
2503
2900
|
let methodName = "";
|
|
@@ -2510,7 +2907,7 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2510
2907
|
let suffix = 1;
|
|
2511
2908
|
while (true) {
|
|
2512
2909
|
const baseWithSuffix = suffix === 1 ? base : `${base}${suffix}`;
|
|
2513
|
-
const candidate =
|
|
2910
|
+
const candidate = selectorIsParameterized ? `${baseWithSuffix}ByKey` : baseWithSuffix;
|
|
2514
2911
|
const actionName = getPrimaryActionMethodName(candidate);
|
|
2515
2912
|
const getterCandidates = getPrimaryGetterNameCandidates(candidate);
|
|
2516
2913
|
let chosenGetterName = getterCandidates.primary;
|
|
@@ -2536,7 +2933,7 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2536
2933
|
const baseNameUpper = upperFirst(baseWithSuffix);
|
|
2537
2934
|
if (!hasRoleSuffix(baseNameUpper, roleSuffix)) {
|
|
2538
2935
|
const baseWithRoleSuffix = `${baseWithSuffix}${roleSuffix}`;
|
|
2539
|
-
const candidateWithRoleSuffix =
|
|
2936
|
+
const candidateWithRoleSuffix = selectorIsParameterized ? `${baseWithRoleSuffix}ByKey` : baseWithRoleSuffix;
|
|
2540
2937
|
const actionNameWithRoleSuffix = getPrimaryActionMethodName(candidateWithRoleSuffix);
|
|
2541
2938
|
const getterCandidatesWithRoleSuffix = getPrimaryGetterNameCandidates(candidateWithRoleSuffix);
|
|
2542
2939
|
let chosenGetterNameWithRoleSuffix = getterCandidatesWithRoleSuffix.primary;
|
|
@@ -2604,51 +3001,46 @@ Conflicts: getter=${last.getterName}, method=${last.actionName}
|
|
|
2604
3001
|
Fix: make the element identifiable (e.g. add id/name/inner text or use a more specific click handler name), or switch generation.nameCollisionBehavior to "warn"/"suffix".`
|
|
2605
3002
|
);
|
|
2606
3003
|
}
|
|
2607
|
-
|
|
2608
|
-
if (isKeyed) {
|
|
2609
|
-
params.key = keyTypeFromValues;
|
|
2610
|
-
}
|
|
3004
|
+
let parameters = selectorIsParameterized ? [createPomParameterSpec("key", keyTypeFromValues)] : [];
|
|
2611
3005
|
switch (normalizedRole) {
|
|
2612
3006
|
case "input":
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
if (!isKeyed) delete params.key;
|
|
3007
|
+
parameters = setPomParameter(parameters, "text", "string");
|
|
3008
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2616
3009
|
break;
|
|
2617
3010
|
case "select":
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
if (!isKeyed) delete params.key;
|
|
3011
|
+
parameters = setPomParameter(parameters, "value", "string");
|
|
3012
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2621
3013
|
break;
|
|
2622
3014
|
case "vselect":
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
if (!isKeyed) delete params.key;
|
|
3015
|
+
parameters = setPomParameter(parameters, "value", "string");
|
|
3016
|
+
parameters = setPomParameter(parameters, "timeOut", "number = 500");
|
|
3017
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2627
3018
|
break;
|
|
2628
3019
|
case "radio":
|
|
2629
|
-
|
|
3020
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2630
3021
|
break;
|
|
2631
3022
|
}
|
|
2632
|
-
|
|
2633
|
-
params.key = keyTypeFromValues;
|
|
2634
|
-
}
|
|
3023
|
+
const normalizedParameters = selectorIsParameterized ? setPomParameter(parameters, "key", keyTypeFromValues) : removePomParameter(parameters, "key");
|
|
2635
3024
|
if (addHtmlAttribute && !fromExisting) {
|
|
2636
3025
|
upsertAttribute(args.element, testIdAttribute, dataTestId);
|
|
2637
3026
|
}
|
|
2638
3027
|
const childComponentName = args.element.tag;
|
|
2639
3028
|
const dataTestIdEntry = {
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
3029
|
+
selectorValue: entryOverrides.selectorValue ?? createPomStringPattern(
|
|
3030
|
+
getAttributeValueText(dataTestId),
|
|
3031
|
+
dataTestId.kind === "template" ? "parameterized" : "static"
|
|
3032
|
+
),
|
|
3033
|
+
templateLiteral: entryOverrides.templateLiteral,
|
|
3034
|
+
targetPageObjectModelClass: entryOverrides.targetPageObjectModelClass
|
|
2643
3035
|
};
|
|
2644
3036
|
dataTestIdEntry.pom = {
|
|
2645
3037
|
nativeRole: normalizedRole,
|
|
2646
3038
|
methodName,
|
|
2647
3039
|
getterNameOverride,
|
|
2648
|
-
|
|
2649
|
-
|
|
3040
|
+
selector: selectorPattern,
|
|
3041
|
+
alternateSelectors: void 0,
|
|
2650
3042
|
mergeKey: args.pomMergeKey,
|
|
2651
|
-
|
|
3043
|
+
parameters: normalizedParameters,
|
|
2652
3044
|
keyValuesOverride: args.keyValuesOverride ?? null
|
|
2653
3045
|
// emitPrimary defaults to true; special cases (including merge) may set it to false below.
|
|
2654
3046
|
};
|
|
@@ -2679,43 +3071,18 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2679
3071
|
}
|
|
2680
3072
|
};
|
|
2681
3073
|
const getSignatureForGeneratedMethod = () => {
|
|
2682
|
-
|
|
2683
|
-
const isNavigation2 = !!dataTestIdEntry.targetPageObjectModelClass;
|
|
2684
|
-
const needsKey2 = Object.prototype.hasOwnProperty.call(params, "key");
|
|
2685
|
-
const keyType = keyTypeFromValues;
|
|
2686
|
-
if (isNavigation2) {
|
|
2687
|
-
if (needsKey2) {
|
|
2688
|
-
return { params: `key: ${keyType}`, argNames: ["key"] };
|
|
2689
|
-
}
|
|
2690
|
-
return { params: "", argNames: [] };
|
|
2691
|
-
}
|
|
2692
|
-
switch (role) {
|
|
2693
|
-
case "input":
|
|
2694
|
-
return needsKey2 ? { params: `key: ${keyType}, text: string, annotationText: string = ""`, argNames: ["key", "text", "annotationText"] } : { params: 'text: string, annotationText: string = ""', argNames: ["text", "annotationText"] };
|
|
2695
|
-
case "select":
|
|
2696
|
-
return needsKey2 ? { params: `key: ${keyType}, value: string, annotationText: string = ""`, argNames: ["key", "value", "annotationText"] } : { params: 'value: string, annotationText: string = ""', argNames: ["value", "annotationText"] };
|
|
2697
|
-
case "vselect":
|
|
2698
|
-
return needsKey2 ? { params: `key: ${keyType}, value: string, timeOut = 500`, argNames: ["key", "value", "timeOut"] } : { params: "value: string, timeOut = 500", argNames: ["value", "timeOut"] };
|
|
2699
|
-
case "radio":
|
|
2700
|
-
return needsKey2 ? { params: `key: ${keyType}, annotationText: string = ""`, argNames: ["key", "annotationText"] } : { params: 'annotationText: string = ""', argNames: ["annotationText"] };
|
|
2701
|
-
default:
|
|
2702
|
-
if (needsKey2) {
|
|
2703
|
-
return { params: `key: ${keyType}`, argNames: ["key"] };
|
|
2704
|
-
}
|
|
2705
|
-
return { params: "", argNames: [] };
|
|
2706
|
-
}
|
|
3074
|
+
return createPomMethodSignature(normalizedParameters);
|
|
2707
3075
|
};
|
|
2708
3076
|
const registerPrimaryOnce = (pom) => {
|
|
2709
|
-
const
|
|
2710
|
-
const alternates = (pom.alternateFormattedDataTestIds ?? []).slice().sort();
|
|
3077
|
+
const alternates = (pom.alternateSelectors ?? []).slice().sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
2711
3078
|
const key = JSON.stringify({
|
|
2712
3079
|
kind: "primary",
|
|
2713
3080
|
role: pom.nativeRole,
|
|
2714
3081
|
methodName: pom.methodName,
|
|
2715
3082
|
getterNameOverride: pom.getterNameOverride ?? null,
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
3083
|
+
selector: pom.selector,
|
|
3084
|
+
alternateSelectors: alternates.length ? alternates : void 0,
|
|
3085
|
+
parameters: pom.parameters,
|
|
2719
3086
|
target: dataTestIdEntry.targetPageObjectModelClass ?? null,
|
|
2720
3087
|
emitPrimary: pom.emitPrimary ?? true
|
|
2721
3088
|
});
|
|
@@ -2729,8 +3096,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2729
3096
|
}
|
|
2730
3097
|
};
|
|
2731
3098
|
const addExtraClickMethod = (spec) => {
|
|
2732
|
-
const
|
|
2733
|
-
const key = JSON.stringify({ kind: spec.kind, selector: spec.selector, keyLiteral: spec.keyLiteral ?? null, params: stableParams });
|
|
3099
|
+
const key = JSON.stringify({ kind: spec.kind, selector: spec.selector, keyLiteral: spec.keyLiteral ?? null, parameters: spec.parameters });
|
|
2734
3100
|
const seen = args.generatedMethodContentByComponent.get(args.parentComponentName) ?? /* @__PURE__ */ new Set();
|
|
2735
3101
|
if (!args.generatedMethodContentByComponent.has(args.parentComponentName)) {
|
|
2736
3102
|
args.generatedMethodContentByComponent.set(args.parentComponentName, seen);
|
|
@@ -2753,7 +3119,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2753
3119
|
if (prev === null) {
|
|
2754
3120
|
return;
|
|
2755
3121
|
}
|
|
2756
|
-
if (signature === null || prev
|
|
3122
|
+
if (signature === null || !pomMethodSignatureEquals(prev, signature)) {
|
|
2757
3123
|
args.dependencies.generatedMethods.set(name, null);
|
|
2758
3124
|
}
|
|
2759
3125
|
};
|
|
@@ -2769,23 +3135,10 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2769
3135
|
return candidate;
|
|
2770
3136
|
};
|
|
2771
3137
|
const tryGetDirectiveExpressionAst = (dir) => {
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
}
|
|
2776
|
-
if (exp.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION) {
|
|
2777
|
-
const simple = exp;
|
|
2778
|
-
const ast = simple.ast;
|
|
2779
|
-
if (ast && "type" in ast) {
|
|
2780
|
-
return ast;
|
|
2781
|
-
}
|
|
2782
|
-
}
|
|
2783
|
-
try {
|
|
2784
|
-
const raw = args.context ? compilerCore.stringifyExpression(exp) : exp.loc.source;
|
|
2785
|
-
return parser.parseExpression(raw, { plugins: ["typescript"] });
|
|
2786
|
-
} catch {
|
|
2787
|
-
return null;
|
|
2788
|
-
}
|
|
3138
|
+
return tryGetDirectiveBabelAst(dir, {
|
|
3139
|
+
preferredViews: args.context ? ["compiled", "loc"] : ["loc", "compiled"],
|
|
3140
|
+
plugins: ["typescript"]
|
|
3141
|
+
});
|
|
2789
3142
|
};
|
|
2790
3143
|
const tryGetStaticStringFromBabel = (node) => {
|
|
2791
3144
|
if (!node) {
|
|
@@ -2882,17 +3235,17 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2882
3235
|
name: generatedName2,
|
|
2883
3236
|
selector: {
|
|
2884
3237
|
kind: "withinTestIdByLabel",
|
|
2885
|
-
|
|
2886
|
-
|
|
3238
|
+
rootTestId: createPomStringPattern(wrapperTestId, selectorPatternKind),
|
|
3239
|
+
label: createPomStringPattern(label, "static"),
|
|
2887
3240
|
exact: true
|
|
2888
3241
|
},
|
|
2889
|
-
|
|
3242
|
+
parameters: [createPomParameterSpec("annotationText", `string = ""`)]
|
|
2890
3243
|
});
|
|
2891
3244
|
if (added2) {
|
|
2892
|
-
registerGeneratedMethodSignature(generatedName2,
|
|
3245
|
+
registerGeneratedMethodSignature(generatedName2, createPomMethodSignature([createPomParameterSpec("annotationText", `string = ""`)]));
|
|
2893
3246
|
}
|
|
2894
3247
|
}
|
|
2895
|
-
return;
|
|
3248
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2896
3249
|
}
|
|
2897
3250
|
const generatedName = ensureUniqueGeneratedName(`select${upperFirst(methodName || "Radio")}`);
|
|
2898
3251
|
if (dataTestIdEntry.pom) {
|
|
@@ -2904,19 +3257,25 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2904
3257
|
name: generatedName,
|
|
2905
3258
|
selector: {
|
|
2906
3259
|
kind: "withinTestIdByLabel",
|
|
2907
|
-
|
|
2908
|
-
|
|
3260
|
+
rootTestId: createPomStringPattern(wrapperTestId, selectorPatternKind),
|
|
3261
|
+
label: createPomStringPattern("${value}", "parameterized"),
|
|
2909
3262
|
exact: true
|
|
2910
3263
|
},
|
|
2911
|
-
|
|
3264
|
+
parameters: [
|
|
3265
|
+
createPomParameterSpec("value", "string"),
|
|
3266
|
+
createPomParameterSpec("annotationText", `string = ""`)
|
|
3267
|
+
]
|
|
2912
3268
|
});
|
|
2913
3269
|
if (added) {
|
|
2914
|
-
registerGeneratedMethodSignature(generatedName,
|
|
3270
|
+
registerGeneratedMethodSignature(generatedName, createPomMethodSignature([
|
|
3271
|
+
createPomParameterSpec("value", "string"),
|
|
3272
|
+
createPomParameterSpec("annotationText", `string = ""`)
|
|
3273
|
+
]));
|
|
2915
3274
|
}
|
|
2916
|
-
return;
|
|
3275
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2917
3276
|
}
|
|
2918
3277
|
const staticKeyValues = args.keyValuesOverride ?? null;
|
|
2919
|
-
const needsKey =
|
|
3278
|
+
const needsKey = hasPomParameter(normalizedParameters, "key") && selectorIsParameterized;
|
|
2920
3279
|
const isNavigation = !!dataTestIdEntry.targetPageObjectModelClass;
|
|
2921
3280
|
if (staticKeyValues && staticKeyValues.length > 0 && needsKey && !isNavigation && normalizedRole !== "input" && normalizedRole !== "select" && normalizedRole !== "vselect" && normalizedRole !== "radio") {
|
|
2922
3281
|
if (dataTestIdEntry.pom) {
|
|
@@ -2935,19 +3294,19 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2935
3294
|
name: generatedName,
|
|
2936
3295
|
selector: {
|
|
2937
3296
|
kind: "testId",
|
|
2938
|
-
|
|
3297
|
+
testId: selectorPattern
|
|
2939
3298
|
},
|
|
2940
3299
|
keyLiteral: rawValue,
|
|
2941
|
-
|
|
3300
|
+
parameters: [createPomParameterSpec("wait", "boolean = true"), createPomParameterSpec("annotationText", 'string = ""')]
|
|
2942
3301
|
});
|
|
2943
3302
|
if (added) {
|
|
2944
|
-
registerGeneratedMethodSignature(generatedName,
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
3303
|
+
registerGeneratedMethodSignature(generatedName, createPomMethodSignature([
|
|
3304
|
+
createPomParameterSpec("wait", "boolean = true"),
|
|
3305
|
+
createPomParameterSpec("annotationText", 'string = ""')
|
|
3306
|
+
]));
|
|
2948
3307
|
}
|
|
2949
3308
|
}
|
|
2950
|
-
return;
|
|
3309
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2951
3310
|
}
|
|
2952
3311
|
if (dataTestIdEntry.pom) {
|
|
2953
3312
|
if (dataTestIdEntry.pom.emitPrimary !== false) {
|
|
@@ -2961,6 +3320,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2961
3320
|
const generatedName = getGeneratedMethodName();
|
|
2962
3321
|
registerGeneratedMethodSignature(generatedName, signature);
|
|
2963
3322
|
}
|
|
3323
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2964
3324
|
}
|
|
2965
3325
|
function safeRealpath(value) {
|
|
2966
3326
|
try {
|
|
@@ -3948,113 +4308,6 @@ class VuePomGeneratorError extends Error {
|
|
|
3948
4308
|
this.name = "VuePomGeneratorError";
|
|
3949
4309
|
}
|
|
3950
4310
|
}
|
|
3951
|
-
function splitParameterList(parameters) {
|
|
3952
|
-
const parts = [];
|
|
3953
|
-
let current = "";
|
|
3954
|
-
let braceDepth = 0;
|
|
3955
|
-
let bracketDepth = 0;
|
|
3956
|
-
let parenDepth = 0;
|
|
3957
|
-
let angleDepth = 0;
|
|
3958
|
-
let inSingleQuote = false;
|
|
3959
|
-
let inDoubleQuote = false;
|
|
3960
|
-
let inTemplateString = false;
|
|
3961
|
-
for (let index = 0; index < parameters.length; index += 1) {
|
|
3962
|
-
const char = parameters[index];
|
|
3963
|
-
const previous = index > 0 ? parameters[index - 1] : "";
|
|
3964
|
-
if (char === "'" && !inDoubleQuote && !inTemplateString && previous !== "\\") {
|
|
3965
|
-
inSingleQuote = !inSingleQuote;
|
|
3966
|
-
current += char;
|
|
3967
|
-
continue;
|
|
3968
|
-
}
|
|
3969
|
-
if (char === '"' && !inSingleQuote && !inTemplateString && previous !== "\\") {
|
|
3970
|
-
inDoubleQuote = !inDoubleQuote;
|
|
3971
|
-
current += char;
|
|
3972
|
-
continue;
|
|
3973
|
-
}
|
|
3974
|
-
if (char === "`" && !inSingleQuote && !inDoubleQuote && previous !== "\\") {
|
|
3975
|
-
inTemplateString = !inTemplateString;
|
|
3976
|
-
current += char;
|
|
3977
|
-
continue;
|
|
3978
|
-
}
|
|
3979
|
-
if (inSingleQuote || inDoubleQuote || inTemplateString) {
|
|
3980
|
-
current += char;
|
|
3981
|
-
continue;
|
|
3982
|
-
}
|
|
3983
|
-
switch (char) {
|
|
3984
|
-
case "{":
|
|
3985
|
-
braceDepth += 1;
|
|
3986
|
-
break;
|
|
3987
|
-
case "}":
|
|
3988
|
-
braceDepth -= 1;
|
|
3989
|
-
break;
|
|
3990
|
-
case "[":
|
|
3991
|
-
bracketDepth += 1;
|
|
3992
|
-
break;
|
|
3993
|
-
case "]":
|
|
3994
|
-
bracketDepth -= 1;
|
|
3995
|
-
break;
|
|
3996
|
-
case "(":
|
|
3997
|
-
parenDepth += 1;
|
|
3998
|
-
break;
|
|
3999
|
-
case ")":
|
|
4000
|
-
parenDepth -= 1;
|
|
4001
|
-
break;
|
|
4002
|
-
case "<":
|
|
4003
|
-
angleDepth += 1;
|
|
4004
|
-
break;
|
|
4005
|
-
case ">":
|
|
4006
|
-
angleDepth -= 1;
|
|
4007
|
-
break;
|
|
4008
|
-
case ",":
|
|
4009
|
-
if (braceDepth === 0 && bracketDepth === 0 && parenDepth === 0 && angleDepth === 0) {
|
|
4010
|
-
const trimmed2 = current.trim();
|
|
4011
|
-
if (trimmed2) {
|
|
4012
|
-
parts.push(trimmed2);
|
|
4013
|
-
}
|
|
4014
|
-
current = "";
|
|
4015
|
-
continue;
|
|
4016
|
-
}
|
|
4017
|
-
break;
|
|
4018
|
-
}
|
|
4019
|
-
current += char;
|
|
4020
|
-
}
|
|
4021
|
-
const trimmed = current.trim();
|
|
4022
|
-
if (trimmed) {
|
|
4023
|
-
parts.push(trimmed);
|
|
4024
|
-
}
|
|
4025
|
-
return parts;
|
|
4026
|
-
}
|
|
4027
|
-
function parseParameterSignature(parameter) {
|
|
4028
|
-
const colonIndex = parameter.indexOf(":");
|
|
4029
|
-
if (colonIndex < 0) {
|
|
4030
|
-
return { name: parameter.trim() };
|
|
4031
|
-
}
|
|
4032
|
-
const rawName = parameter.slice(0, colonIndex).trim();
|
|
4033
|
-
const hasQuestionToken = rawName.endsWith("?");
|
|
4034
|
-
const name = hasQuestionToken ? rawName.slice(0, -1).trim() : rawName;
|
|
4035
|
-
const remainder = parameter.slice(colonIndex + 1).trim();
|
|
4036
|
-
const initializerIndex = remainder.lastIndexOf("=");
|
|
4037
|
-
if (initializerIndex < 0) {
|
|
4038
|
-
return {
|
|
4039
|
-
name,
|
|
4040
|
-
hasQuestionToken,
|
|
4041
|
-
type: remainder || void 0
|
|
4042
|
-
};
|
|
4043
|
-
}
|
|
4044
|
-
return {
|
|
4045
|
-
name,
|
|
4046
|
-
hasQuestionToken,
|
|
4047
|
-
type: remainder.slice(0, initializerIndex).trim() || void 0,
|
|
4048
|
-
initializer: remainder.slice(initializerIndex + 1).trim() || void 0
|
|
4049
|
-
};
|
|
4050
|
-
}
|
|
4051
|
-
function parseParameterSignatures(parameters) {
|
|
4052
|
-
const trimmed = parameters.trim();
|
|
4053
|
-
if (!trimmed) {
|
|
4054
|
-
return [];
|
|
4055
|
-
}
|
|
4056
|
-
return splitParameterList(trimmed).map(parseParameterSignature);
|
|
4057
|
-
}
|
|
4058
4311
|
function toPosixRelativePath(fromDir, toFile) {
|
|
4059
4312
|
let rel = path.relative(fromDir, toFile).replace(/\\/g, "/");
|
|
4060
4313
|
if (!rel.startsWith(".")) {
|
|
@@ -4074,6 +4327,21 @@ function resolveRouterEntry(projectRoot, routerEntry) {
|
|
|
4074
4327
|
const root = projectRoot ?? process.cwd();
|
|
4075
4328
|
return path.isAbsolute(routerEntry) ? routerEntry : path.resolve(root, routerEntry);
|
|
4076
4329
|
}
|
|
4330
|
+
function createMissingCustomPomDirectoryError(configuredDir, resolvedDir) {
|
|
4331
|
+
return new VuePomGeneratorError(
|
|
4332
|
+
`Custom POM directory "${configuredDir}" does not exist.
|
|
4333
|
+
Resolved path: ${resolvedDir}
|
|
4334
|
+
Create the directory, point generation.playwright.customPoms.dir at the correct location, or remove the customPoms configuration.`
|
|
4335
|
+
);
|
|
4336
|
+
}
|
|
4337
|
+
function createMissingCustomPomAttachmentClassError(missingClassNames, configuredDir) {
|
|
4338
|
+
const renderedClassNames = missingClassNames.map((name) => `"${name}"`).join(", ");
|
|
4339
|
+
return new VuePomGeneratorError(
|
|
4340
|
+
`Custom POM attachments reference missing helper classes: ${renderedClassNames}.
|
|
4341
|
+
Expected matching helper files/exports under "${configuredDir}".
|
|
4342
|
+
Add the missing helper classes or remove the corresponding generation.playwright.customPoms.attachments entries.`
|
|
4343
|
+
);
|
|
4344
|
+
}
|
|
4077
4345
|
function createCustomPomImportCollisionError(exportName, requested) {
|
|
4078
4346
|
return new VuePomGeneratorError(
|
|
4079
4347
|
`Custom POM import name collision detected for "${exportName}".
|
|
@@ -4212,35 +4480,28 @@ function generateGoToSelfMethod(componentName) {
|
|
|
4212
4480
|
})
|
|
4213
4481
|
];
|
|
4214
4482
|
}
|
|
4215
|
-
function
|
|
4216
|
-
|
|
4217
|
-
return "";
|
|
4218
|
-
const preferredOrder = ["key", "value", "text", "timeOut", "annotationText", "wait"];
|
|
4219
|
-
const entries = Object.entries(params);
|
|
4220
|
-
if (!entries.length)
|
|
4221
|
-
return "";
|
|
4222
|
-
const score = (name) => {
|
|
4223
|
-
const idx = preferredOrder.indexOf(name);
|
|
4224
|
-
return idx < 0 ? 999 : idx;
|
|
4225
|
-
};
|
|
4226
|
-
return entries.slice().sort((a, b) => score(a[0]) - score(b[0]) || a[0].localeCompare(b[0])).map(([name, typeExpr]) => `${name}: ${typeExpr}`).join(", ");
|
|
4483
|
+
function getSelectorPatterns(selector) {
|
|
4484
|
+
return selector.kind === "testId" ? [selector.testId] : [selector.rootTestId, selector.label];
|
|
4227
4485
|
}
|
|
4228
4486
|
function generateExtraClickMethodMembers(spec) {
|
|
4229
4487
|
if (spec.kind !== "click") {
|
|
4230
4488
|
return [];
|
|
4231
4489
|
}
|
|
4232
|
-
const
|
|
4233
|
-
const
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4490
|
+
const selectorPatterns = getSelectorPatterns(spec.selector);
|
|
4491
|
+
const signatureSpecs = orderPomPatternParameters(
|
|
4492
|
+
spec.parameters,
|
|
4493
|
+
selectorPatterns,
|
|
4494
|
+
{ omit: spec.keyLiteral !== void 0 ? ["key"] : [] }
|
|
4495
|
+
);
|
|
4496
|
+
const parameters = toTypeScriptPomParameterStructures(signatureSpecs);
|
|
4497
|
+
const hasAnnotationText = signatureSpecs.some((param) => param.name === "annotationText");
|
|
4498
|
+
const hasWait = signatureSpecs.some((param) => param.name === "wait");
|
|
4237
4499
|
const annotationArg = hasAnnotationText ? "annotationText" : '""';
|
|
4238
4500
|
const waitArg = hasWait ? "wait" : "true";
|
|
4239
4501
|
if (spec.selector.kind === "testId") {
|
|
4240
|
-
const
|
|
4241
|
-
const testIdExpr = needsTemplate ? `\`${spec.selector.formattedDataTestId}\`` : JSON.stringify(spec.selector.formattedDataTestId);
|
|
4502
|
+
const testIdBinding = bindTypeScriptPomPattern(spec.selector.testId, "testId");
|
|
4242
4503
|
const clickArgs = [];
|
|
4243
|
-
clickArgs.push(
|
|
4504
|
+
clickArgs.push(testIdBinding.expression);
|
|
4244
4505
|
if (hasAnnotationText || hasWait) {
|
|
4245
4506
|
clickArgs.push(annotationArg);
|
|
4246
4507
|
}
|
|
@@ -4256,20 +4517,16 @@ function generateExtraClickMethodMembers(spec) {
|
|
|
4256
4517
|
if (spec.keyLiteral !== void 0) {
|
|
4257
4518
|
writer.writeLine(`const key = ${JSON.stringify(spec.keyLiteral)};`);
|
|
4258
4519
|
}
|
|
4259
|
-
|
|
4260
|
-
writer.writeLine(
|
|
4520
|
+
for (const statement of testIdBinding.setupStatements) {
|
|
4521
|
+
writer.writeLine(statement);
|
|
4261
4522
|
}
|
|
4262
4523
|
writer.writeLine(`await this.clickByTestId(${clickArgs.join(", ")});`);
|
|
4263
4524
|
}
|
|
4264
4525
|
})
|
|
4265
4526
|
];
|
|
4266
4527
|
}
|
|
4267
|
-
const
|
|
4268
|
-
const
|
|
4269
|
-
const rootExpr = rootNeedsTemplate ? `\`${spec.selector.rootFormattedDataTestId}\`` : JSON.stringify(spec.selector.rootFormattedDataTestId);
|
|
4270
|
-
const labelExpr = labelNeedsTemplate ? `\`${spec.selector.formattedLabel}\`` : JSON.stringify(spec.selector.formattedLabel);
|
|
4271
|
-
const rootArg = rootNeedsTemplate ? "rootTestId" : rootExpr;
|
|
4272
|
-
const labelArg = labelNeedsTemplate ? "label" : labelExpr;
|
|
4528
|
+
const rootBinding = bindTypeScriptPomPattern(spec.selector.rootTestId, "rootTestId");
|
|
4529
|
+
const labelBinding = bindTypeScriptPomPattern(spec.selector.label, "label");
|
|
4273
4530
|
return [
|
|
4274
4531
|
createClassMethod({
|
|
4275
4532
|
name: spec.name,
|
|
@@ -4279,13 +4536,13 @@ function generateExtraClickMethodMembers(spec) {
|
|
|
4279
4536
|
if (spec.keyLiteral !== void 0) {
|
|
4280
4537
|
writer.writeLine(`const key = ${JSON.stringify(spec.keyLiteral)};`);
|
|
4281
4538
|
}
|
|
4282
|
-
|
|
4283
|
-
writer.writeLine(
|
|
4539
|
+
for (const statement of rootBinding.setupStatements) {
|
|
4540
|
+
writer.writeLine(statement);
|
|
4284
4541
|
}
|
|
4285
|
-
|
|
4286
|
-
writer.writeLine(
|
|
4542
|
+
for (const statement of labelBinding.setupStatements) {
|
|
4543
|
+
writer.writeLine(statement);
|
|
4287
4544
|
}
|
|
4288
|
-
writer.writeLine(`await this.clickWithinTestIdByLabel(${
|
|
4545
|
+
writer.writeLine(`await this.clickWithinTestIdByLabel(${rootBinding.expression}, ${labelBinding.expression}, ${annotationArg}, ${waitArg});`);
|
|
4289
4546
|
}
|
|
4290
4547
|
})
|
|
4291
4548
|
];
|
|
@@ -4298,10 +4555,10 @@ function generateMethodMembersFromPom(primary, targetPageObjectModelClass) {
|
|
|
4298
4555
|
targetPageObjectModelClass,
|
|
4299
4556
|
primary.methodName,
|
|
4300
4557
|
primary.nativeRole,
|
|
4301
|
-
primary.
|
|
4302
|
-
primary.
|
|
4558
|
+
primary.selector,
|
|
4559
|
+
primary.alternateSelectors,
|
|
4303
4560
|
primary.getterNameOverride,
|
|
4304
|
-
primary.
|
|
4561
|
+
primary.parameters
|
|
4305
4562
|
);
|
|
4306
4563
|
}
|
|
4307
4564
|
function generateMethodsContentForDependencies(dependencies) {
|
|
@@ -4309,15 +4566,14 @@ function generateMethodsContentForDependencies(dependencies) {
|
|
|
4309
4566
|
const primarySpecsAll = entries.map((e) => ({ pom: e.pom, target: e.targetPageObjectModelClass })).filter((x) => !!x.pom).sort((a, b) => a.pom.methodName.localeCompare(b.pom.methodName));
|
|
4310
4567
|
const seenPrimaryKeys = /* @__PURE__ */ new Set();
|
|
4311
4568
|
const primarySpecs = primarySpecsAll.filter(({ pom, target }) => {
|
|
4312
|
-
const
|
|
4313
|
-
const alternates = (pom.alternateFormattedDataTestIds ?? []).slice().sort();
|
|
4569
|
+
const alternates = (pom.alternateSelectors ?? []).slice().sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
4314
4570
|
const key = JSON.stringify({
|
|
4315
4571
|
role: pom.nativeRole,
|
|
4316
4572
|
methodName: pom.methodName,
|
|
4317
4573
|
getterNameOverride: pom.getterNameOverride ?? null,
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4574
|
+
selector: pom.selector,
|
|
4575
|
+
alternateSelectors: alternates.length ? alternates : void 0,
|
|
4576
|
+
parameters: pom.parameters,
|
|
4321
4577
|
target: target ?? null,
|
|
4322
4578
|
emitPrimary: pom.emitPrimary ?? true
|
|
4323
4579
|
});
|
|
@@ -4344,6 +4600,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
|
|
|
4344
4600
|
customPomAttachments = [],
|
|
4345
4601
|
projectRoot,
|
|
4346
4602
|
customPomDir,
|
|
4603
|
+
requireCustomPomDir,
|
|
4347
4604
|
customPomImportAliases,
|
|
4348
4605
|
customPomImportNameCollisionBehavior = "error",
|
|
4349
4606
|
testIdAttribute,
|
|
@@ -4376,6 +4633,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
|
|
|
4376
4633
|
customPomAttachments,
|
|
4377
4634
|
projectRoot,
|
|
4378
4635
|
customPomDir,
|
|
4636
|
+
requireCustomPomDir,
|
|
4379
4637
|
customPomImportAliases,
|
|
4380
4638
|
customPomImportNameCollisionBehavior,
|
|
4381
4639
|
testIdAttribute,
|
|
@@ -4385,6 +4643,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
|
|
|
4385
4643
|
customPomAttachments,
|
|
4386
4644
|
projectRoot,
|
|
4387
4645
|
customPomDir,
|
|
4646
|
+
requireCustomPomDir,
|
|
4388
4647
|
customPomImportAliases,
|
|
4389
4648
|
customPomImportNameCollisionBehavior,
|
|
4390
4649
|
testIdAttribute,
|
|
@@ -4440,9 +4699,15 @@ async function generateSplitTypeScriptFiles(componentHierarchyMap, vueFilesPathM
|
|
|
4440
4699
|
}
|
|
4441
4700
|
const customPomImportResolution = resolveCustomPomImportResolution(generatedClassNames, projectRoot, {
|
|
4442
4701
|
customPomDir: options.customPomDir,
|
|
4702
|
+
requireCustomPomDir: options.requireCustomPomDir,
|
|
4443
4703
|
customPomImportAliases: options.customPomImportAliases,
|
|
4444
4704
|
customPomImportNameCollisionBehavior: options.customPomImportNameCollisionBehavior
|
|
4445
4705
|
});
|
|
4706
|
+
assertCustomPomAttachmentsResolved(
|
|
4707
|
+
options.customPomAttachments ?? [],
|
|
4708
|
+
customPomImportResolution.classIdentifierMap,
|
|
4709
|
+
options.customPomDir ?? "tests/playwright/pom/custom"
|
|
4710
|
+
);
|
|
4446
4711
|
const runtimeBasePagePath = path.join(base, "_pom-runtime", "class-generation", "base-page.ts");
|
|
4447
4712
|
const files = [];
|
|
4448
4713
|
for (const [name, deps] of entries) {
|
|
@@ -4601,21 +4866,8 @@ function buildGeneratedGitAttributesFiles(generatedFilePaths) {
|
|
|
4601
4866
|
return { filePath, content };
|
|
4602
4867
|
});
|
|
4603
4868
|
}
|
|
4604
|
-
function
|
|
4605
|
-
const
|
|
4606
|
-
if (!needsInterpolation) {
|
|
4607
|
-
return JSON.stringify(formattedDataTestId);
|
|
4608
|
-
}
|
|
4609
|
-
const inner = formattedDataTestId.replace(/\$\{/g, "{");
|
|
4610
|
-
const quoted = JSON.stringify(inner);
|
|
4611
|
-
return `$${quoted}`;
|
|
4612
|
-
}
|
|
4613
|
-
function toCSharpParam(paramTypeExpr) {
|
|
4614
|
-
const trimmed = (paramTypeExpr ?? "").trim();
|
|
4615
|
-
const eqIdx = trimmed.indexOf("=");
|
|
4616
|
-
const left = eqIdx >= 0 ? trimmed.slice(0, eqIdx).trim() : trimmed;
|
|
4617
|
-
const right = eqIdx >= 0 ? trimmed.slice(eqIdx + 1).trim() : void 0;
|
|
4618
|
-
const typePart = left.includes("|") ? "string" : left;
|
|
4869
|
+
function toCSharpParam(param) {
|
|
4870
|
+
const typePart = param.type?.includes("|") ? "string" : param.type ?? "string";
|
|
4619
4871
|
let type = "string";
|
|
4620
4872
|
if (/(?:^|\s)boolean(?:\s|$)/.test(typePart))
|
|
4621
4873
|
type = "bool";
|
|
@@ -4623,19 +4875,17 @@ function toCSharpParam(paramTypeExpr) {
|
|
|
4623
4875
|
type = "string";
|
|
4624
4876
|
else if (/(?:^|\s)number(?:\s|$)/.test(typePart))
|
|
4625
4877
|
type = "int";
|
|
4626
|
-
else if (/\d+/.test(typePart) && typePart === "")
|
|
4627
|
-
type = "int";
|
|
4628
4878
|
else if (/\btimeOut\b/i.test(typePart))
|
|
4629
4879
|
type = "int";
|
|
4630
4880
|
let defaultExpr;
|
|
4631
|
-
if (
|
|
4881
|
+
if (param.initializer !== void 0) {
|
|
4632
4882
|
if (type === "bool") {
|
|
4633
|
-
defaultExpr =
|
|
4883
|
+
defaultExpr = param.initializer.includes("true") ? "true" : param.initializer.includes("false") ? "false" : void 0;
|
|
4634
4884
|
} else if (type === "int") {
|
|
4635
|
-
const m =
|
|
4885
|
+
const m = param.initializer.match(/\d+/);
|
|
4636
4886
|
defaultExpr = m ? m[0] : void 0;
|
|
4637
4887
|
} else {
|
|
4638
|
-
if (
|
|
4888
|
+
if (param.initializer === '""' || param.initializer === "''") {
|
|
4639
4889
|
defaultExpr = '""';
|
|
4640
4890
|
}
|
|
4641
4891
|
}
|
|
@@ -4643,17 +4893,15 @@ function toCSharpParam(paramTypeExpr) {
|
|
|
4643
4893
|
return { type, defaultExpr };
|
|
4644
4894
|
}
|
|
4645
4895
|
function formatCSharpParams(params) {
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
const entries = Object.entries(params);
|
|
4649
|
-
if (!entries.length)
|
|
4896
|
+
const normalizedParams = normalizePomParameters(params);
|
|
4897
|
+
if (!normalizedParams.length)
|
|
4650
4898
|
return { signature: "", argNames: [] };
|
|
4651
4899
|
const signatureParts = [];
|
|
4652
4900
|
const argNames = [];
|
|
4653
|
-
for (const
|
|
4654
|
-
const { type, defaultExpr } = toCSharpParam(
|
|
4655
|
-
argNames.push(name);
|
|
4656
|
-
signatureParts.push(defaultExpr !== void 0 ? `${type} ${name} = ${defaultExpr}` : `${type} ${name}`);
|
|
4901
|
+
for (const param of normalizedParams) {
|
|
4902
|
+
const { type, defaultExpr } = toCSharpParam(param);
|
|
4903
|
+
argNames.push(param.name);
|
|
4904
|
+
signatureParts.push(defaultExpr !== void 0 ? `${type} ${param.name} = ${defaultExpr}` : `${type} ${param.name}`);
|
|
4657
4905
|
}
|
|
4658
4906
|
return { signature: signatureParts.join(", "), argNames };
|
|
4659
4907
|
}
|
|
@@ -4744,23 +4992,13 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4744
4992
|
const baseMethodName = upperFirst(pom.methodName);
|
|
4745
4993
|
const baseGetterName = upperFirst(pom.getterNameOverride ?? pom.methodName);
|
|
4746
4994
|
const locatorName = baseGetterName.endsWith(roleSuffix) ? baseGetterName : `${baseGetterName}${roleSuffix}`;
|
|
4747
|
-
const
|
|
4748
|
-
const
|
|
4749
|
-
const
|
|
4750
|
-
const augmentedParams = { ...pom.params };
|
|
4751
|
-
for (const v of templateVars) {
|
|
4752
|
-
if (!Object.prototype.hasOwnProperty.call(augmentedParams, v)) {
|
|
4753
|
-
augmentedParams[v] = "string";
|
|
4754
|
-
}
|
|
4755
|
-
}
|
|
4756
|
-
const orderedParams = Object.fromEntries([
|
|
4757
|
-
...templateVars.map((v) => [v, augmentedParams[v]]),
|
|
4758
|
-
...Object.entries(augmentedParams).filter(([k]) => !templateVars.includes(k))
|
|
4759
|
-
]);
|
|
4995
|
+
const selectorIsParameterized = isParameterizedPomPattern(pom.selector.patternKind);
|
|
4996
|
+
const testIdExpr = toCSharpPomPatternExpression(pom.selector);
|
|
4997
|
+
const orderedParams = orderPomPatternParameters(pom.parameters, [pom.selector]);
|
|
4760
4998
|
const { signature, argNames } = formatCSharpParams(orderedParams);
|
|
4761
4999
|
const args = argNames.join(", ");
|
|
4762
|
-
const allTestIds =
|
|
4763
|
-
if (
|
|
5000
|
+
const allTestIds = uniquePomStringPatterns(pom.selector, pom.alternateSelectors);
|
|
5001
|
+
if (selectorIsParameterized) {
|
|
4764
5002
|
chunks.push(` public ILocator ${locatorName}(${signature}) => LocatorByTestId(${testIdExpr});`);
|
|
4765
5003
|
} else {
|
|
4766
5004
|
chunks.push(` public ILocator ${locatorName} => LocatorByTestId(${testIdExpr});`);
|
|
@@ -4771,12 +5009,12 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4771
5009
|
if (target) {
|
|
4772
5010
|
chunks.push(` public async Task<${target}> ${actionName}(${sig})`);
|
|
4773
5011
|
chunks.push(" {");
|
|
4774
|
-
if (
|
|
4775
|
-
chunks.push(` await ${locatorName}${
|
|
5012
|
+
if (selectorIsParameterized || allTestIds.length <= 1) {
|
|
5013
|
+
chunks.push(` await ${locatorName}${selectorIsParameterized ? `(${args})` : ""}.ClickAsync();`);
|
|
4776
5014
|
chunks.push(` return new ${target}(Page);`);
|
|
4777
5015
|
} else {
|
|
4778
5016
|
chunks.push(" Exception? lastError = null;");
|
|
4779
|
-
chunks.push(` foreach (var testId in new[] { ${allTestIds.map(
|
|
5017
|
+
chunks.push(` foreach (var testId in new[] { ${allTestIds.map((testId) => toCSharpPomPatternExpression(testId)).join(", ")} })`);
|
|
4780
5018
|
chunks.push(" {");
|
|
4781
5019
|
chunks.push(" try");
|
|
4782
5020
|
chunks.push(" {");
|
|
@@ -4800,7 +5038,7 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4800
5038
|
}
|
|
4801
5039
|
chunks.push(` public async Task ${actionName}(${sig})`);
|
|
4802
5040
|
chunks.push(" {");
|
|
4803
|
-
const callSuffix =
|
|
5041
|
+
const callSuffix = selectorIsParameterized ? `(${args})` : "";
|
|
4804
5042
|
const emitActionCall = (locatorAccess) => {
|
|
4805
5043
|
if (pom.nativeRole === "input") {
|
|
4806
5044
|
chunks.push(` var editableLocator = await ResolveEditableLocatorAsync(${locatorAccess});`);
|
|
@@ -4813,9 +5051,9 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4813
5051
|
chunks.push(` await ${locatorAccess}.ClickAsync();`);
|
|
4814
5052
|
}
|
|
4815
5053
|
};
|
|
4816
|
-
if (!
|
|
5054
|
+
if (!selectorIsParameterized && allTestIds.length > 1) {
|
|
4817
5055
|
chunks.push(" Exception? lastError = null;");
|
|
4818
|
-
chunks.push(` foreach (var testId in new[] { ${allTestIds.map(
|
|
5056
|
+
chunks.push(` foreach (var testId in new[] { ${allTestIds.map((testId) => toCSharpPomPatternExpression(testId)).join(", ")} })`);
|
|
4819
5057
|
chunks.push(" {");
|
|
4820
5058
|
chunks.push(" try");
|
|
4821
5059
|
chunks.push(" {");
|
|
@@ -4861,7 +5099,12 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4861
5099
|
for (const extra of extras) {
|
|
4862
5100
|
if (extra.kind !== "click")
|
|
4863
5101
|
continue;
|
|
4864
|
-
const
|
|
5102
|
+
const extraParams = orderPomPatternParameters(
|
|
5103
|
+
extra.parameters,
|
|
5104
|
+
getSelectorPatterns(extra.selector),
|
|
5105
|
+
{ omit: extra.keyLiteral !== void 0 ? ["key"] : [] }
|
|
5106
|
+
);
|
|
5107
|
+
const { signature } = formatCSharpParams(extraParams);
|
|
4865
5108
|
const extraName = upperFirst(extra.name);
|
|
4866
5109
|
chunks.push(` public async Task ${extraName}Async(${signature})`);
|
|
4867
5110
|
chunks.push(" {");
|
|
@@ -4869,29 +5112,22 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4869
5112
|
chunks.push(` var key = ${JSON.stringify(extra.keyLiteral)};`);
|
|
4870
5113
|
}
|
|
4871
5114
|
if (extra.selector.kind === "testId") {
|
|
4872
|
-
const
|
|
4873
|
-
const
|
|
4874
|
-
|
|
4875
|
-
chunks.push(` var testId = ${testIdExpr};`);
|
|
4876
|
-
chunks.push(" await LocatorByTestId(testId).ClickAsync();");
|
|
4877
|
-
} else {
|
|
4878
|
-
chunks.push(` await LocatorByTestId(${testIdExpr}).ClickAsync();`);
|
|
5115
|
+
const testIdBinding = bindCSharpPomPattern(extra.selector.testId, "testId");
|
|
5116
|
+
for (const statement of testIdBinding.setupStatements) {
|
|
5117
|
+
chunks.push(` ${statement}`);
|
|
4879
5118
|
}
|
|
5119
|
+
chunks.push(` await LocatorByTestId(${testIdBinding.expression}).ClickAsync();`);
|
|
4880
5120
|
} else {
|
|
4881
|
-
const
|
|
4882
|
-
const
|
|
4883
|
-
const rootExpr = toCSharpTestIdExpression(extra.selector.rootFormattedDataTestId);
|
|
4884
|
-
const labelExpr = toCSharpTestIdExpression(extra.selector.formattedLabel);
|
|
5121
|
+
const rootBinding = bindCSharpPomPattern(extra.selector.rootTestId, "rootTestId");
|
|
5122
|
+
const labelBinding = bindCSharpPomPattern(extra.selector.label, "label");
|
|
4885
5123
|
const exactArg = extra.selector.exact === false ? "false" : "true";
|
|
4886
|
-
|
|
4887
|
-
chunks.push(`
|
|
5124
|
+
for (const statement of rootBinding.setupStatements) {
|
|
5125
|
+
chunks.push(` ${statement}`);
|
|
4888
5126
|
}
|
|
4889
|
-
|
|
4890
|
-
chunks.push(`
|
|
5127
|
+
for (const statement of labelBinding.setupStatements) {
|
|
5128
|
+
chunks.push(` ${statement}`);
|
|
4891
5129
|
}
|
|
4892
|
-
|
|
4893
|
-
const labelArg = labelNeedsTemplate ? "label" : labelExpr;
|
|
4894
|
-
chunks.push(` await ClickWithinTestIdByLabelAsync(${rootArg}, ${labelArg}, ${exactArg});`);
|
|
5130
|
+
chunks.push(` await ClickWithinTestIdByLabelAsync(${rootBinding.expression}, ${labelBinding.expression}, ${exactArg});`);
|
|
4895
5131
|
}
|
|
4896
5132
|
chunks.push(" }");
|
|
4897
5133
|
chunks.push("");
|
|
@@ -5335,7 +5571,7 @@ function getViewPassthroughMethods(viewName, viewDependencies, childrenComponent
|
|
|
5335
5571
|
if (existingOnView.has(name) || blockedMethodNames.has(name))
|
|
5336
5572
|
continue;
|
|
5337
5573
|
const list = methodToChildren.get(name) ?? [];
|
|
5338
|
-
list.push({ childProp,
|
|
5574
|
+
list.push({ childProp, signature: sig });
|
|
5339
5575
|
methodToChildren.set(name, list);
|
|
5340
5576
|
}
|
|
5341
5577
|
}
|
|
@@ -5345,12 +5581,12 @@ function getViewPassthroughMethods(viewName, viewDependencies, childrenComponent
|
|
|
5345
5581
|
return [];
|
|
5346
5582
|
}
|
|
5347
5583
|
return passthroughs.map(([methodName, candidates]) => {
|
|
5348
|
-
const { childProp,
|
|
5349
|
-
const callArgs =
|
|
5584
|
+
const { childProp, signature } = candidates[0];
|
|
5585
|
+
const callArgs = getPomParameterArgumentNames(signature.parameters).join(", ");
|
|
5350
5586
|
return createClassMethod({
|
|
5351
5587
|
name: methodName,
|
|
5352
5588
|
isAsync: true,
|
|
5353
|
-
parameters:
|
|
5589
|
+
parameters: toTypeScriptPomParameterStructures(signature.parameters),
|
|
5354
5590
|
statements: [
|
|
5355
5591
|
`return await this.${childProp}.${methodName}(${callArgs});`
|
|
5356
5592
|
]
|
|
@@ -5374,8 +5610,7 @@ function getAttachmentPassthroughMethods(ownerName, ownerDependencies, attachmen
|
|
|
5374
5610
|
const list = methodToAttachments.get(methodName) ?? [];
|
|
5375
5611
|
list.push({
|
|
5376
5612
|
propertyName: attachment.propertyName,
|
|
5377
|
-
|
|
5378
|
-
argNames: signature.argNames
|
|
5613
|
+
signature
|
|
5379
5614
|
});
|
|
5380
5615
|
methodToAttachments.set(methodName, list);
|
|
5381
5616
|
}
|
|
@@ -5386,12 +5621,12 @@ function getAttachmentPassthroughMethods(ownerName, ownerDependencies, attachmen
|
|
|
5386
5621
|
return [];
|
|
5387
5622
|
}
|
|
5388
5623
|
return passthroughs.map(([methodName, candidates]) => {
|
|
5389
|
-
const { propertyName,
|
|
5390
|
-
const callArgs =
|
|
5624
|
+
const { propertyName, signature } = candidates[0];
|
|
5625
|
+
const callArgs = getPomParameterArgumentNames(signature.parameters).join(", ");
|
|
5391
5626
|
const invocation = callArgs ? `this.${propertyName}.${methodName}(${callArgs})` : `this.${propertyName}.${methodName}()`;
|
|
5392
5627
|
return createClassMethod({
|
|
5393
5628
|
name: methodName,
|
|
5394
|
-
parameters:
|
|
5629
|
+
parameters: toTypeScriptPomParameterStructures(signature.parameters),
|
|
5395
5630
|
statements: [
|
|
5396
5631
|
`return ${invocation};`
|
|
5397
5632
|
]
|
|
@@ -5405,15 +5640,44 @@ function sliceNodeSource(source, node) {
|
|
|
5405
5640
|
const snippet = source.slice(node.start, node.end).trim();
|
|
5406
5641
|
return snippet.length ? snippet : null;
|
|
5407
5642
|
}
|
|
5408
|
-
function
|
|
5643
|
+
function getTypeAnnotationSource(source, node) {
|
|
5644
|
+
const rawTypeAnnotation = node.typeAnnotation;
|
|
5645
|
+
if (!rawTypeAnnotation || typeof rawTypeAnnotation !== "object" || !("type" in rawTypeAnnotation) || rawTypeAnnotation.type !== "TSTypeAnnotation" || !("typeAnnotation" in rawTypeAnnotation)) {
|
|
5646
|
+
return void 0;
|
|
5647
|
+
}
|
|
5648
|
+
const typeAnnotation = rawTypeAnnotation.typeAnnotation;
|
|
5649
|
+
return typeAnnotation && typeof typeAnnotation === "object" ? sliceNodeSource(source, typeAnnotation) ?? void 0 : void 0;
|
|
5650
|
+
}
|
|
5651
|
+
function getCustomPomParameterSpec(source, param) {
|
|
5409
5652
|
if (param.type === "Identifier") {
|
|
5410
|
-
return param.name
|
|
5653
|
+
return createPomParameterSpec(param.name, getTypeAnnotationSource(source, param), {
|
|
5654
|
+
hasQuestionToken: !!param.optional
|
|
5655
|
+
});
|
|
5411
5656
|
}
|
|
5412
5657
|
if (param.type === "AssignmentPattern") {
|
|
5413
|
-
|
|
5658
|
+
if (param.left.type !== "Identifier") {
|
|
5659
|
+
return null;
|
|
5660
|
+
}
|
|
5661
|
+
const initializer = sliceNodeSource(source, param.right);
|
|
5662
|
+
if (!initializer) {
|
|
5663
|
+
return null;
|
|
5664
|
+
}
|
|
5665
|
+
return createPomParameterSpec(param.left.name, getTypeAnnotationSource(source, param.left), {
|
|
5666
|
+
initializer,
|
|
5667
|
+
hasQuestionToken: !!param.left.optional
|
|
5668
|
+
});
|
|
5414
5669
|
}
|
|
5415
5670
|
if (param.type === "RestElement") {
|
|
5416
|
-
|
|
5671
|
+
if (param.argument.type !== "Identifier") {
|
|
5672
|
+
return null;
|
|
5673
|
+
}
|
|
5674
|
+
const typeExpression = getTypeAnnotationSource(
|
|
5675
|
+
source,
|
|
5676
|
+
param
|
|
5677
|
+
) ?? getTypeAnnotationSource(source, param.argument);
|
|
5678
|
+
return createPomParameterSpec(param.argument.name, typeExpression, {
|
|
5679
|
+
isRestParameter: true
|
|
5680
|
+
});
|
|
5417
5681
|
}
|
|
5418
5682
|
return null;
|
|
5419
5683
|
}
|
|
@@ -5446,29 +5710,23 @@ function extractCustomPomMethodSignatures(source, exportName) {
|
|
|
5446
5710
|
if (member.key.type !== "Identifier") {
|
|
5447
5711
|
continue;
|
|
5448
5712
|
}
|
|
5449
|
-
const
|
|
5450
|
-
const argNames = [];
|
|
5713
|
+
const parameters = [];
|
|
5451
5714
|
let supported = true;
|
|
5452
5715
|
member.params.forEach((param) => {
|
|
5453
5716
|
if (!supported) {
|
|
5454
5717
|
return;
|
|
5455
5718
|
}
|
|
5456
|
-
const
|
|
5457
|
-
|
|
5458
|
-
if (!paramSource || !argName) {
|
|
5719
|
+
const parameter = getCustomPomParameterSpec(source, param);
|
|
5720
|
+
if (!parameter) {
|
|
5459
5721
|
supported = false;
|
|
5460
5722
|
return;
|
|
5461
5723
|
}
|
|
5462
|
-
|
|
5463
|
-
argNames.push(argName);
|
|
5724
|
+
parameters.push(parameter);
|
|
5464
5725
|
});
|
|
5465
5726
|
if (!supported) {
|
|
5466
5727
|
continue;
|
|
5467
5728
|
}
|
|
5468
|
-
signatures.set(member.key.name,
|
|
5469
|
-
params: params.join(", "),
|
|
5470
|
-
argNames
|
|
5471
|
-
});
|
|
5729
|
+
signatures.set(member.key.name, createPomMethodSignature(parameters));
|
|
5472
5730
|
}
|
|
5473
5731
|
}
|
|
5474
5732
|
return signatures;
|
|
@@ -5651,6 +5909,9 @@ function resolveCustomPomImportResolution(generatedClassNames, projectRoot, opti
|
|
|
5651
5909
|
const customDirRelOrAbs = options.customPomDir ?? "tests/playwright/pom/custom";
|
|
5652
5910
|
const customDirAbs = path.isAbsolute(customDirRelOrAbs) ? customDirRelOrAbs : path.resolve(projectRoot, customDirRelOrAbs);
|
|
5653
5911
|
if (!fs.existsSync(customDirAbs)) {
|
|
5912
|
+
if (options.requireCustomPomDir) {
|
|
5913
|
+
throw createMissingCustomPomDirectoryError(customDirRelOrAbs, customDirAbs);
|
|
5914
|
+
}
|
|
5654
5915
|
return {
|
|
5655
5916
|
classIdentifierMap,
|
|
5656
5917
|
methodSignaturesByClass,
|
|
@@ -5693,6 +5954,14 @@ function resolveCustomPomImportResolution(generatedClassNames, projectRoot, opti
|
|
|
5693
5954
|
importSpecifiersByClass
|
|
5694
5955
|
};
|
|
5695
5956
|
}
|
|
5957
|
+
function assertCustomPomAttachmentsResolved(attachments, classIdentifierMap, configuredDir) {
|
|
5958
|
+
const missingClassNames = Array.from(new Set(
|
|
5959
|
+
attachments.map((attachment) => attachment.className).filter((className) => !Object.prototype.hasOwnProperty.call(classIdentifierMap, className))
|
|
5960
|
+
)).sort((left, right) => left.localeCompare(right));
|
|
5961
|
+
if (missingClassNames.length > 0) {
|
|
5962
|
+
throw createMissingCustomPomAttachmentClassError(missingClassNames, configuredDir);
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5696
5965
|
function getComposedStubBody(targetClassName, availableClassNames, depsByClassName, vueFilesPathMap, projectRoot) {
|
|
5697
5966
|
const filePath = resolveVueSourcePath(targetClassName, vueFilesPathMap, projectRoot);
|
|
5698
5967
|
if (!filePath)
|
|
@@ -5721,7 +5990,7 @@ function getComposedStubBody(targetClassName, availableClassNames, depsByClassNa
|
|
|
5721
5990
|
if (!sig)
|
|
5722
5991
|
continue;
|
|
5723
5992
|
const list = methodToChildren.get(name) ?? [];
|
|
5724
|
-
list.push({ child,
|
|
5993
|
+
list.push({ child, signature: sig });
|
|
5725
5994
|
methodToChildren.set(name, list);
|
|
5726
5995
|
}
|
|
5727
5996
|
}
|
|
@@ -5729,12 +5998,12 @@ function getComposedStubBody(targetClassName, availableClassNames, depsByClassNa
|
|
|
5729
5998
|
for (const [methodName, candidatesForMethod] of methodToChildren.entries()) {
|
|
5730
5999
|
if (candidatesForMethod.length !== 1 || methodName === "constructor")
|
|
5731
6000
|
continue;
|
|
5732
|
-
const { child,
|
|
5733
|
-
const callArgs =
|
|
6001
|
+
const { child, signature } = candidatesForMethod[0];
|
|
6002
|
+
const callArgs = getPomParameterArgumentNames(signature.parameters).join(", ");
|
|
5734
6003
|
passthroughMembers.push(createClassMethod({
|
|
5735
6004
|
name: methodName,
|
|
5736
6005
|
isAsync: true,
|
|
5737
|
-
parameters:
|
|
6006
|
+
parameters: toTypeScriptPomParameterStructures(signature.parameters),
|
|
5738
6007
|
statements: [
|
|
5739
6008
|
`return await this.${child}.${methodName}(${callArgs});`
|
|
5740
6009
|
]
|
|
@@ -5783,9 +6052,15 @@ async function generateAggregatedFiles(componentHierarchyMap, vueFilesPathMap, b
|
|
|
5783
6052
|
imports.push(`export * from "${runtimeClassGenRel}/base-page";`);
|
|
5784
6053
|
const customPomImportResolution = resolveCustomPomImportResolution(generatedClassNames, projectRoot, {
|
|
5785
6054
|
customPomDir: options.customPomDir,
|
|
6055
|
+
requireCustomPomDir: options.requireCustomPomDir,
|
|
5786
6056
|
customPomImportAliases: options.customPomImportAliases,
|
|
5787
6057
|
customPomImportNameCollisionBehavior: options.customPomImportNameCollisionBehavior
|
|
5788
6058
|
});
|
|
6059
|
+
assertCustomPomAttachmentsResolved(
|
|
6060
|
+
options.customPomAttachments ?? [],
|
|
6061
|
+
customPomImportResolution.classIdentifierMap,
|
|
6062
|
+
options.customPomDir ?? "tests/playwright/pom/custom"
|
|
6063
|
+
);
|
|
5789
6064
|
const customPomClassIdentifierMap = customPomImportResolution.classIdentifierMap;
|
|
5790
6065
|
const customPomMethodSignaturesByClass = customPomImportResolution.methodSignaturesByClass;
|
|
5791
6066
|
const customPomAvailableClassIdentifiers = customPomImportResolution.availableClassIdentifiers;
|
|
@@ -5935,8 +6210,8 @@ function getWidgetInstancesForView(componentName, dataTestIdSet, availableClassI
|
|
|
5935
6210
|
return candidate;
|
|
5936
6211
|
};
|
|
5937
6212
|
for (const dt of dataTestIdSet) {
|
|
5938
|
-
const raw = dt.
|
|
5939
|
-
if (
|
|
6213
|
+
const raw = dt.selectorValue.formatted;
|
|
6214
|
+
if (isParameterizedPomPattern(dt.selectorValue.patternKind)) {
|
|
5940
6215
|
continue;
|
|
5941
6216
|
}
|
|
5942
6217
|
const toggleSuffix = "-toggle";
|
|
@@ -6026,7 +6301,6 @@ function getConstructor(childrenComponent, componentHierarchyMap, attachmentsFor
|
|
|
6026
6301
|
});
|
|
6027
6302
|
}
|
|
6028
6303
|
const TESTID_CLICK_EVENT_NAME = "__testid_event__";
|
|
6029
|
-
const TESTID_CLICK_EVENT_STRICT_FLAG = "__testid_click_event_strict__";
|
|
6030
6304
|
const CLICK_EVENT_NAME = TESTID_CLICK_EVENT_NAME;
|
|
6031
6305
|
const inferredNativeWrapperConfigByLookup = /* @__PURE__ */ new Map();
|
|
6032
6306
|
const inferredSfcPathByLookup = /* @__PURE__ */ new Map();
|
|
@@ -6288,7 +6562,7 @@ function getConditionalDirectiveInfo(element) {
|
|
|
6288
6562
|
if (directive.name === "else") {
|
|
6289
6563
|
const exp2 = directive.exp;
|
|
6290
6564
|
if (exp2 && (exp2.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION || exp2.type === compilerCore.NodeTypes.COMPOUND_EXPRESSION)) {
|
|
6291
|
-
const source2 = (exp2
|
|
6565
|
+
const source2 = getVueExpressionSource(exp2, "content", "compiled");
|
|
6292
6566
|
return { kind: "else-if", source: source2 };
|
|
6293
6567
|
}
|
|
6294
6568
|
return { kind: "else", source: "" };
|
|
@@ -6297,13 +6571,13 @@ function getConditionalDirectiveInfo(element) {
|
|
|
6297
6571
|
const exp2 = directive.exp;
|
|
6298
6572
|
if (!exp2 || exp2.type !== compilerCore.NodeTypes.SIMPLE_EXPRESSION && exp2.type !== compilerCore.NodeTypes.COMPOUND_EXPRESSION)
|
|
6299
6573
|
return null;
|
|
6300
|
-
const source2 = (exp2
|
|
6574
|
+
const source2 = getVueExpressionSource(exp2, "content", "compiled");
|
|
6301
6575
|
return { kind: "else-if", source: source2 };
|
|
6302
6576
|
}
|
|
6303
6577
|
const exp = directive.exp;
|
|
6304
6578
|
if (!exp || exp.type !== compilerCore.NodeTypes.SIMPLE_EXPRESSION && exp.type !== compilerCore.NodeTypes.COMPOUND_EXPRESSION)
|
|
6305
6579
|
return null;
|
|
6306
|
-
const source = (exp
|
|
6580
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
6307
6581
|
return { kind: directive.name, source };
|
|
6308
6582
|
}
|
|
6309
6583
|
function tryExtractStableHintFromConditionalExpressionSource(source) {
|
|
@@ -6329,26 +6603,26 @@ function tryExtractStableHintFromConditionalExpressionSource(source) {
|
|
|
6329
6603
|
};
|
|
6330
6604
|
try {
|
|
6331
6605
|
const expr = parser.parseExpression(src, { plugins: ["typescript"] });
|
|
6332
|
-
const
|
|
6606
|
+
const isNodeType2 = (n, type) => {
|
|
6333
6607
|
return n !== null && n.type === type;
|
|
6334
6608
|
};
|
|
6335
|
-
const
|
|
6336
|
-
return
|
|
6609
|
+
const isStringLiteralNode2 = (n) => {
|
|
6610
|
+
return isNodeType2(n, "StringLiteral") && typeof n.value === "string";
|
|
6337
6611
|
};
|
|
6338
|
-
const
|
|
6339
|
-
return
|
|
6612
|
+
const isIdentifierNode2 = (n) => {
|
|
6613
|
+
return isNodeType2(n, "Identifier") && typeof n.name === "string";
|
|
6340
6614
|
};
|
|
6341
6615
|
const results = [];
|
|
6342
6616
|
const walk = (n) => {
|
|
6343
6617
|
if (!n)
|
|
6344
6618
|
return;
|
|
6345
|
-
if (
|
|
6619
|
+
if (isStringLiteralNode2(n)) {
|
|
6346
6620
|
const v = (n.value ?? "").trim();
|
|
6347
6621
|
if (isIdentifierish(v)) {
|
|
6348
6622
|
results.push(v);
|
|
6349
6623
|
}
|
|
6350
6624
|
}
|
|
6351
|
-
if (
|
|
6625
|
+
if (isIdentifierNode2(n)) {
|
|
6352
6626
|
const v = (n.name ?? "").trim();
|
|
6353
6627
|
if (isIdentifierish(v)) {
|
|
6354
6628
|
results.push(v);
|
|
@@ -6498,34 +6772,20 @@ ${buildSearchRootsKey(normalizedSearchRoots)}`;
|
|
|
6498
6772
|
inferredNativeWrapperConfigByLookup.set(cacheKey, { role: "" });
|
|
6499
6773
|
return null;
|
|
6500
6774
|
}
|
|
6501
|
-
function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
6775
|
+
function tryWrapClickDirectiveForTestEvents(element, testIdAttribute, resolvedRuntimeTestId) {
|
|
6502
6776
|
const jsStringLiteral = (value) => {
|
|
6503
6777
|
return JSON.stringify(value);
|
|
6504
6778
|
};
|
|
6505
6779
|
const getTestIdExpressionForNode = () => {
|
|
6506
|
-
|
|
6507
|
-
if (!existing) {
|
|
6508
|
-
return "undefined";
|
|
6509
|
-
}
|
|
6510
|
-
if (existing.type === compilerCore.NodeTypes.ATTRIBUTE) {
|
|
6511
|
-
const v = existing.value?.content;
|
|
6512
|
-
if (!v) {
|
|
6513
|
-
return "undefined";
|
|
6514
|
-
}
|
|
6515
|
-
return jsStringLiteral(v);
|
|
6516
|
-
}
|
|
6517
|
-
const directive = existing;
|
|
6518
|
-
const exp2 = directive.exp;
|
|
6519
|
-
if (!exp2 || exp2.type !== compilerCore.NodeTypes.SIMPLE_EXPRESSION) {
|
|
6780
|
+
if (!resolvedRuntimeTestId) {
|
|
6520
6781
|
return "undefined";
|
|
6521
6782
|
}
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
return "undefined";
|
|
6783
|
+
if (resolvedRuntimeTestId.kind === "static") {
|
|
6784
|
+
return jsStringLiteral(resolvedRuntimeTestId.value);
|
|
6525
6785
|
}
|
|
6526
|
-
return `(${
|
|
6786
|
+
return `(${renderTemplateLiteralExpression(resolvedRuntimeTestId)})`;
|
|
6527
6787
|
};
|
|
6528
|
-
const
|
|
6788
|
+
const testIdExpression = getTestIdExpressionForNode();
|
|
6529
6789
|
const clickDirective = tryGetClickDirective(element);
|
|
6530
6790
|
if (!clickDirective)
|
|
6531
6791
|
return;
|
|
@@ -6536,10 +6796,10 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6536
6796
|
const exp = clickDirective.exp;
|
|
6537
6797
|
if (!exp)
|
|
6538
6798
|
return;
|
|
6539
|
-
const existingSource = (exp
|
|
6799
|
+
const existingSource = getVueExpressionSource(exp, "loc", "content");
|
|
6540
6800
|
if (existingSource.includes(CLICK_EVENT_NAME))
|
|
6541
6801
|
return;
|
|
6542
|
-
const originalExpression = (exp
|
|
6802
|
+
const originalExpression = getVueExpressionSource(exp, "content", "loc");
|
|
6543
6803
|
if (!originalExpression)
|
|
6544
6804
|
return;
|
|
6545
6805
|
const isStatementBody = (() => {
|
|
@@ -6556,7 +6816,7 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6556
6816
|
const statementWrappedHandler = `($event) => {
|
|
6557
6817
|
const __win = ($event && $event.view) ? $event.view : undefined;
|
|
6558
6818
|
const __target = ($event && $event.currentTarget) ? $event.currentTarget : undefined;
|
|
6559
|
-
const __testIdFromNode = ${
|
|
6819
|
+
const __testIdFromNode = ${testIdExpression};
|
|
6560
6820
|
const __testIdFromTarget = (__target && typeof __target.getAttribute === 'function') ? __target.getAttribute(${jsStringLiteral(testIdAttribute)}) : undefined;
|
|
6561
6821
|
const __testId = (__testIdFromNode ?? __testIdFromTarget);
|
|
6562
6822
|
const __emit = (phase, err) => {
|
|
@@ -6567,16 +6827,12 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6567
6827
|
__w.dispatchEvent(new __CustomEvent('${CLICK_EVENT_NAME}', { detail: { testId: __testId, phase, err: err ? String(err) : undefined } }));
|
|
6568
6828
|
}
|
|
6569
6829
|
} catch (e) {
|
|
6570
|
-
// Instrumentation
|
|
6571
|
-
// In strict mode we rethrow so tests fail fast and the underlying problem is visible.
|
|
6572
|
-
// Outside strict mode we log and continue so we don't break real user clicks.
|
|
6830
|
+
// Instrumentation failures should never be silent. Log the root cause and fail fast.
|
|
6573
6831
|
const __w = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
6574
6832
|
if (__w && __w.console && typeof __w.console.error === 'function') {
|
|
6575
6833
|
__w.console.error('[testid-click-event] failed to emit ${CLICK_EVENT_NAME}', e);
|
|
6576
6834
|
}
|
|
6577
|
-
|
|
6578
|
-
throw e;
|
|
6579
|
-
}
|
|
6835
|
+
throw e;
|
|
6580
6836
|
}
|
|
6581
6837
|
};
|
|
6582
6838
|
const __w2 = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
@@ -6607,7 +6863,7 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6607
6863
|
const expressionWrappedHandler = `($event) => {
|
|
6608
6864
|
const __win = ($event && $event.view) ? $event.view : undefined;
|
|
6609
6865
|
const __target = ($event && $event.currentTarget) ? $event.currentTarget : undefined;
|
|
6610
|
-
const __testIdFromNode = ${
|
|
6866
|
+
const __testIdFromNode = ${testIdExpression};
|
|
6611
6867
|
const __testIdFromTarget = (__target && typeof __target.getAttribute === 'function') ? __target.getAttribute(${jsStringLiteral(testIdAttribute)}) : undefined;
|
|
6612
6868
|
const __testId = (__testIdFromNode ?? __testIdFromTarget);
|
|
6613
6869
|
const __emit = (phase, err) => {
|
|
@@ -6618,16 +6874,12 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6618
6874
|
__w.dispatchEvent(new __CustomEvent('${CLICK_EVENT_NAME}', { detail: { testId: __testId, phase, err: err ? String(err) : undefined } }));
|
|
6619
6875
|
}
|
|
6620
6876
|
} catch (e) {
|
|
6621
|
-
// Instrumentation
|
|
6622
|
-
// In strict mode we rethrow so tests fail fast and the underlying problem is visible.
|
|
6623
|
-
// Outside strict mode we log and continue so we don't break real user clicks.
|
|
6877
|
+
// Instrumentation failures should never be silent. Log the root cause and fail fast.
|
|
6624
6878
|
const __w = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
6625
6879
|
if (__w && __w.console && typeof __w.console.error === 'function') {
|
|
6626
6880
|
__w.console.error('[testid-click-event] failed to emit ${CLICK_EVENT_NAME}', e);
|
|
6627
6881
|
}
|
|
6628
|
-
|
|
6629
|
-
throw e;
|
|
6630
|
-
}
|
|
6882
|
+
throw e;
|
|
6631
6883
|
}
|
|
6632
6884
|
};
|
|
6633
6885
|
const __w2 = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
@@ -6665,10 +6917,9 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6665
6917
|
let previousFileName = "";
|
|
6666
6918
|
const hierarchyMap = /* @__PURE__ */ new Map();
|
|
6667
6919
|
function createTestIdTransform(componentName, componentHierarchyMap, nativeWrappers = {}, excludedComponents = [], viewsDirAbs, options = {}) {
|
|
6668
|
-
const existingIdBehavior = options.existingIdBehavior ?? "
|
|
6920
|
+
const existingIdBehavior = options.existingIdBehavior ?? "error";
|
|
6669
6921
|
const testIdAttribute = (options.testIdAttribute || "data-testid").trim() || "data-testid";
|
|
6670
|
-
const nameCollisionBehavior = options.nameCollisionBehavior ?? "
|
|
6671
|
-
const missingSemanticNameBehavior = options.missingSemanticNameBehavior ?? "error";
|
|
6922
|
+
const nameCollisionBehavior = options.nameCollisionBehavior ?? "error";
|
|
6672
6923
|
const warn = options.warn;
|
|
6673
6924
|
const vueFilesPathMap = options.vueFilesPathMap;
|
|
6674
6925
|
const wrapperSearchRoots = options.wrapperSearchRoots ?? [];
|
|
@@ -6744,7 +6995,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6744
6995
|
conditionalHintByIfBranch.set(branch, hint);
|
|
6745
6996
|
continue;
|
|
6746
6997
|
}
|
|
6747
|
-
const condSource = (cond
|
|
6998
|
+
const condSource = getVueExpressionSource(cond, "content", "compiled");
|
|
6748
6999
|
const stable = tryExtractStableHintFromConditionalExpressionSource(condSource);
|
|
6749
7000
|
if (stable) {
|
|
6750
7001
|
conditionalHintByIfBranch.set(branch, stable);
|
|
@@ -6806,17 +7057,18 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6806
7057
|
nativeWrappers[element.tag] = { role: "grid" };
|
|
6807
7058
|
}
|
|
6808
7059
|
}
|
|
6809
|
-
const
|
|
7060
|
+
const getBestAvailableKeyInfo = () => {
|
|
6810
7061
|
const parentNode = context.parent && typeof context.parent === "object" ? context.parent : null;
|
|
6811
7062
|
const isDirectVForChild = parentNode?.type === compilerCore.NodeTypes.FOR;
|
|
6812
|
-
const
|
|
6813
|
-
if (
|
|
6814
|
-
|
|
7063
|
+
const vForKeyInfo = (isDirectVForChild ? getKeyDirectiveInfo(element) : null) || getContainedInVForDirectiveKeyInfo(context, element, hierarchyMap);
|
|
7064
|
+
if (vForKeyInfo) {
|
|
7065
|
+
return vForKeyInfo;
|
|
7066
|
+
}
|
|
7067
|
+
return getContainedInSlotDataKeyInfo(element, hierarchyMap);
|
|
6815
7068
|
};
|
|
6816
|
-
const
|
|
6817
|
-
const
|
|
6818
|
-
const
|
|
6819
|
-
const bestKeyVariable = isSlotKey ? bestKeyInferred : null;
|
|
7069
|
+
const bestKeyInfo = getBestAvailableKeyInfo();
|
|
7070
|
+
const bestKeyPlaceholder = bestKeyInfo?.selectorFragment ?? null;
|
|
7071
|
+
const bestRuntimeKeyPlaceholder = bestKeyInfo?.runtimeFragment ?? null;
|
|
6820
7072
|
const keyValuesOverride = tryGetContainedInStaticVForSourceLiteralValues(context);
|
|
6821
7073
|
const parentKey = context?.parent ? context.parent : null;
|
|
6822
7074
|
const conditional = getConditionalDirectiveInfo(element);
|
|
@@ -6859,7 +7111,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6859
7111
|
if (!cond) {
|
|
6860
7112
|
conditionalHint = "else";
|
|
6861
7113
|
} else {
|
|
6862
|
-
const condSource = (cond
|
|
7114
|
+
const condSource = getVueExpressionSource(cond, "content", "compiled");
|
|
6863
7115
|
conditionalHint = tryExtractStableHintFromConditionalExpressionSource(condSource) ?? "if";
|
|
6864
7116
|
}
|
|
6865
7117
|
}
|
|
@@ -6869,7 +7121,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6869
7121
|
});
|
|
6870
7122
|
if (showDirective?.exp && (showDirective.exp.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION || showDirective.exp.type === compilerCore.NodeTypes.COMPOUND_EXPRESSION)) {
|
|
6871
7123
|
const exp = showDirective.exp;
|
|
6872
|
-
const source = (exp
|
|
7124
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
6873
7125
|
const showHint = tryExtractStableHintFromConditionalExpressionSource(source);
|
|
6874
7126
|
if (showHint) {
|
|
6875
7127
|
conditionalHint = conditionalHint ? `${conditionalHint} ${showHint}` : showHint;
|
|
@@ -6903,13 +7155,17 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6903
7155
|
const tagSuffix = getTagSuffix();
|
|
6904
7156
|
return bestKeyPlaceholder ? templateAttributeValue(`${componentName}-${bestKeyPlaceholder}${clickSuffix}${tagSuffix}`) : staticAttributeValue(`${componentName}${clickSuffix}${tagSuffix}`);
|
|
6905
7157
|
};
|
|
7158
|
+
const getClickRuntimeDataTestId = (clickSuffix) => {
|
|
7159
|
+
const tagSuffix = getTagSuffix();
|
|
7160
|
+
return bestRuntimeKeyPlaceholder ? templateAttributeValue(`${componentName}-${bestRuntimeKeyPlaceholder}${clickSuffix}${tagSuffix}`) : staticAttributeValue(`${componentName}${clickSuffix}${tagSuffix}`);
|
|
7161
|
+
};
|
|
6906
7162
|
const getSubmitDataTestId = (identifier) => {
|
|
6907
7163
|
const tagSuffix = getTagSuffix();
|
|
6908
7164
|
return `${componentName}-${identifier}${tagSuffix}`;
|
|
6909
7165
|
};
|
|
6910
7166
|
const applyResolvedDataTestIdForElement = (args) => {
|
|
6911
7167
|
const nativeRole = args.nativeRoleOverride ?? getNativeRoleFromTagSuffix();
|
|
6912
|
-
applyResolvedDataTestId({
|
|
7168
|
+
return applyResolvedDataTestId({
|
|
6913
7169
|
element,
|
|
6914
7170
|
componentName,
|
|
6915
7171
|
parentComponentName,
|
|
@@ -6919,8 +7175,8 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6919
7175
|
generatedMethodContentByComponent,
|
|
6920
7176
|
nativeRole,
|
|
6921
7177
|
preferredGeneratedValue: args.preferredGeneratedValue,
|
|
6922
|
-
|
|
6923
|
-
|
|
7178
|
+
preferredRuntimeValue: args.preferredRuntimeValue,
|
|
7179
|
+
keyInfo: bestKeyInfo,
|
|
6924
7180
|
keyValuesOverride,
|
|
6925
7181
|
entryOverrides: args.entryOverrides,
|
|
6926
7182
|
semanticNameHint: args.semanticNameHint,
|
|
@@ -6938,7 +7194,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6938
7194
|
return p.type === compilerCore.NodeTypes.DIRECTIVE && p.name === "bind" && p.arg?.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION && p.arg.content === "handler" && !!p.exp;
|
|
6939
7195
|
}) ?? null;
|
|
6940
7196
|
const handlerInfo = handlerDirective ? nodeHandlerAttributeInfo(element) : null;
|
|
6941
|
-
if (
|
|
7197
|
+
if (nativeWrappers[element.tag]?.role === "button" && handlerDirective && !handlerInfo) {
|
|
6942
7198
|
const loc = element.loc?.start;
|
|
6943
7199
|
const locationHint = loc ? `${loc.line}:${loc.column}` : "unknown";
|
|
6944
7200
|
const handlerSource = (handlerDirective.exp?.loc?.source ?? "").trim() || "<unknown>";
|
|
@@ -6947,7 +7203,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6947
7203
|
Element: <${element.tag}>
|
|
6948
7204
|
Handler: ${handlerSource}
|
|
6949
7205
|
|
|
6950
|
-
Fix: move complex inline logic into a named function (for example, const onAction = () => ...; then bind :handler="onAction"), or simplify the handler to a direct identifier/call the generator can name
|
|
7206
|
+
Fix: move complex inline logic into a named function (for example, const onAction = () => ...; then bind :handler="onAction"), or simplify the handler to a direct identifier/call the generator can name.`
|
|
6951
7207
|
);
|
|
6952
7208
|
}
|
|
6953
7209
|
if (nativeWrappersValue) {
|
|
@@ -7074,20 +7330,20 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
|
|
|
7074
7330
|
contextFilename: context.filename
|
|
7075
7331
|
});
|
|
7076
7332
|
const clickHint = trimLeadingSeparators(clickSuffix) || void 0;
|
|
7077
|
-
const idOrName =
|
|
7333
|
+
const idOrName = getStaticIdOrNameHint(element) || void 0;
|
|
7078
7334
|
const semanticHintCandidates = [clickHint, idOrName, innerText, conditionalHint].map((value) => (value ?? "").trim()).filter(Boolean).filter((value, index, values) => values.indexOf(value) === index);
|
|
7079
7335
|
const [semanticNameHint2, ...semanticNameHintAlternates] = semanticHintCandidates;
|
|
7080
7336
|
const pomMergeKey = clickHint ? `click:hint:${clickHint}` : void 0;
|
|
7081
7337
|
const testId = getClickDataTestId(clickSuffix);
|
|
7082
|
-
|
|
7338
|
+
const runtimeTestId = getClickRuntimeDataTestId(clickSuffix);
|
|
7339
|
+
const resolvedDataTestId = applyResolvedDataTestIdForElement({
|
|
7083
7340
|
preferredGeneratedValue: testId,
|
|
7341
|
+
preferredRuntimeValue: runtimeTestId,
|
|
7084
7342
|
semanticNameHint: semanticNameHint2,
|
|
7085
7343
|
semanticNameHintAlternates,
|
|
7086
7344
|
pomMergeKey
|
|
7087
7345
|
});
|
|
7088
|
-
|
|
7089
|
-
tryWrapClickDirectiveForTestEvents(element, testIdAttribute);
|
|
7090
|
-
}
|
|
7346
|
+
tryWrapClickDirectiveForTestEvents(element, testIdAttribute, resolvedDataTestId.runtimeValue);
|
|
7091
7347
|
return;
|
|
7092
7348
|
}
|
|
7093
7349
|
const existingElementDataTestId = tryGetExistingElementDataTestId(element, testIdAttribute);
|
|
@@ -7097,7 +7353,7 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
|
|
|
7097
7353
|
if (!isRecognizedInteractiveRole) {
|
|
7098
7354
|
return;
|
|
7099
7355
|
}
|
|
7100
|
-
const identifierHint =
|
|
7356
|
+
const identifierHint = getStaticIdOrNameHint(element) || nodeHandlerAttributeValue(element) || innerText || existingElementDataTestId.value || conditionalHint || void 0;
|
|
7101
7357
|
const preferredGeneratedValue = existingElementDataTestId.isDynamic ? templateAttributeValue(existingElementDataTestId.template) : staticAttributeValue(existingElementDataTestId.value);
|
|
7102
7358
|
applyResolvedDataTestIdForElement({
|
|
7103
7359
|
preferredGeneratedValue,
|
|
@@ -7107,7 +7363,7 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
|
|
|
7107
7363
|
}
|
|
7108
7364
|
const isSubmit = element.props.find((p) => p.type === compilerCore.NodeTypes.ATTRIBUTE && p.name === "type")?.value?.content === "submit";
|
|
7109
7365
|
if (isSubmit) {
|
|
7110
|
-
const identifier =
|
|
7366
|
+
const identifier = getStaticIdOrNameHint(element) || innerText;
|
|
7111
7367
|
if (!identifier) {
|
|
7112
7368
|
const loc = element.loc?.start;
|
|
7113
7369
|
const locationHint = loc ? `${loc.line}:${loc.column}` : "unknown";
|
|
@@ -7159,29 +7415,32 @@ function createBuildProcessorPlugin(options) {
|
|
|
7159
7415
|
getSourceDirs,
|
|
7160
7416
|
basePageClassPath,
|
|
7161
7417
|
normalizedBasePagePath,
|
|
7418
|
+
generation,
|
|
7419
|
+
projectRootRef,
|
|
7420
|
+
nativeWrappers,
|
|
7421
|
+
excludedComponents,
|
|
7422
|
+
getWrapperSearchRoots,
|
|
7423
|
+
getResolvedRouterEntry,
|
|
7424
|
+
loggerRef
|
|
7425
|
+
} = options;
|
|
7426
|
+
const {
|
|
7162
7427
|
outDir,
|
|
7163
7428
|
emitLanguages,
|
|
7164
7429
|
typescriptOutputStructure,
|
|
7165
7430
|
csharp,
|
|
7166
7431
|
generateFixtures,
|
|
7167
7432
|
customPomAttachments,
|
|
7168
|
-
projectRootRef,
|
|
7169
7433
|
customPomDir,
|
|
7434
|
+
requireCustomPomDir,
|
|
7170
7435
|
customPomImportAliases,
|
|
7171
7436
|
customPomImportNameCollisionBehavior,
|
|
7172
7437
|
testIdAttribute,
|
|
7173
7438
|
nameCollisionBehavior,
|
|
7174
|
-
missingSemanticNameBehavior = "error",
|
|
7175
7439
|
existingIdBehavior,
|
|
7176
|
-
nativeWrappers,
|
|
7177
|
-
excludedComponents,
|
|
7178
|
-
getWrapperSearchRoots,
|
|
7179
7440
|
routerAwarePoms,
|
|
7180
|
-
getResolvedRouterEntry,
|
|
7181
7441
|
routerType,
|
|
7182
|
-
routerModuleShims
|
|
7183
|
-
|
|
7184
|
-
} = options;
|
|
7442
|
+
routerModuleShims
|
|
7443
|
+
} = generation;
|
|
7185
7444
|
let lastGeneratedMetrics = {
|
|
7186
7445
|
entryCount: 0,
|
|
7187
7446
|
interactiveComponentCount: 0,
|
|
@@ -7280,10 +7539,9 @@ function createBuildProcessorPlugin(options) {
|
|
|
7280
7539
|
excludedComponents,
|
|
7281
7540
|
getViewsDirAbs(),
|
|
7282
7541
|
{
|
|
7283
|
-
existingIdBehavior: existingIdBehavior ?? "
|
|
7542
|
+
existingIdBehavior: existingIdBehavior ?? "error",
|
|
7284
7543
|
testIdAttribute,
|
|
7285
7544
|
nameCollisionBehavior,
|
|
7286
|
-
missingSemanticNameBehavior,
|
|
7287
7545
|
warn: (message) => loggerRef.current.warn(message),
|
|
7288
7546
|
vueFilesPathMap,
|
|
7289
7547
|
wrapperSearchRoots: getWrapperSearchRoots()
|
|
@@ -7383,6 +7641,7 @@ function createBuildProcessorPlugin(options) {
|
|
|
7383
7641
|
customPomAttachments,
|
|
7384
7642
|
projectRoot: projectRootRef.current,
|
|
7385
7643
|
customPomDir,
|
|
7644
|
+
requireCustomPomDir,
|
|
7386
7645
|
customPomImportAliases,
|
|
7387
7646
|
customPomImportNameCollisionBehavior,
|
|
7388
7647
|
testIdAttribute,
|
|
@@ -7414,6 +7673,11 @@ function createDevProcessorPlugin(options) {
|
|
|
7414
7673
|
projectRootRef,
|
|
7415
7674
|
normalizedBasePagePath,
|
|
7416
7675
|
basePageClassPath,
|
|
7676
|
+
generation,
|
|
7677
|
+
getResolvedRouterEntry,
|
|
7678
|
+
loggerRef
|
|
7679
|
+
} = options;
|
|
7680
|
+
const {
|
|
7417
7681
|
outDir,
|
|
7418
7682
|
emitLanguages,
|
|
7419
7683
|
typescriptOutputStructure,
|
|
@@ -7421,18 +7685,16 @@ function createDevProcessorPlugin(options) {
|
|
|
7421
7685
|
generateFixtures,
|
|
7422
7686
|
customPomAttachments,
|
|
7423
7687
|
customPomDir,
|
|
7688
|
+
requireCustomPomDir,
|
|
7424
7689
|
customPomImportAliases,
|
|
7425
7690
|
customPomImportNameCollisionBehavior,
|
|
7426
|
-
nameCollisionBehavior
|
|
7427
|
-
missingSemanticNameBehavior = "error",
|
|
7691
|
+
nameCollisionBehavior,
|
|
7428
7692
|
existingIdBehavior,
|
|
7429
7693
|
testIdAttribute,
|
|
7430
7694
|
routerAwarePoms,
|
|
7431
|
-
getResolvedRouterEntry,
|
|
7432
7695
|
routerType,
|
|
7433
|
-
routerModuleShims
|
|
7434
|
-
|
|
7435
|
-
} = options;
|
|
7696
|
+
routerModuleShims
|
|
7697
|
+
} = generation;
|
|
7436
7698
|
let scheduleVueFileRegen = null;
|
|
7437
7699
|
const getProjectRootCandidates = () => Array.from(/* @__PURE__ */ new Set([
|
|
7438
7700
|
path.resolve(projectRootRef.current),
|
|
@@ -7619,9 +7881,8 @@ function createDevProcessorPlugin(options) {
|
|
|
7619
7881
|
excludedComponents,
|
|
7620
7882
|
getViewsDirAbs(),
|
|
7621
7883
|
{
|
|
7622
|
-
existingIdBehavior: existingIdBehavior ?? "
|
|
7884
|
+
existingIdBehavior: existingIdBehavior ?? "error",
|
|
7623
7885
|
nameCollisionBehavior,
|
|
7624
|
-
missingSemanticNameBehavior,
|
|
7625
7886
|
testIdAttribute,
|
|
7626
7887
|
warn: (message) => loggerRef.current.warn(message),
|
|
7627
7888
|
vueFilesPathMap: provisionalVuePathMap,
|
|
@@ -7672,6 +7933,7 @@ function createDevProcessorPlugin(options) {
|
|
|
7672
7933
|
customPomAttachments,
|
|
7673
7934
|
projectRoot: projectRootRef.current,
|
|
7674
7935
|
customPomDir,
|
|
7936
|
+
requireCustomPomDir,
|
|
7675
7937
|
customPomImportAliases,
|
|
7676
7938
|
customPomImportNameCollisionBehavior,
|
|
7677
7939
|
pageDirs: getPageDirs(),
|
|
@@ -7907,27 +8169,30 @@ function createSupportPlugins(options) {
|
|
|
7907
8169
|
getViewsDir,
|
|
7908
8170
|
getSourceDirs,
|
|
7909
8171
|
getWrapperSearchRoots,
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
8172
|
+
generation,
|
|
8173
|
+
projectRootRef,
|
|
8174
|
+
basePageClassPath: basePageClassPathOverride,
|
|
8175
|
+
loggerRef
|
|
8176
|
+
} = options;
|
|
8177
|
+
const {
|
|
7913
8178
|
outDir,
|
|
7914
8179
|
emitLanguages,
|
|
7915
8180
|
typescriptOutputStructure,
|
|
7916
8181
|
csharp,
|
|
7917
|
-
routerAwarePoms,
|
|
7918
|
-
routerEntry,
|
|
7919
|
-
routerType,
|
|
7920
|
-
routerModuleShims,
|
|
7921
8182
|
generateFixtures,
|
|
7922
8183
|
customPomAttachments,
|
|
7923
|
-
projectRootRef,
|
|
7924
|
-
basePageClassPath: basePageClassPathOverride,
|
|
7925
8184
|
customPomDir,
|
|
8185
|
+
requireCustomPomDir,
|
|
7926
8186
|
customPomImportAliases,
|
|
7927
8187
|
customPomImportNameCollisionBehavior,
|
|
8188
|
+
nameCollisionBehavior,
|
|
8189
|
+
existingIdBehavior,
|
|
7928
8190
|
testIdAttribute,
|
|
7929
|
-
|
|
7930
|
-
|
|
8191
|
+
routerAwarePoms,
|
|
8192
|
+
routerEntry,
|
|
8193
|
+
routerType,
|
|
8194
|
+
routerModuleShims
|
|
8195
|
+
} = generation;
|
|
7931
8196
|
const resolveRouterEntry2 = () => {
|
|
7932
8197
|
if (!routerAwarePoms)
|
|
7933
8198
|
return void 0;
|
|
@@ -7956,27 +8221,12 @@ function createSupportPlugins(options) {
|
|
|
7956
8221
|
getSourceDirs,
|
|
7957
8222
|
basePageClassPath,
|
|
7958
8223
|
normalizedBasePagePath,
|
|
7959
|
-
|
|
7960
|
-
emitLanguages,
|
|
7961
|
-
typescriptOutputStructure,
|
|
7962
|
-
csharp,
|
|
7963
|
-
generateFixtures,
|
|
7964
|
-
customPomAttachments,
|
|
8224
|
+
generation,
|
|
7965
8225
|
projectRootRef,
|
|
7966
|
-
customPomDir,
|
|
7967
|
-
customPomImportAliases,
|
|
7968
|
-
customPomImportNameCollisionBehavior,
|
|
7969
|
-
testIdAttribute,
|
|
7970
|
-
nameCollisionBehavior,
|
|
7971
|
-
missingSemanticNameBehavior,
|
|
7972
|
-
existingIdBehavior,
|
|
7973
8226
|
nativeWrappers,
|
|
7974
8227
|
excludedComponents,
|
|
7975
8228
|
getWrapperSearchRoots,
|
|
7976
|
-
routerAwarePoms,
|
|
7977
|
-
routerType,
|
|
7978
8229
|
getResolvedRouterEntry: resolveRouterEntry2,
|
|
7979
|
-
routerModuleShims,
|
|
7980
8230
|
loggerRef
|
|
7981
8231
|
});
|
|
7982
8232
|
const devProcessor = createDevProcessorPlugin({
|
|
@@ -7991,23 +8241,8 @@ function createSupportPlugins(options) {
|
|
|
7991
8241
|
projectRootRef,
|
|
7992
8242
|
normalizedBasePagePath,
|
|
7993
8243
|
basePageClassPath,
|
|
7994
|
-
|
|
7995
|
-
emitLanguages,
|
|
7996
|
-
typescriptOutputStructure,
|
|
7997
|
-
csharp,
|
|
7998
|
-
generateFixtures,
|
|
7999
|
-
customPomAttachments,
|
|
8000
|
-
customPomDir,
|
|
8001
|
-
customPomImportAliases,
|
|
8002
|
-
customPomImportNameCollisionBehavior,
|
|
8003
|
-
nameCollisionBehavior,
|
|
8004
|
-
missingSemanticNameBehavior,
|
|
8005
|
-
existingIdBehavior,
|
|
8006
|
-
testIdAttribute,
|
|
8007
|
-
routerAwarePoms,
|
|
8008
|
-
routerType,
|
|
8244
|
+
generation,
|
|
8009
8245
|
getResolvedRouterEntry: resolveRouterEntry2,
|
|
8010
|
-
routerModuleShims,
|
|
8011
8246
|
loggerRef
|
|
8012
8247
|
});
|
|
8013
8248
|
const virtualModules = createTestIdsVirtualModulesPlugin(componentTestIds);
|
|
@@ -8344,8 +8579,7 @@ function createVuePluginWithTestIds(options) {
|
|
|
8344
8579
|
});
|
|
8345
8580
|
const api = viteVuePlugin?.api;
|
|
8346
8581
|
if (!api) {
|
|
8347
|
-
|
|
8348
|
-
return;
|
|
8582
|
+
throw new Error("[vue-pom-generator] Nuxt bridge could not find vite:vue plugin to patch.");
|
|
8349
8583
|
}
|
|
8350
8584
|
const currentOptions = api.options ?? {};
|
|
8351
8585
|
const currentTemplate = currentOptions.template ?? {};
|
|
@@ -8411,33 +8645,6 @@ function assertOneOf(value, allowed, name) {
|
|
|
8411
8645
|
}
|
|
8412
8646
|
throw new TypeError(`${name} must be one of: ${allowed.join(", ")}.`);
|
|
8413
8647
|
}
|
|
8414
|
-
function assertErrorBehavior(value, name) {
|
|
8415
|
-
if (!value) {
|
|
8416
|
-
return;
|
|
8417
|
-
}
|
|
8418
|
-
if (value === "ignore" || value === "error") {
|
|
8419
|
-
return;
|
|
8420
|
-
}
|
|
8421
|
-
if (typeof value !== "object" || Array.isArray(value)) {
|
|
8422
|
-
throw new TypeError(`${name} must be "ignore", "error", or an object.`);
|
|
8423
|
-
}
|
|
8424
|
-
const supportedKeys = /* @__PURE__ */ new Set(["missingSemanticNameBehavior"]);
|
|
8425
|
-
for (const key of Object.keys(value)) {
|
|
8426
|
-
if (!supportedKeys.has(key)) {
|
|
8427
|
-
throw new TypeError(`${name} contains unsupported key "${key}".`);
|
|
8428
|
-
}
|
|
8429
|
-
}
|
|
8430
|
-
assertOneOf(value.missingSemanticNameBehavior, ["ignore", "error"], `${name}.missingSemanticNameBehavior`);
|
|
8431
|
-
}
|
|
8432
|
-
function resolveMissingSemanticNameBehavior(value) {
|
|
8433
|
-
if (!value) {
|
|
8434
|
-
return "error";
|
|
8435
|
-
}
|
|
8436
|
-
if (value === "ignore" || value === "error") {
|
|
8437
|
-
return value;
|
|
8438
|
-
}
|
|
8439
|
-
return value.missingSemanticNameBehavior ?? "error";
|
|
8440
|
-
}
|
|
8441
8648
|
function readPackageJson(projectRoot) {
|
|
8442
8649
|
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
8443
8650
|
if (!fs.existsSync(packageJsonPath)) {
|
|
@@ -8546,7 +8753,7 @@ function applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompile
|
|
|
8546
8753
|
'[vue-pom-generator] vuePluginOwnership="external" requires the resolved Vite Vue plugin, but none was found. Add vue() to your Vite plugins before spreading createVuePomGeneratorPlugins(...).'
|
|
8547
8754
|
);
|
|
8548
8755
|
}
|
|
8549
|
-
throw new Error("[vue-pom-generator] Nuxt
|
|
8756
|
+
throw new Error("[vue-pom-generator] Nuxt bridge could not find vite:vue plugin to patch.");
|
|
8550
8757
|
}
|
|
8551
8758
|
const currentOptions = viteVuePlugin.api.options ?? {};
|
|
8552
8759
|
const currentTemplate = currentOptions.template ?? {};
|
|
@@ -8603,18 +8810,23 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8603
8810
|
const vueGenerationOptions = generationOptions;
|
|
8604
8811
|
const verbosity = options.logging?.verbosity ?? "warn";
|
|
8605
8812
|
const vueOptions = options.vueOptions;
|
|
8606
|
-
const
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8813
|
+
const resolvedInjectionOptionsRef = {
|
|
8814
|
+
current: resolveInjectionSupportOptions({
|
|
8815
|
+
isNuxt,
|
|
8816
|
+
viewsDir: injection.viewsDir,
|
|
8817
|
+
componentDirs: injection.componentDirs,
|
|
8818
|
+
layoutDirs: injection.layoutDirs,
|
|
8819
|
+
wrapperSearchRoots: injection.wrapperSearchRoots,
|
|
8820
|
+
nativeWrappers: injection.nativeWrappers,
|
|
8821
|
+
excludedComponents: injection.excludeComponents,
|
|
8822
|
+
existingIdBehavior: injection.existingIdBehavior,
|
|
8823
|
+
testIdAttribute: injection.attribute
|
|
8824
|
+
})
|
|
8825
|
+
};
|
|
8826
|
+
const resolvedInjectionOptions = resolvedInjectionOptionsRef.current;
|
|
8827
|
+
const nativeWrappers = resolvedInjectionOptions.nativeWrappers;
|
|
8828
|
+
const excludedComponents = resolvedInjectionOptions.excludedComponents;
|
|
8829
|
+
const testIdAttribute = resolvedInjectionOptions.testIdAttribute;
|
|
8618
8830
|
const routerEntry = !isNuxt ? vueGenerationOptions?.router?.entry : void 0;
|
|
8619
8831
|
const routerType = isNuxt ? "nuxt" : vueGenerationOptions?.router?.type ?? "vue-router";
|
|
8620
8832
|
const routerModuleShims = !isNuxt ? vueGenerationOptions?.router?.moduleShims : void 0;
|
|
@@ -8623,27 +8835,41 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8623
8835
|
}
|
|
8624
8836
|
const vuePluginOwnership = isNuxt ? "external" : options.vuePluginOwnership ?? "internal";
|
|
8625
8837
|
const usesExternalVuePlugin = vuePluginOwnership === "external";
|
|
8626
|
-
const csharp = generationOptions?.csharp;
|
|
8627
|
-
const errorBehavior = options.errorBehavior;
|
|
8628
|
-
const missingSemanticNameBehavior = resolveMissingSemanticNameBehavior(errorBehavior);
|
|
8629
|
-
const typescriptOutputStructure = generationOptions?.playwright?.outputStructure ?? "aggregated";
|
|
8630
8838
|
const generateFixtures = generationOptions?.playwright?.fixtures;
|
|
8631
8839
|
const customPoms = generationOptions?.playwright?.customPoms;
|
|
8632
8840
|
const resolvedCustomPomAttachments = customPoms?.attachments ?? [];
|
|
8633
|
-
const resolvedCustomPomDir = customPoms?.dir ?? "tests/playwright/pom/custom";
|
|
8634
8841
|
const resolvedCustomPomImportAliases = customPoms?.importAliases;
|
|
8635
|
-
const
|
|
8842
|
+
const requireCustomPomDir = customPoms?.dir !== void 0 || resolvedCustomPomAttachments.length > 0 || Object.keys(resolvedCustomPomImportAliases ?? {}).length > 0;
|
|
8843
|
+
const resolvedGenerationOptions = resolveGenerationSupportOptions({
|
|
8844
|
+
outDir: generationOptions?.outDir,
|
|
8845
|
+
emitLanguages: generationOptions?.emit,
|
|
8846
|
+
typescriptOutputStructure: generationOptions?.playwright?.outputStructure,
|
|
8847
|
+
csharp: generationOptions?.csharp,
|
|
8848
|
+
generateFixtures,
|
|
8849
|
+
customPomAttachments: resolvedCustomPomAttachments,
|
|
8850
|
+
customPomDir: customPoms?.dir,
|
|
8851
|
+
requireCustomPomDir,
|
|
8852
|
+
customPomImportAliases: resolvedCustomPomImportAliases,
|
|
8853
|
+
customPomImportNameCollisionBehavior: customPoms?.importNameCollisionBehavior,
|
|
8854
|
+
nameCollisionBehavior: generationOptions?.nameCollisionBehavior,
|
|
8855
|
+
existingIdBehavior: resolvedInjectionOptions.existingIdBehavior,
|
|
8856
|
+
testIdAttribute,
|
|
8857
|
+
routerAwarePoms: typeof routerEntry === "string" && routerEntry.trim().length > 0 || routerType === "nuxt",
|
|
8858
|
+
routerEntry,
|
|
8859
|
+
routerType,
|
|
8860
|
+
routerModuleShims
|
|
8861
|
+
});
|
|
8636
8862
|
const basePageClassPathOverride = generationOptions?.basePageClassPath;
|
|
8637
|
-
const getPageDirs = () =>
|
|
8863
|
+
const getPageDirs = () => resolvedInjectionOptionsRef.current.pageDirs;
|
|
8638
8864
|
const getViewsDir = () => getPageDirs()[0] ?? "src/views";
|
|
8639
|
-
const getComponentDirs = () =>
|
|
8640
|
-
const getLayoutDirs = () =>
|
|
8865
|
+
const getComponentDirs = () => resolvedInjectionOptionsRef.current.componentDirs;
|
|
8866
|
+
const getLayoutDirs = () => resolvedInjectionOptionsRef.current.layoutDirs;
|
|
8641
8867
|
const getSourceDirs = () => Array.from(/* @__PURE__ */ new Set([
|
|
8642
8868
|
...getPageDirs(),
|
|
8643
8869
|
...getComponentDirs(),
|
|
8644
8870
|
...getLayoutDirs()
|
|
8645
8871
|
]));
|
|
8646
|
-
const getWrapperSearchRoots = () =>
|
|
8872
|
+
const getWrapperSearchRoots = () => resolvedInjectionOptionsRef.current.wrapperSearchRoots;
|
|
8647
8873
|
const sharedStateKey = JSON.stringify({
|
|
8648
8874
|
cwd: process.cwd(),
|
|
8649
8875
|
mode: isNuxt ? "nuxt" : "vue",
|
|
@@ -8651,9 +8877,9 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8651
8877
|
componentDirs: isNuxt ? null : getComponentDirs(),
|
|
8652
8878
|
layoutDirs: isNuxt ? null : getLayoutDirs(),
|
|
8653
8879
|
wrapperSearchRoots: isNuxt ? null : getWrapperSearchRoots(),
|
|
8654
|
-
outDir,
|
|
8880
|
+
outDir: resolvedGenerationOptions.outDir,
|
|
8655
8881
|
testIdAttribute,
|
|
8656
|
-
routerType,
|
|
8882
|
+
routerType: resolvedGenerationOptions.routerType,
|
|
8657
8883
|
vuePluginOwnership
|
|
8658
8884
|
});
|
|
8659
8885
|
const sharedState = getSharedGeneratorState(sharedStateKey);
|
|
@@ -8674,23 +8900,22 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8674
8900
|
if (isNuxt) {
|
|
8675
8901
|
const nuxtDiscovery = await loadNuxtProjectDiscovery(process.cwd());
|
|
8676
8902
|
projectRootRef.current = nuxtDiscovery.rootDir;
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
8903
|
+
resolvedInjectionOptionsRef.current = applyNuxtDiscoveryToInjectionOptions(
|
|
8904
|
+
resolvedInjectionOptionsRef.current,
|
|
8905
|
+
nuxtDiscovery
|
|
8906
|
+
);
|
|
8681
8907
|
}
|
|
8682
8908
|
assertNonEmptyString(testIdAttribute, "[vue-pom-generator] injection.attribute");
|
|
8683
8909
|
assertNonEmptyString(getViewsDir(), "[vue-pom-generator] injection.viewsDir");
|
|
8684
8910
|
assertNonEmptyStringArray(getComponentDirs(), "[vue-pom-generator] injection.componentDirs");
|
|
8685
8911
|
assertNonEmptyStringArray(getLayoutDirs(), "[vue-pom-generator] injection.layoutDirs");
|
|
8686
8912
|
assertNonEmptyStringArray(getWrapperSearchRoots(), "[vue-pom-generator] injection.wrapperSearchRoots");
|
|
8687
|
-
assertErrorBehavior(errorBehavior, "[vue-pom-generator] errorBehavior");
|
|
8688
8913
|
if (generationEnabled) {
|
|
8689
|
-
assertNonEmptyString(outDir, "[vue-pom-generator] generation.outDir");
|
|
8690
|
-
assertOneOf(typescriptOutputStructure, ["aggregated", "split"], "[vue-pom-generator] generation.playwright.outputStructure");
|
|
8691
|
-
assertRouterModuleShims(routerModuleShims, "[vue-pom-generator] generation.router.moduleShims");
|
|
8692
|
-
if (!isNuxt && vueGenerationOptions?.router && routerType === "vue-router") {
|
|
8693
|
-
assertNonEmptyString(routerEntry, "[vue-pom-generator] generation.router.entry");
|
|
8914
|
+
assertNonEmptyString(resolvedGenerationOptions.outDir, "[vue-pom-generator] generation.outDir");
|
|
8915
|
+
assertOneOf(resolvedGenerationOptions.typescriptOutputStructure, ["aggregated", "split"], "[vue-pom-generator] generation.playwright.outputStructure");
|
|
8916
|
+
assertRouterModuleShims(resolvedGenerationOptions.routerModuleShims, "[vue-pom-generator] generation.router.moduleShims");
|
|
8917
|
+
if (!isNuxt && vueGenerationOptions?.router && resolvedGenerationOptions.routerType === "vue-router") {
|
|
8918
|
+
assertNonEmptyString(resolvedGenerationOptions.routerEntry, "[vue-pom-generator] generation.router.entry");
|
|
8694
8919
|
}
|
|
8695
8920
|
}
|
|
8696
8921
|
if (usesExternalVuePlugin) {
|
|
@@ -8712,8 +8937,8 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8712
8937
|
const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
|
|
8713
8938
|
const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
|
|
8714
8939
|
vueOptions,
|
|
8715
|
-
existingIdBehavior,
|
|
8716
|
-
nameCollisionBehavior,
|
|
8940
|
+
existingIdBehavior: resolvedGenerationOptions.existingIdBehavior,
|
|
8941
|
+
nameCollisionBehavior: resolvedGenerationOptions.nameCollisionBehavior,
|
|
8717
8942
|
nativeWrappers,
|
|
8718
8943
|
elementMetadata,
|
|
8719
8944
|
semanticNameMap,
|
|
@@ -8728,7 +8953,6 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8728
8953
|
getProjectRoot: () => projectRootRef.current
|
|
8729
8954
|
});
|
|
8730
8955
|
templateCompilerOptionsForResolvedPlugin = templateCompilerOptions;
|
|
8731
|
-
const routerAwarePoms = typeof routerEntry === "string" && routerEntry.trim().length > 0 || routerType === "nuxt";
|
|
8732
8956
|
const supportPlugins = createSupportPlugins({
|
|
8733
8957
|
componentTestIds,
|
|
8734
8958
|
componentHierarchyMap,
|
|
@@ -8741,26 +8965,10 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8741
8965
|
getViewsDir,
|
|
8742
8966
|
getSourceDirs,
|
|
8743
8967
|
getWrapperSearchRoots: getWrapperSearchRootsAbs,
|
|
8744
|
-
|
|
8745
|
-
missingSemanticNameBehavior,
|
|
8746
|
-
existingIdBehavior,
|
|
8747
|
-
outDir,
|
|
8748
|
-
emitLanguages,
|
|
8749
|
-
typescriptOutputStructure,
|
|
8750
|
-
csharp,
|
|
8751
|
-
routerAwarePoms,
|
|
8752
|
-
routerEntry,
|
|
8753
|
-
generateFixtures,
|
|
8968
|
+
generation: resolvedGenerationOptions,
|
|
8754
8969
|
projectRootRef,
|
|
8755
8970
|
basePageClassPath: basePageClassPathOverride,
|
|
8756
|
-
|
|
8757
|
-
customPomDir: resolvedCustomPomDir,
|
|
8758
|
-
customPomImportAliases: resolvedCustomPomImportAliases,
|
|
8759
|
-
customPomImportNameCollisionBehavior: resolvedCustomPomImportCollisionBehavior,
|
|
8760
|
-
testIdAttribute,
|
|
8761
|
-
loggerRef,
|
|
8762
|
-
routerType,
|
|
8763
|
-
routerModuleShims
|
|
8971
|
+
loggerRef
|
|
8764
8972
|
});
|
|
8765
8973
|
if (isNuxt) {
|
|
8766
8974
|
loggerRef.current.info("Nuxt environment detected. Skipping internal @vitejs/plugin-vue to avoid conflicts.");
|