@immense/vue-pom-generator 1.0.57 → 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 +7 -19
- package/RELEASE_NOTES.md +76 -13
- 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 +1283 -1019
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1285 -1021
- 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/playwright.config.d.ts.map +1 -1
- package/dist/plugin/create-vue-pom-generator-plugins.d.ts.map +1 -1
- package/dist/plugin/nuxt-discovery.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
|
@@ -104,6 +104,21 @@ function createLogger(options) {
|
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
const requireFromModule = node_module.createRequire(typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href);
|
|
107
|
+
function resolveNuxtKitEntry(cwd) {
|
|
108
|
+
const attemptResolvers = [
|
|
109
|
+
node_module.createRequire(path.resolve(cwd, "package.json")),
|
|
110
|
+
requireFromModule
|
|
111
|
+
];
|
|
112
|
+
let lastError;
|
|
113
|
+
for (const resolver of attemptResolvers) {
|
|
114
|
+
try {
|
|
115
|
+
return resolver.resolve("@nuxt/kit");
|
|
116
|
+
} catch (error) {
|
|
117
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
throw lastError ?? new Error("Unknown module resolution error");
|
|
121
|
+
}
|
|
107
122
|
function toUniqueResolvedPaths(paths) {
|
|
108
123
|
return Array.from(new Set(paths.map((value) => path.resolve(value))));
|
|
109
124
|
}
|
|
@@ -214,7 +229,7 @@ async function loadNuxtProjectDiscovery(cwd = process.cwd()) {
|
|
|
214
229
|
let loadNuxtConfig;
|
|
215
230
|
let getLayerDirectories;
|
|
216
231
|
try {
|
|
217
|
-
const nuxtKitEntry =
|
|
232
|
+
const nuxtKitEntry = resolveNuxtKitEntry(cwd);
|
|
218
233
|
({ loadNuxtConfig, getLayerDirectories } = await import(node_url.pathToFileURL(nuxtKitEntry).href));
|
|
219
234
|
} catch (error) {
|
|
220
235
|
throw new TypeError(
|
|
@@ -230,6 +245,49 @@ async function loadNuxtProjectDiscovery(cwd = process.cwd()) {
|
|
|
230
245
|
const nuxtOptions = await loadNuxtConfig({ cwd });
|
|
231
246
|
return resolveNuxtProjectDiscovery(nuxtOptions, getLayerDirectories, cwd);
|
|
232
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
|
+
}
|
|
233
291
|
function createTypeScriptProject() {
|
|
234
292
|
return new tsMorph.Project({
|
|
235
293
|
useInMemoryFileSystem: true,
|
|
@@ -321,16 +379,7 @@ function createClassConstructor(constructorDeclaration) {
|
|
|
321
379
|
...constructorDeclaration
|
|
322
380
|
};
|
|
323
381
|
}
|
|
324
|
-
function
|
|
325
|
-
if (!value) {
|
|
326
|
-
return value;
|
|
327
|
-
}
|
|
328
|
-
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
329
|
-
}
|
|
330
|
-
function hasParam(params, name) {
|
|
331
|
-
return Object.prototype.hasOwnProperty.call(params, name);
|
|
332
|
-
}
|
|
333
|
-
function splitTypeAndInitializer(typeExpression) {
|
|
382
|
+
function splitPomParameterTypeExpression(typeExpression) {
|
|
334
383
|
const trimmed = typeExpression.trim();
|
|
335
384
|
const initializerIndex = trimmed.lastIndexOf("=");
|
|
336
385
|
if (initializerIndex < 0) {
|
|
@@ -341,49 +390,237 @@ function splitTypeAndInitializer(typeExpression) {
|
|
|
341
390
|
initializer: trimmed.slice(initializerIndex + 1).trim()
|
|
342
391
|
};
|
|
343
392
|
}
|
|
344
|
-
function
|
|
345
|
-
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 };
|
|
346
396
|
return {
|
|
347
397
|
name,
|
|
348
|
-
|
|
349
|
-
|
|
398
|
+
typeExpression: normalizedTypeExpression,
|
|
399
|
+
type,
|
|
400
|
+
initializer: options.initializer ?? initializer,
|
|
401
|
+
hasQuestionToken: options.hasQuestionToken,
|
|
402
|
+
isRestParameter: options.isRestParameter
|
|
350
403
|
};
|
|
351
404
|
}
|
|
352
|
-
function
|
|
353
|
-
|
|
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
|
+
}));
|
|
354
414
|
}
|
|
355
|
-
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) {
|
|
356
451
|
return {
|
|
357
|
-
|
|
358
|
-
type: options.type,
|
|
359
|
-
initializer: options.initializer
|
|
452
|
+
parameters: normalizePomParameters(parameters)
|
|
360
453
|
};
|
|
361
454
|
}
|
|
362
|
-
function
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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;
|
|
366
463
|
}
|
|
367
|
-
return
|
|
464
|
+
return leftParams.every((param, index) => pomParameterSpecEquals(param, rightParams[index]));
|
|
465
|
+
}
|
|
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) {
|
|
473
|
+
const out = [];
|
|
474
|
+
const seen = /* @__PURE__ */ new Set();
|
|
475
|
+
const matches = formatted.matchAll(/\$\{(\w+)\}/g);
|
|
476
|
+
for (const match of matches) {
|
|
477
|
+
const variableName = match[1];
|
|
478
|
+
if (seen.has(variableName)) {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
seen.add(variableName);
|
|
482
|
+
out.push(variableName);
|
|
483
|
+
}
|
|
484
|
+
return out;
|
|
368
485
|
}
|
|
369
|
-
function
|
|
486
|
+
function createPomStringPattern(formatted, patternKind) {
|
|
487
|
+
return {
|
|
488
|
+
formatted,
|
|
489
|
+
patternKind,
|
|
490
|
+
templateVariables: getTemplateVariables(formatted)
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function getPomPatternVariables(patterns, options = {}) {
|
|
370
494
|
const out = [];
|
|
371
495
|
const seen = /* @__PURE__ */ new Set();
|
|
372
|
-
|
|
373
|
-
for (const
|
|
374
|
-
|
|
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);
|
|
375
518
|
continue;
|
|
376
519
|
}
|
|
377
|
-
|
|
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)) {
|
|
378
531
|
continue;
|
|
379
532
|
}
|
|
380
|
-
seen.add(
|
|
381
|
-
|
|
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);
|
|
382
599
|
}
|
|
383
600
|
return out;
|
|
384
601
|
}
|
|
385
|
-
function
|
|
386
|
-
|
|
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);
|
|
387
624
|
}
|
|
388
625
|
function createAsyncMethod(name, parameters, statements) {
|
|
389
626
|
return createClassMethod({
|
|
@@ -393,17 +630,27 @@ function createAsyncMethod(name, parameters, statements) {
|
|
|
393
630
|
statements
|
|
394
631
|
});
|
|
395
632
|
}
|
|
396
|
-
function generateClickMethod(methodName,
|
|
633
|
+
function generateClickMethod(methodName, selector, alternateSelectors, parameters) {
|
|
397
634
|
const name = `click${methodName}`;
|
|
398
635
|
const noWaitName = `${name}NoWait`;
|
|
399
|
-
const
|
|
400
|
-
const
|
|
401
|
-
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);
|
|
402
642
|
if (alternates.length > 0) {
|
|
403
|
-
const candidatesExpr = [
|
|
643
|
+
const candidatesExpr = [primaryTestIdExpr, ...alternates.map((id) => toTypeScriptPomPatternExpression(id))].join(", ");
|
|
404
644
|
const clickMethod = createAsyncMethod(
|
|
405
645
|
name,
|
|
406
|
-
|
|
646
|
+
hasSelectorVariables ? [
|
|
647
|
+
...baseParameters,
|
|
648
|
+
createInlineParameter("wait", { type: "boolean", initializer: "true" }),
|
|
649
|
+
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
650
|
+
] : [
|
|
651
|
+
createInlineParameter("wait", { type: "boolean", initializer: "true" }),
|
|
652
|
+
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
653
|
+
],
|
|
407
654
|
(writer) => {
|
|
408
655
|
writer.writeLine(`const candidates = [${candidatesExpr}] as const;`);
|
|
409
656
|
writer.writeLine("let lastError: unknown;");
|
|
@@ -411,7 +658,7 @@ function generateClickMethod(methodName, formattedDataTestId, alternateFormatted
|
|
|
411
658
|
writer.writeLine("const locator = this.locatorByTestId(testId);");
|
|
412
659
|
writer.write("try ").block(() => {
|
|
413
660
|
writer.write("if (await locator.count()) ").block(() => {
|
|
414
|
-
writer.writeLine(
|
|
661
|
+
writer.writeLine("await this.clickLocator(locator, annotationText, wait);");
|
|
415
662
|
writer.writeLine("return;");
|
|
416
663
|
});
|
|
417
664
|
});
|
|
@@ -422,60 +669,77 @@ function generateClickMethod(methodName, formattedDataTestId, alternateFormatted
|
|
|
422
669
|
writer.writeLine(`throw (lastError instanceof Error) ? lastError : new Error("[pom] Failed to click any candidate locator for ${name}.");`);
|
|
423
670
|
}
|
|
424
671
|
);
|
|
425
|
-
const noWaitArgs = argsForForward ? `${argsForForward}, false` : "false";
|
|
672
|
+
const noWaitArgs = argsForForward ? `${argsForForward}, false, annotationText` : "false, annotationText";
|
|
426
673
|
const noWaitMethod = createAsyncMethod(
|
|
427
674
|
noWaitName,
|
|
428
|
-
|
|
675
|
+
hasSelectorVariables ? [...baseParameters, createInlineParameter("annotationText", { type: "string", initializer: '""' })] : [createInlineParameter("annotationText", { type: "string", initializer: '""' })],
|
|
429
676
|
(writer) => {
|
|
430
677
|
writer.writeLine(`await this.${name}(${noWaitArgs});`);
|
|
431
678
|
}
|
|
432
679
|
);
|
|
433
680
|
return [clickMethod, noWaitMethod];
|
|
434
681
|
}
|
|
435
|
-
if (
|
|
682
|
+
if (hasSelectorVariables) {
|
|
436
683
|
return [
|
|
437
|
-
createAsyncMethod(
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
684
|
+
createAsyncMethod(
|
|
685
|
+
name,
|
|
686
|
+
[
|
|
687
|
+
...baseParameters,
|
|
688
|
+
createInlineParameter("wait", { type: "boolean", initializer: "true" }),
|
|
689
|
+
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
690
|
+
],
|
|
691
|
+
(writer) => {
|
|
692
|
+
writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
|
|
693
|
+
}
|
|
694
|
+
),
|
|
695
|
+
createAsyncMethod(
|
|
696
|
+
noWaitName,
|
|
697
|
+
[...baseParameters, createInlineParameter("annotationText", { type: "string", initializer: '""' })],
|
|
698
|
+
(writer) => {
|
|
699
|
+
writer.writeLine(`await this.${name}(${argsForForward}, false, annotationText);`);
|
|
700
|
+
}
|
|
701
|
+
)
|
|
443
702
|
];
|
|
444
703
|
}
|
|
445
704
|
return [
|
|
446
|
-
createAsyncMethod(
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
705
|
+
createAsyncMethod(
|
|
706
|
+
name,
|
|
707
|
+
[
|
|
708
|
+
createInlineParameter("wait", { type: "boolean", initializer: "true" }),
|
|
709
|
+
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
710
|
+
],
|
|
711
|
+
(writer) => {
|
|
712
|
+
writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
|
|
713
|
+
}
|
|
714
|
+
),
|
|
715
|
+
createAsyncMethod(
|
|
716
|
+
noWaitName,
|
|
717
|
+
[createInlineParameter("annotationText", { type: "string", initializer: '""' })],
|
|
718
|
+
(writer) => {
|
|
719
|
+
writer.writeLine(`await this.${name}(false, annotationText);`);
|
|
720
|
+
}
|
|
721
|
+
)
|
|
452
722
|
];
|
|
453
723
|
}
|
|
454
|
-
function generateRadioMethod(methodName,
|
|
724
|
+
function generateRadioMethod(methodName, selector, parameters) {
|
|
455
725
|
const name = `select${methodName}`;
|
|
456
|
-
const
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
460
|
-
] : [createInlineParameter("annotationText", { type: "string", initializer: '""' })];
|
|
461
|
-
const testIdExpr = hasKey ? `\`${formattedDataTestId}\`` : `"${formattedDataTestId}"`;
|
|
726
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
727
|
+
const methodParameters = createParameters(selectorParams);
|
|
728
|
+
const testIdExpr = toTypeScriptPomPatternExpression(selector);
|
|
462
729
|
return [
|
|
463
|
-
createAsyncMethod(name,
|
|
730
|
+
createAsyncMethod(name, methodParameters, (writer) => {
|
|
464
731
|
writer.writeLine(`await this.clickByTestId(${testIdExpr}, annotationText);`);
|
|
465
732
|
})
|
|
466
733
|
];
|
|
467
734
|
}
|
|
468
|
-
function generateSelectMethod(methodName,
|
|
735
|
+
function generateSelectMethod(methodName, selector, parameters) {
|
|
469
736
|
const name = `select${methodName}`;
|
|
470
|
-
const
|
|
471
|
-
const selectorExpr =
|
|
737
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
738
|
+
const selectorExpr = `this.selectorForTestId(${toTypeScriptPomPatternExpression(selector)})`;
|
|
472
739
|
return [
|
|
473
740
|
createAsyncMethod(
|
|
474
741
|
name,
|
|
475
|
-
|
|
476
|
-
createInlineParameter("value", { type: "string" }),
|
|
477
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
478
|
-
],
|
|
742
|
+
createParameters(selectorParams),
|
|
479
743
|
(writer) => {
|
|
480
744
|
writer.writeLine(`const selector = ${selectorExpr};`);
|
|
481
745
|
writer.writeLine("await this.animateCursorToElement(selector, false, 500, annotationText);");
|
|
@@ -484,33 +748,28 @@ function generateSelectMethod(methodName, formattedDataTestId) {
|
|
|
484
748
|
)
|
|
485
749
|
];
|
|
486
750
|
}
|
|
487
|
-
function generateVSelectMethod(methodName,
|
|
751
|
+
function generateVSelectMethod(methodName, selector, parameters) {
|
|
488
752
|
const name = `select${methodName}`;
|
|
753
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
489
754
|
return [
|
|
490
755
|
createAsyncMethod(
|
|
491
756
|
name,
|
|
492
|
-
|
|
493
|
-
createInlineParameter("value", { type: "string" }),
|
|
494
|
-
createInlineParameter("timeOut", { type: "number", initializer: "500" }),
|
|
495
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
496
|
-
],
|
|
757
|
+
createParameters(selectorParams),
|
|
497
758
|
(writer) => {
|
|
498
|
-
writer.writeLine(`await this.selectVSelectByTestId(
|
|
759
|
+
writer.writeLine(`await this.selectVSelectByTestId(${toTypeScriptPomPatternExpression(selector)}, value, timeOut, annotationText);`);
|
|
499
760
|
}
|
|
500
761
|
)
|
|
501
762
|
];
|
|
502
763
|
}
|
|
503
|
-
function generateTypeMethod(methodName,
|
|
764
|
+
function generateTypeMethod(methodName, selector, parameters) {
|
|
504
765
|
const name = `type${methodName}`;
|
|
766
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
505
767
|
return [
|
|
506
768
|
createAsyncMethod(
|
|
507
769
|
name,
|
|
508
|
-
|
|
509
|
-
createInlineParameter("text", { type: "string" }),
|
|
510
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
511
|
-
],
|
|
770
|
+
createParameters(selectorParams),
|
|
512
771
|
(writer) => {
|
|
513
|
-
writer.writeLine(`await this.fillInputByTestId(
|
|
772
|
+
writer.writeLine(`await this.fillInputByTestId(${toTypeScriptPomPatternExpression(selector)}, text, annotationText);`);
|
|
514
773
|
}
|
|
515
774
|
)
|
|
516
775
|
];
|
|
@@ -525,30 +784,31 @@ function isAllDigits(value) {
|
|
|
525
784
|
}
|
|
526
785
|
return true;
|
|
527
786
|
}
|
|
528
|
-
function generateGetElementByDataTestId(methodName, nativeRole,
|
|
787
|
+
function generateGetElementByDataTestId(methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
|
|
529
788
|
const roleSuffix = upperFirst$1(nativeRole || "Element");
|
|
530
789
|
const baseName = upperFirst$1(methodName);
|
|
531
790
|
const numericSuffix = baseName.startsWith(roleSuffix) ? baseName.slice(roleSuffix.length) : "";
|
|
532
791
|
const hasRoleSuffix = baseName.endsWith(roleSuffix) || baseName.startsWith(roleSuffix) && isAllDigits(numericSuffix);
|
|
533
792
|
const propertyName = hasRoleSuffix ? `${baseName}` : `${baseName}${roleSuffix}`;
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
793
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
794
|
+
const indexedVariable = getIndexedPomPatternVariable(selector);
|
|
795
|
+
if (indexedVariable) {
|
|
796
|
+
const keyType = getPomParameter(selectorParams, indexedVariable)?.typeExpression || "string";
|
|
537
797
|
const keyedPropertyName = getterNameOverride ?? removeByKeySegment(propertyName);
|
|
538
798
|
return [
|
|
539
799
|
createClassGetter({
|
|
540
800
|
name: keyedPropertyName,
|
|
541
801
|
statements: [
|
|
542
|
-
`return this.keyedLocators((
|
|
802
|
+
`return this.keyedLocators((${indexedVariable}: ${keyType}) => this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}));`
|
|
543
803
|
]
|
|
544
804
|
})
|
|
545
805
|
];
|
|
546
806
|
}
|
|
547
807
|
const finalPropertyName = getterNameOverride ?? propertyName;
|
|
548
|
-
const alternates =
|
|
808
|
+
const alternates = uniquePomStringPatterns(selector, alternateSelectors).slice(1);
|
|
549
809
|
if (alternates.length > 0) {
|
|
550
|
-
const all = [
|
|
551
|
-
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})`);
|
|
552
812
|
return [
|
|
553
813
|
createClassGetter({
|
|
554
814
|
name: finalPropertyName,
|
|
@@ -559,21 +819,22 @@ function generateGetElementByDataTestId(methodName, nativeRole, formattedDataTes
|
|
|
559
819
|
return [
|
|
560
820
|
createClassGetter({
|
|
561
821
|
name: finalPropertyName,
|
|
562
|
-
statements: [`return this.locatorByTestId(
|
|
822
|
+
statements: [`return this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)});`]
|
|
563
823
|
})
|
|
564
824
|
];
|
|
565
825
|
}
|
|
566
826
|
function generateNavigationMethod(args) {
|
|
567
|
-
const { targetPageObjectModelClass: target, baseMethodName,
|
|
827
|
+
const { targetPageObjectModelClass: target, baseMethodName, selector, alternateSelectors, parameters } = args;
|
|
568
828
|
const methodName = baseMethodName ? `goTo${upperFirst$1(baseMethodName)}` : `goTo${target.endsWith("Page") ? target.slice(0, -"Page".length) : target}`;
|
|
569
|
-
const
|
|
570
|
-
const
|
|
571
|
-
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(", ");
|
|
572
833
|
if (alternates.length > 0) {
|
|
573
834
|
return [
|
|
574
835
|
createClassMethod({
|
|
575
836
|
name: methodName,
|
|
576
|
-
parameters,
|
|
837
|
+
parameters: methodParameters,
|
|
577
838
|
returnType: `Fluent<${target}>`,
|
|
578
839
|
statements: (writer) => {
|
|
579
840
|
writer.write("return this.fluent(async () => ").block(() => {
|
|
@@ -601,11 +862,11 @@ function generateNavigationMethod(args) {
|
|
|
601
862
|
return [
|
|
602
863
|
createClassMethod({
|
|
603
864
|
name: methodName,
|
|
604
|
-
parameters,
|
|
865
|
+
parameters: methodParameters,
|
|
605
866
|
returnType: `Fluent<${target}>`,
|
|
606
867
|
statements: (writer) => {
|
|
607
868
|
writer.write("return this.fluent(async () => ").block(() => {
|
|
608
|
-
writer.writeLine(`await this.clickByTestId(
|
|
869
|
+
writer.writeLine(`await this.clickByTestId(${toTypeScriptPomPatternExpression(selector)});`);
|
|
609
870
|
writer.writeLine(`return new ${target}(this.page);`);
|
|
610
871
|
});
|
|
611
872
|
writer.writeLine(");");
|
|
@@ -613,15 +874,15 @@ function generateNavigationMethod(args) {
|
|
|
613
874
|
})
|
|
614
875
|
];
|
|
615
876
|
}
|
|
616
|
-
function generateViewObjectModelMembers(targetPageObjectModelClass, methodName, nativeRole,
|
|
877
|
+
function generateViewObjectModelMembers(targetPageObjectModelClass, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
|
|
617
878
|
const baseMethodName = nativeRole === "radio" ? methodName || "Radio" : methodName;
|
|
618
879
|
const members = generateGetElementByDataTestId(
|
|
619
880
|
baseMethodName,
|
|
620
881
|
nativeRole,
|
|
621
|
-
|
|
622
|
-
|
|
882
|
+
selector,
|
|
883
|
+
alternateSelectors,
|
|
623
884
|
getterNameOverride,
|
|
624
|
-
|
|
885
|
+
parameters
|
|
625
886
|
);
|
|
626
887
|
if (targetPageObjectModelClass) {
|
|
627
888
|
return [
|
|
@@ -629,25 +890,25 @@ function generateViewObjectModelMembers(targetPageObjectModelClass, methodName,
|
|
|
629
890
|
...generateNavigationMethod({
|
|
630
891
|
targetPageObjectModelClass,
|
|
631
892
|
baseMethodName,
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
893
|
+
selector,
|
|
894
|
+
alternateSelectors,
|
|
895
|
+
parameters
|
|
635
896
|
})
|
|
636
897
|
];
|
|
637
898
|
}
|
|
638
899
|
if (nativeRole === "select") {
|
|
639
|
-
return [...members, ...generateSelectMethod(baseMethodName,
|
|
900
|
+
return [...members, ...generateSelectMethod(baseMethodName, selector, parameters)];
|
|
640
901
|
}
|
|
641
902
|
if (nativeRole === "vselect") {
|
|
642
|
-
return [...members, ...generateVSelectMethod(baseMethodName,
|
|
903
|
+
return [...members, ...generateVSelectMethod(baseMethodName, selector, parameters)];
|
|
643
904
|
}
|
|
644
905
|
if (nativeRole === "input") {
|
|
645
|
-
return [...members, ...generateTypeMethod(baseMethodName,
|
|
906
|
+
return [...members, ...generateTypeMethod(baseMethodName, selector, parameters)];
|
|
646
907
|
}
|
|
647
908
|
if (nativeRole === "radio") {
|
|
648
|
-
return [...members, ...generateRadioMethod(baseMethodName || "Radio",
|
|
909
|
+
return [...members, ...generateRadioMethod(baseMethodName || "Radio", selector, parameters)];
|
|
649
910
|
}
|
|
650
|
-
return [...members, ...generateClickMethod(baseMethodName,
|
|
911
|
+
return [...members, ...generateClickMethod(baseMethodName, selector, alternateSelectors, parameters)];
|
|
651
912
|
}
|
|
652
913
|
function isSimpleExpressionNode(value) {
|
|
653
914
|
return value !== null && "type" in value && value.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION;
|
|
@@ -687,43 +948,72 @@ function buildPlaceholderParams(keys) {
|
|
|
687
948
|
params[k] = "__placeholder__";
|
|
688
949
|
return params;
|
|
689
950
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
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();
|
|
695
991
|
let expr;
|
|
696
992
|
try {
|
|
697
993
|
expr = parser.parseExpression(rawSource, { plugins: ["typescript"] });
|
|
698
|
-
} catch {
|
|
699
|
-
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
|
+
};
|
|
700
1001
|
}
|
|
701
|
-
const isNodeType = (node, type) => {
|
|
702
|
-
return node !== null && node.type === type;
|
|
703
|
-
};
|
|
704
|
-
const isStringLiteralNode = (node) => {
|
|
705
|
-
return isNodeType(node, "StringLiteral") && typeof node.value === "string";
|
|
706
|
-
};
|
|
707
|
-
const isIdentifierNode = (node) => {
|
|
708
|
-
return isNodeType(node, "Identifier") && typeof node.name === "string";
|
|
709
|
-
};
|
|
710
|
-
const isObjectPropertyNode = (node) => {
|
|
711
|
-
if (!isNodeType(node, "ObjectProperty"))
|
|
712
|
-
return false;
|
|
713
|
-
const n = node;
|
|
714
|
-
return typeof n.key === "object" && n.key !== null && typeof n.value === "object" && n.value !== null;
|
|
715
|
-
};
|
|
716
|
-
const isObjectExpressionNode = (node) => {
|
|
717
|
-
if (!isNodeType(node, "ObjectExpression"))
|
|
718
|
-
return false;
|
|
719
|
-
const n = node;
|
|
720
|
-
return Array.isArray(n.properties);
|
|
721
|
-
};
|
|
722
1002
|
if (isStringLiteralNode(expr)) {
|
|
723
|
-
return
|
|
1003
|
+
return {
|
|
1004
|
+
kind: "resolved",
|
|
1005
|
+
rawSource,
|
|
1006
|
+
target: expr.value,
|
|
1007
|
+
routeNameKey: null,
|
|
1008
|
+
paramKeys: []
|
|
1009
|
+
};
|
|
724
1010
|
}
|
|
725
1011
|
if (!isObjectExpressionNode(expr)) {
|
|
726
|
-
return
|
|
1012
|
+
return {
|
|
1013
|
+
kind: "unsupported",
|
|
1014
|
+
rawSource,
|
|
1015
|
+
reason: "dynamic-expression"
|
|
1016
|
+
};
|
|
727
1017
|
}
|
|
728
1018
|
const getStringField = (fieldName) => {
|
|
729
1019
|
const prop = expr.properties.find((p) => {
|
|
@@ -744,7 +1034,7 @@ function getRouteLocationLikeFromToDirective(toDirective) {
|
|
|
744
1034
|
const key = p.key;
|
|
745
1035
|
return isIdentifierNode(key) && key.name === "params" || isStringLiteralNode(key) && key.value === "params";
|
|
746
1036
|
});
|
|
747
|
-
let
|
|
1037
|
+
let paramKeys = [];
|
|
748
1038
|
if (paramsProp && isObjectPropertyNode(paramsProp) && isObjectExpressionNode(paramsProp.value)) {
|
|
749
1039
|
const keys = [];
|
|
750
1040
|
for (const prop of paramsProp.value.properties) {
|
|
@@ -756,47 +1046,50 @@ function getRouteLocationLikeFromToDirective(toDirective) {
|
|
|
756
1046
|
else if (isStringLiteralNode(key))
|
|
757
1047
|
keys.push(key.value);
|
|
758
1048
|
}
|
|
759
|
-
|
|
760
|
-
params = buildPlaceholderParams(Array.from(new Set(keys)));
|
|
761
|
-
}
|
|
1049
|
+
paramKeys = Array.from(new Set(keys));
|
|
762
1050
|
}
|
|
763
1051
|
if (name) {
|
|
764
|
-
|
|
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
|
+
};
|
|
765
1067
|
}
|
|
766
1068
|
if (path2) {
|
|
767
|
-
return {
|
|
1069
|
+
return {
|
|
1070
|
+
kind: "resolved",
|
|
1071
|
+
rawSource,
|
|
1072
|
+
target: { path: path2 },
|
|
1073
|
+
routeNameKey: null,
|
|
1074
|
+
paramKeys
|
|
1075
|
+
};
|
|
768
1076
|
}
|
|
769
|
-
return
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
return null;
|
|
775
|
-
const name = to.name;
|
|
776
|
-
if (typeof name !== "string")
|
|
777
|
-
return null;
|
|
778
|
-
const trimmed = name.trim();
|
|
779
|
-
if (!trimmed.length)
|
|
780
|
-
return null;
|
|
781
|
-
return toPascalCaseRouteKey(trimmed);
|
|
782
|
-
}
|
|
783
|
-
function getRouteNameKeyFromToDirective(toDirective) {
|
|
784
|
-
const objectName = toDirectiveObjectFieldNameValue$1(toDirective);
|
|
785
|
-
if (objectName)
|
|
786
|
-
return objectName;
|
|
787
|
-
return null;
|
|
1077
|
+
return {
|
|
1078
|
+
kind: "unsupported",
|
|
1079
|
+
rawSource,
|
|
1080
|
+
reason: "missing-name-or-path"
|
|
1081
|
+
};
|
|
788
1082
|
}
|
|
789
1083
|
function tryResolveToDirectiveTargetComponentName(toDirective) {
|
|
790
|
-
const
|
|
791
|
-
if (
|
|
792
|
-
const resolved = resolveToComponentName(
|
|
1084
|
+
const analysis = analyzeToDirectiveTarget(toDirective);
|
|
1085
|
+
if (analysis.kind === "resolved" && resolveToComponentName) {
|
|
1086
|
+
const resolved = resolveToComponentName(materializeResolvedRouteTarget(analysis.target, analysis.paramKeys));
|
|
793
1087
|
if (resolved)
|
|
794
1088
|
return resolved;
|
|
795
1089
|
}
|
|
796
|
-
|
|
797
|
-
if (!key || !routeNameToComponentName)
|
|
1090
|
+
if (analysis.kind !== "resolved" || !analysis.routeNameKey || !routeNameToComponentName)
|
|
798
1091
|
return null;
|
|
799
|
-
return routeNameToComponentName.get(
|
|
1092
|
+
return routeNameToComponentName.get(analysis.routeNameKey) ?? null;
|
|
800
1093
|
}
|
|
801
1094
|
function getDataTestIdFromGroupOption(text) {
|
|
802
1095
|
return text.replace(/[-_]/g, " ").split(" ").filter((a) => a).map((str) => {
|
|
@@ -831,11 +1124,81 @@ function staticAttributeValue(value) {
|
|
|
831
1124
|
return { kind: "static", value };
|
|
832
1125
|
}
|
|
833
1126
|
function templateAttributeValue(template) {
|
|
834
|
-
|
|
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 };
|
|
835
1132
|
}
|
|
836
1133
|
function getAttributeValueText(value) {
|
|
837
1134
|
return value.kind === "static" ? value.value : value.template;
|
|
838
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
|
+
}
|
|
839
1202
|
function toPascalCase(str) {
|
|
840
1203
|
const cleaned = (str ?? "").replace(/\$\{[^}]*\}/g, " ").replace(/[^a-z0-9]+/gi, " ").trim();
|
|
841
1204
|
if (!cleaned) {
|
|
@@ -877,32 +1240,14 @@ function tryGetClickDirective(node) {
|
|
|
877
1240
|
function nodeHasClickDirective(node) {
|
|
878
1241
|
return tryGetClickDirective(node) !== void 0;
|
|
879
1242
|
}
|
|
880
|
-
function
|
|
1243
|
+
function findTemplateSlotScopeExpression(node) {
|
|
881
1244
|
if (node.tag !== "template") {
|
|
882
1245
|
return null;
|
|
883
1246
|
}
|
|
884
1247
|
const slotProp = node.props.find((prop) => {
|
|
885
1248
|
return prop.type === compilerCore.NodeTypes.DIRECTIVE && prop.name === "slot";
|
|
886
1249
|
});
|
|
887
|
-
|
|
888
|
-
if (slotProp.exp.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION) {
|
|
889
|
-
return slotProp.exp.content;
|
|
890
|
-
}
|
|
891
|
-
if (slotProp.exp.type === compilerCore.NodeTypes.COMPOUND_EXPRESSION) {
|
|
892
|
-
return compilerCore.stringifyExpression(slotProp.exp);
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
return null;
|
|
896
|
-
}
|
|
897
|
-
function isSimpleScopeIdentifier(value) {
|
|
898
|
-
if (!value) {
|
|
899
|
-
return false;
|
|
900
|
-
}
|
|
901
|
-
try {
|
|
902
|
-
return types.isIdentifier(parser.parseExpression(value, { plugins: ["typescript"] }));
|
|
903
|
-
} catch {
|
|
904
|
-
return false;
|
|
905
|
-
}
|
|
1250
|
+
return slotProp?.exp && (slotProp.exp.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION || slotProp.exp.type === compilerCore.NodeTypes.COMPOUND_EXPRESSION) ? slotProp.exp : null;
|
|
906
1251
|
}
|
|
907
1252
|
function buildSlotScopeFallbackKeyExpression(identifier) {
|
|
908
1253
|
return `${identifier}.key ?? ${identifier}.data?.id ?? ${identifier}.id ?? ${identifier}.value ?? ${identifier}`;
|
|
@@ -1004,43 +1349,64 @@ function tryGetSlotScopeKeyCandidate(node) {
|
|
|
1004
1349
|
}
|
|
1005
1350
|
return best;
|
|
1006
1351
|
}
|
|
1007
|
-
function
|
|
1008
|
-
const
|
|
1009
|
-
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) {
|
|
1010
1362
|
return null;
|
|
1011
1363
|
}
|
|
1012
|
-
|
|
1013
|
-
return
|
|
1364
|
+
try {
|
|
1365
|
+
return parser.parseExpression(rawSource, { plugins: ["typescript"] });
|
|
1366
|
+
} catch {
|
|
1014
1367
|
}
|
|
1015
1368
|
try {
|
|
1016
|
-
const parsed = parser.parse(`(${
|
|
1369
|
+
const parsed = parser.parse(`(${rawSource}) => {}`, {
|
|
1017
1370
|
sourceType: "module",
|
|
1018
1371
|
plugins: ["typescript"]
|
|
1019
1372
|
});
|
|
1020
1373
|
const statement = parsed.program.body[0];
|
|
1021
1374
|
if (statement && types.isExpressionStatement(statement) && types.isArrowFunctionExpression(statement.expression)) {
|
|
1022
|
-
return
|
|
1375
|
+
return statement.expression.params[0] ?? null;
|
|
1023
1376
|
}
|
|
1024
1377
|
} catch {
|
|
1378
|
+
return null;
|
|
1025
1379
|
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
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;
|
|
1042
1399
|
}
|
|
1043
|
-
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;
|
|
1044
1410
|
}
|
|
1045
1411
|
function nodeHasToDirective(node) {
|
|
1046
1412
|
const toDirective = findDirectiveByName(node, "bind", "to");
|
|
@@ -1049,51 +1415,160 @@ function nodeHasToDirective(node) {
|
|
|
1049
1415
|
}
|
|
1050
1416
|
return void 0;
|
|
1051
1417
|
}
|
|
1052
|
-
function nodeHasForDirective(node) {
|
|
1053
|
-
return node.props.some(
|
|
1054
|
-
(attr) => attr.type === compilerCore.NodeTypes.DIRECTIVE && attr.name === "for"
|
|
1055
|
-
);
|
|
1056
|
-
}
|
|
1057
1418
|
function getKeyDirective(node) {
|
|
1058
1419
|
return findDirectiveByName(node, "bind", "key") ?? null;
|
|
1059
1420
|
}
|
|
1060
|
-
function
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1421
|
+
function tryUnwrapTemplateLiteralSource(source) {
|
|
1422
|
+
const rawSource = source.trim();
|
|
1423
|
+
if (!rawSource) {
|
|
1424
|
+
return null;
|
|
1425
|
+
}
|
|
1426
|
+
let ast = null;
|
|
1427
|
+
try {
|
|
1428
|
+
ast = parser.parseExpression(rawSource, { plugins: ["typescript"] });
|
|
1429
|
+
} catch {
|
|
1430
|
+
return null;
|
|
1065
1431
|
}
|
|
1066
|
-
if (
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
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;
|
|
1070
1464
|
}
|
|
1071
|
-
|
|
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);
|
|
1072
1551
|
}
|
|
1073
1552
|
function getModelBindingValues(node) {
|
|
1074
1553
|
let vModel = "";
|
|
1075
1554
|
const vModelDirective = findDirectiveByName(node, "model");
|
|
1076
|
-
if (vModelDirective?.exp
|
|
1077
|
-
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"));
|
|
1078
1557
|
}
|
|
1079
1558
|
let modelValue = null;
|
|
1080
1559
|
const modelValueDirective = findDirectiveByName(node, "bind", "modelValue");
|
|
1081
|
-
|
|
1082
|
-
|
|
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);
|
|
1083
1567
|
modelValue = mv;
|
|
1084
1568
|
}
|
|
1085
1569
|
return { vModel, modelValue };
|
|
1086
1570
|
}
|
|
1087
|
-
function
|
|
1088
|
-
if (node.isSelfClosing) {
|
|
1089
|
-
const hasForDirective = nodeHasForDirective(node);
|
|
1090
|
-
if (hasForDirective) {
|
|
1091
|
-
return getKeyDirectiveValue(node);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
return null;
|
|
1095
|
-
}
|
|
1096
|
-
function getIdOrName(node) {
|
|
1571
|
+
function getStaticIdOrNameHint(node) {
|
|
1097
1572
|
let idAttr = findAttributeByKey(node, "id");
|
|
1098
1573
|
if (!idAttr) {
|
|
1099
1574
|
idAttr = findAttributeByKey(node, "name");
|
|
@@ -1101,8 +1576,9 @@ function getIdOrName(node) {
|
|
|
1101
1576
|
let identifier = idAttr?.value?.content ?? "";
|
|
1102
1577
|
if (!identifier) {
|
|
1103
1578
|
const dynamicIdAttr = findDirectiveByName(node, "bind", "id");
|
|
1104
|
-
|
|
1105
|
-
|
|
1579
|
+
const dynamicNameAttr = findDirectiveByName(node, "bind", "name");
|
|
1580
|
+
if (dynamicIdAttr?.exp || dynamicNameAttr?.exp) {
|
|
1581
|
+
return "";
|
|
1106
1582
|
}
|
|
1107
1583
|
}
|
|
1108
1584
|
if (identifier.includes("-")) {
|
|
@@ -1113,20 +1589,20 @@ function getIdOrName(node) {
|
|
|
1113
1589
|
}
|
|
1114
1590
|
return identifier;
|
|
1115
1591
|
}
|
|
1116
|
-
function
|
|
1592
|
+
function getContainedInSlotDataKeyInfo(node, hierarchyMap2) {
|
|
1117
1593
|
let parent = getParent(hierarchyMap2, node);
|
|
1118
1594
|
while (parent) {
|
|
1119
1595
|
if (parent.type === compilerCore.NodeTypes.ELEMENT && parent.tag === "template") {
|
|
1120
|
-
const
|
|
1121
|
-
if (
|
|
1122
|
-
return
|
|
1596
|
+
const slotScopeExpression = findTemplateSlotScopeExpression(parent);
|
|
1597
|
+
if (slotScopeExpression) {
|
|
1598
|
+
return tryGetTemplateSlotScopeKeyInfo(slotScopeExpression);
|
|
1123
1599
|
}
|
|
1124
1600
|
}
|
|
1125
1601
|
parent = getParent(hierarchyMap2, parent);
|
|
1126
1602
|
}
|
|
1127
1603
|
return null;
|
|
1128
1604
|
}
|
|
1129
|
-
function
|
|
1605
|
+
function getContainedInVForDirectiveKeyInfo(context, node, hierarchyMap2) {
|
|
1130
1606
|
if (!context.scopes.vFor || context.scopes.vFor === 0) {
|
|
1131
1607
|
return null;
|
|
1132
1608
|
}
|
|
@@ -1135,8 +1611,7 @@ function getContainedInVForDirectiveKeyValue(context, node, hierarchyMap2) {
|
|
|
1135
1611
|
if (parent.type === compilerCore.NodeTypes.ELEMENT) {
|
|
1136
1612
|
const forDirective = findDirectiveByName(parent, "for");
|
|
1137
1613
|
if (forDirective) {
|
|
1138
|
-
|
|
1139
|
-
return keyValue;
|
|
1614
|
+
return getKeyDirectiveInfo(parent);
|
|
1140
1615
|
}
|
|
1141
1616
|
}
|
|
1142
1617
|
parent = getParent(hierarchyMap2, parent);
|
|
@@ -1162,13 +1637,7 @@ function tryGetContainedInStaticVForSourceLiteralValues(context, _node, _hierarc
|
|
|
1162
1637
|
if (simpleSourceExp.constType === compilerCore.ConstantTypes.NOT_CONSTANT) {
|
|
1163
1638
|
return null;
|
|
1164
1639
|
}
|
|
1165
|
-
const iterableRaw = (
|
|
1166
|
-
try {
|
|
1167
|
-
return compilerCore.stringifyExpression(simpleSourceExp).trim();
|
|
1168
|
-
} catch {
|
|
1169
|
-
return (simpleSourceExp.loc?.source ?? "").trim();
|
|
1170
|
-
}
|
|
1171
|
-
})();
|
|
1640
|
+
const iterableRaw = getVueExpressionSource(simpleSourceExp, "compiled", "loc");
|
|
1172
1641
|
if (!iterableRaw) {
|
|
1173
1642
|
return null;
|
|
1174
1643
|
}
|
|
@@ -1224,91 +1693,95 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1224
1693
|
return null;
|
|
1225
1694
|
}
|
|
1226
1695
|
const exp = handlerDirective.exp;
|
|
1227
|
-
const source = (exp
|
|
1696
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
1228
1697
|
if (!source) {
|
|
1229
1698
|
return null;
|
|
1230
1699
|
}
|
|
1231
1700
|
const mergeKey = `handler:expr:${source}`;
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
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) {
|
|
1236
1709
|
return null;
|
|
1237
1710
|
}
|
|
1238
|
-
const
|
|
1711
|
+
const isNodeType2 = (node2, type) => {
|
|
1239
1712
|
return node2 !== null && node2.type === type;
|
|
1240
1713
|
};
|
|
1241
|
-
const
|
|
1242
|
-
return
|
|
1714
|
+
const isIdentifierNode2 = (node2) => {
|
|
1715
|
+
return isNodeType2(node2, "Identifier") && typeof node2.name === "string";
|
|
1243
1716
|
};
|
|
1244
|
-
const
|
|
1245
|
-
return
|
|
1717
|
+
const isStringLiteralNode2 = (node2) => {
|
|
1718
|
+
return isNodeType2(node2, "StringLiteral") && typeof node2.value === "string";
|
|
1246
1719
|
};
|
|
1247
1720
|
const isBooleanLiteralNode = (node2) => {
|
|
1248
|
-
return
|
|
1721
|
+
return isNodeType2(node2, "BooleanLiteral") && typeof node2.value === "boolean";
|
|
1249
1722
|
};
|
|
1250
1723
|
const isNumericLiteralNode = (node2) => {
|
|
1251
|
-
return
|
|
1724
|
+
return isNodeType2(node2, "NumericLiteral") && typeof node2.value === "number";
|
|
1252
1725
|
};
|
|
1253
1726
|
const isNullLiteralNode = (node2) => {
|
|
1254
|
-
return
|
|
1727
|
+
return isNodeType2(node2, "NullLiteral");
|
|
1255
1728
|
};
|
|
1256
1729
|
const isMemberExpressionNode = (node2) => {
|
|
1257
|
-
if (!
|
|
1730
|
+
if (!isNodeType2(node2, "MemberExpression"))
|
|
1258
1731
|
return false;
|
|
1259
1732
|
const n = node2;
|
|
1260
1733
|
return typeof n.computed === "boolean" && typeof n.object === "object" && n.object !== null && typeof n.property === "object" && n.property !== null;
|
|
1261
1734
|
};
|
|
1262
1735
|
const isCallExpressionNode = (node2) => {
|
|
1263
|
-
if (!
|
|
1736
|
+
if (!isNodeType2(node2, "CallExpression"))
|
|
1264
1737
|
return false;
|
|
1265
1738
|
const n = node2;
|
|
1266
1739
|
return typeof n.callee === "object" && n.callee !== null && Array.isArray(n.arguments);
|
|
1267
1740
|
};
|
|
1268
1741
|
const isAwaitExpressionNode = (node2) => {
|
|
1269
|
-
if (!
|
|
1742
|
+
if (!isNodeType2(node2, "AwaitExpression"))
|
|
1270
1743
|
return false;
|
|
1271
1744
|
const n = node2;
|
|
1272
1745
|
return typeof n.argument === "object" && n.argument !== null;
|
|
1273
1746
|
};
|
|
1274
1747
|
const isAssignmentExpressionNode = (node2) => {
|
|
1275
|
-
if (!
|
|
1748
|
+
if (!isNodeType2(node2, "AssignmentExpression"))
|
|
1276
1749
|
return false;
|
|
1277
1750
|
const n = node2;
|
|
1278
1751
|
return typeof n.left === "object" && n.left !== null && typeof n.right === "object" && n.right !== null;
|
|
1279
1752
|
};
|
|
1280
1753
|
const isArrowFunctionExpressionNode = (node2) => {
|
|
1281
|
-
if (!
|
|
1754
|
+
if (!isNodeType2(node2, "ArrowFunctionExpression"))
|
|
1282
1755
|
return false;
|
|
1283
1756
|
const n = node2;
|
|
1284
1757
|
return typeof n.body === "object" && n.body !== null;
|
|
1285
1758
|
};
|
|
1286
1759
|
const isBlockStatementNode = (node2) => {
|
|
1287
|
-
if (!
|
|
1760
|
+
if (!isNodeType2(node2, "BlockStatement"))
|
|
1288
1761
|
return false;
|
|
1289
1762
|
const n = node2;
|
|
1290
1763
|
return Array.isArray(n.body);
|
|
1291
1764
|
};
|
|
1292
1765
|
const isExpressionStatementNode = (node2) => {
|
|
1293
|
-
if (!
|
|
1766
|
+
if (!isNodeType2(node2, "ExpressionStatement"))
|
|
1294
1767
|
return false;
|
|
1295
1768
|
const n = node2;
|
|
1296
1769
|
return typeof n.expression === "object" && n.expression !== null;
|
|
1297
1770
|
};
|
|
1298
1771
|
const isReturnStatementNode = (node2) => {
|
|
1299
|
-
if (!
|
|
1772
|
+
if (!isNodeType2(node2, "ReturnStatement"))
|
|
1300
1773
|
return false;
|
|
1301
1774
|
const n = node2;
|
|
1302
1775
|
return typeof n.argument === "object" || n.argument === null;
|
|
1303
1776
|
};
|
|
1304
|
-
const
|
|
1305
|
-
if (!
|
|
1777
|
+
const isObjectExpressionNode2 = (node2) => {
|
|
1778
|
+
if (!isNodeType2(node2, "ObjectExpression"))
|
|
1306
1779
|
return false;
|
|
1307
1780
|
const n = node2;
|
|
1308
1781
|
return Array.isArray(n.properties);
|
|
1309
1782
|
};
|
|
1310
|
-
const
|
|
1311
|
-
if (!
|
|
1783
|
+
const isObjectPropertyNode2 = (node2) => {
|
|
1784
|
+
if (!isNodeType2(node2, "ObjectProperty"))
|
|
1312
1785
|
return false;
|
|
1313
1786
|
const n = node2;
|
|
1314
1787
|
return typeof n.computed === "boolean" && typeof n.key === "object" && n.key !== null && typeof n.value === "object" && n.value !== null;
|
|
@@ -1316,16 +1789,16 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1316
1789
|
const getLastIdentifierFromMemberChain = (node2) => {
|
|
1317
1790
|
if (!node2)
|
|
1318
1791
|
return null;
|
|
1319
|
-
if (
|
|
1792
|
+
if (isIdentifierNode2(node2))
|
|
1320
1793
|
return node2.name;
|
|
1321
1794
|
if (isMemberExpressionNode(node2)) {
|
|
1322
1795
|
const prop = node2.property;
|
|
1323
1796
|
if (node2.computed === false) {
|
|
1324
|
-
if (
|
|
1797
|
+
if (isIdentifierNode2(prop))
|
|
1325
1798
|
return prop.name;
|
|
1326
1799
|
}
|
|
1327
1800
|
if (node2.computed === true) {
|
|
1328
|
-
if (
|
|
1801
|
+
if (isStringLiteralNode2(prop))
|
|
1329
1802
|
return prop.value;
|
|
1330
1803
|
}
|
|
1331
1804
|
}
|
|
@@ -1335,7 +1808,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1335
1808
|
if (!node2) {
|
|
1336
1809
|
return null;
|
|
1337
1810
|
}
|
|
1338
|
-
if (
|
|
1811
|
+
if (isIdentifierNode2(node2)) {
|
|
1339
1812
|
return node2.name;
|
|
1340
1813
|
}
|
|
1341
1814
|
if (isMemberExpressionNode(node2)) {
|
|
@@ -1347,11 +1820,11 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1347
1820
|
if (!lhs) {
|
|
1348
1821
|
return null;
|
|
1349
1822
|
}
|
|
1350
|
-
if (
|
|
1823
|
+
if (isIdentifierNode2(lhs)) {
|
|
1351
1824
|
return lhs.name;
|
|
1352
1825
|
}
|
|
1353
1826
|
if (isMemberExpressionNode(lhs)) {
|
|
1354
|
-
if (lhs.computed === false &&
|
|
1827
|
+
if (lhs.computed === false && isIdentifierNode2(lhs.property) && lhs.property.name === "value") {
|
|
1355
1828
|
return getLastIdentifierFromMemberChain(lhs.object);
|
|
1356
1829
|
}
|
|
1357
1830
|
return getLastIdentifierFromMemberChain(lhs);
|
|
@@ -1359,7 +1832,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1359
1832
|
return null;
|
|
1360
1833
|
};
|
|
1361
1834
|
const isTemplateLiteralNode = (node2) => {
|
|
1362
|
-
if (!
|
|
1835
|
+
if (!isNodeType2(node2, "TemplateLiteral")) {
|
|
1363
1836
|
return false;
|
|
1364
1837
|
}
|
|
1365
1838
|
const n = node2;
|
|
@@ -1378,7 +1851,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1378
1851
|
if (isNullLiteralNode(arg)) {
|
|
1379
1852
|
return "Null";
|
|
1380
1853
|
}
|
|
1381
|
-
if (
|
|
1854
|
+
if (isStringLiteralNode2(arg)) {
|
|
1382
1855
|
const cleaned = (arg.value ?? "").trim();
|
|
1383
1856
|
if (!cleaned) {
|
|
1384
1857
|
return null;
|
|
@@ -1404,7 +1877,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1404
1877
|
return toPascalCase(stableName.slice(0, 24));
|
|
1405
1878
|
}
|
|
1406
1879
|
}
|
|
1407
|
-
if (
|
|
1880
|
+
if (isIdentifierNode2(arg)) {
|
|
1408
1881
|
const firstChar = arg.name.charAt(0);
|
|
1409
1882
|
const isUpperAlpha = firstChar !== "" && firstChar === firstChar.toUpperCase() && firstChar !== firstChar.toLowerCase();
|
|
1410
1883
|
if (isUpperAlpha) {
|
|
@@ -1416,7 +1889,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1416
1889
|
const getStableSuffixFromCall = (call) => {
|
|
1417
1890
|
const args = call.arguments ?? [];
|
|
1418
1891
|
const first = args.length > 0 ? args[0] : null;
|
|
1419
|
-
if (!
|
|
1892
|
+
if (!isObjectExpressionNode2(first)) {
|
|
1420
1893
|
const parts2 = [];
|
|
1421
1894
|
for (const arg of args.slice(0, 4)) {
|
|
1422
1895
|
const w = stableWordFromValue(arg ?? null);
|
|
@@ -1435,20 +1908,20 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1435
1908
|
}
|
|
1436
1909
|
const parts = [];
|
|
1437
1910
|
for (const prop of first.properties ?? []) {
|
|
1438
|
-
if (!
|
|
1911
|
+
if (!isObjectPropertyNode2(prop)) {
|
|
1439
1912
|
continue;
|
|
1440
1913
|
}
|
|
1441
1914
|
if (prop.computed) {
|
|
1442
1915
|
continue;
|
|
1443
1916
|
}
|
|
1444
|
-
const keyName =
|
|
1917
|
+
const keyName = isIdentifierNode2(prop.key) ? prop.key.name : isStringLiteralNode2(prop.key) ? prop.key.value : null;
|
|
1445
1918
|
if (!keyName) {
|
|
1446
1919
|
continue;
|
|
1447
1920
|
}
|
|
1448
1921
|
let valueWord = null;
|
|
1449
1922
|
if (isBooleanLiteralNode(prop.value)) {
|
|
1450
1923
|
valueWord = prop.value.value ? "True" : "False";
|
|
1451
|
-
} else if (
|
|
1924
|
+
} else if (isStringLiteralNode2(prop.value)) {
|
|
1452
1925
|
const cleaned = (prop.value.value ?? "").trim();
|
|
1453
1926
|
if (cleaned) {
|
|
1454
1927
|
valueWord = toPascalCase(cleaned.slice(0, 24));
|
|
@@ -1577,13 +2050,18 @@ function getDataTestIdValueFromValueAttribute(node, actualFileName, attributeKey
|
|
|
1577
2050
|
return staticAttributeValue(`${actualFileName}-${value}-${role}`);
|
|
1578
2051
|
}
|
|
1579
2052
|
const attrDynamic = findDirectiveByName(node, "bind", attributeKey);
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
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)) {
|
|
1583
2061
|
return staticAttributeValue(`${actualFileName}-${value.replaceAll(".", "")}-${role}`);
|
|
1584
2062
|
}
|
|
1585
|
-
if (
|
|
1586
|
-
value =
|
|
2063
|
+
if (types.isCallExpression(attrDynamicAst) || types.isOptionalCallExpression(attrDynamicAst)) {
|
|
2064
|
+
value = getVueExpressionSource(attrDynamic.exp, "compiled", "loc");
|
|
1587
2065
|
return templateAttributeValue(`${actualFileName}-\${${value}}-${role}`);
|
|
1588
2066
|
}
|
|
1589
2067
|
return staticAttributeValue(`${actualFileName}-${value}-${role}`);
|
|
@@ -1591,16 +2069,16 @@ function getDataTestIdValueFromValueAttribute(node, actualFileName, attributeKey
|
|
|
1591
2069
|
return null;
|
|
1592
2070
|
}
|
|
1593
2071
|
function generateToDirectiveDataTestId(componentName, node, toDirective, context, hierarchyMap2, nativeWrappers) {
|
|
1594
|
-
const
|
|
1595
|
-
if (
|
|
1596
|
-
return templateAttributeValue(`${componentName}-${
|
|
2072
|
+
const keyInfo = getKeyDirectiveInfo(node) || getContainedInVForDirectiveKeyInfo(context, node, hierarchyMap2);
|
|
2073
|
+
if (keyInfo) {
|
|
2074
|
+
return templateAttributeValue(`${componentName}-${keyInfo.selectorFragment}-${formatTagName(node, nativeWrappers)}`);
|
|
1597
2075
|
} else {
|
|
1598
2076
|
let name = toDirectiveObjectFieldNameValue(toDirective);
|
|
1599
2077
|
if (!name) {
|
|
1600
2078
|
if (toDirective.exp == null) {
|
|
1601
2079
|
return null;
|
|
1602
2080
|
}
|
|
1603
|
-
const source =
|
|
2081
|
+
const source = getVueExpressionSource(toDirective.exp, "compiled", "loc");
|
|
1604
2082
|
const toAst = toDirective.exp.ast;
|
|
1605
2083
|
const interpolated = toAst !== void 0 && toAst !== null && toAst !== false && types.isTemplateLiteral(toAst);
|
|
1606
2084
|
return templateAttributeValue(`${componentName}-\${${source}${interpolated ? ".replaceAll(' ', '')" : "?.name?.replaceAll(' ', '') ?? ''"}}${formatTagName(node, nativeWrappers)}`);
|
|
@@ -1624,44 +2102,46 @@ function toDirectiveObjectFieldNameValue(node) {
|
|
|
1624
2102
|
if (!node.exp || node.exp.type !== compilerCore.NodeTypes.COMPOUND_EXPRESSION && node.exp.type !== compilerCore.NodeTypes.SIMPLE_EXPRESSION) {
|
|
1625
2103
|
return null;
|
|
1626
2104
|
}
|
|
1627
|
-
const
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
const isStringLiteralNode = (n) => {
|
|
1634
|
-
return isNodeType(n, "StringLiteral") && typeof n.value === "string";
|
|
1635
|
-
};
|
|
1636
|
-
const isIdentifierNode = (n) => {
|
|
1637
|
-
return isNodeType(n, "Identifier") && typeof n.name === "string";
|
|
1638
|
-
};
|
|
1639
|
-
const isObjectPropertyNode = (n) => {
|
|
1640
|
-
if (!isNodeType(n, "ObjectProperty"))
|
|
1641
|
-
return false;
|
|
1642
|
-
const nn = n;
|
|
1643
|
-
return typeof nn.key === "object" && nn.key !== null && typeof nn.value === "object" && nn.value !== null;
|
|
1644
|
-
};
|
|
1645
|
-
const isObjectExpressionNode = (n) => {
|
|
1646
|
-
if (!isNodeType(n, "ObjectExpression"))
|
|
1647
|
-
return false;
|
|
1648
|
-
const nn = n;
|
|
1649
|
-
return Array.isArray(nn.properties);
|
|
1650
|
-
};
|
|
1651
|
-
if (!isObjectExpressionNode(expr))
|
|
1652
|
-
return null;
|
|
1653
|
-
const nameProp = expr.properties.find((p) => {
|
|
1654
|
-
if (!isObjectPropertyNode(p))
|
|
1655
|
-
return false;
|
|
1656
|
-
const key = p.key;
|
|
1657
|
-
return isIdentifierNode(key) && key.name === "name" || isStringLiteralNode(key) && key.value === "name";
|
|
1658
|
-
});
|
|
1659
|
-
if (!nameProp || !isObjectPropertyNode(nameProp) || !isStringLiteralNode(nameProp.value))
|
|
1660
|
-
return null;
|
|
1661
|
-
return toPascalCase(nameProp.value.value);
|
|
1662
|
-
} catch {
|
|
2105
|
+
const expr = tryGetDirectiveBabelAst(node, {
|
|
2106
|
+
preferredViews: ["loc", "compiled"],
|
|
2107
|
+
plugins: ["typescript"],
|
|
2108
|
+
preferExistingAst: false
|
|
2109
|
+
});
|
|
2110
|
+
if (!expr) {
|
|
1663
2111
|
return null;
|
|
1664
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);
|
|
1665
2145
|
}
|
|
1666
2146
|
function getComposedClickHandlerContent(node, _context, innerText, clickDirective, _options = {}) {
|
|
1667
2147
|
const click = clickDirective ?? tryGetClickDirective(node);
|
|
@@ -1671,7 +2151,7 @@ function getComposedClickHandlerContent(node, _context, innerText, clickDirectiv
|
|
|
1671
2151
|
let handlerName = "";
|
|
1672
2152
|
if (click.exp) {
|
|
1673
2153
|
const exp = click.exp;
|
|
1674
|
-
const source = (exp
|
|
2154
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
1675
2155
|
if (source) {
|
|
1676
2156
|
const parsed = tryParseBabelAstFromHandlerSource(source);
|
|
1677
2157
|
if (parsed) {
|
|
@@ -1692,9 +2172,9 @@ function tryParseBabelAstFromHandlerSource(source) {
|
|
|
1692
2172
|
const trimmed = source.trim();
|
|
1693
2173
|
if (!trimmed)
|
|
1694
2174
|
return null;
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
2175
|
+
const expressionAst = tryParseBabelExpressionFromSource(trimmed, ["typescript", "jsx"]);
|
|
2176
|
+
if (expressionAst) {
|
|
2177
|
+
return expressionAst;
|
|
1698
2178
|
}
|
|
1699
2179
|
try {
|
|
1700
2180
|
return parser.parse(trimmed, { sourceType: "module", plugins: ["typescript", "jsx"] });
|
|
@@ -2033,23 +2513,24 @@ function tryGetExistingElementDataTestId(node, attributeName = "data-testid") {
|
|
|
2033
2513
|
return null;
|
|
2034
2514
|
}
|
|
2035
2515
|
const simpleExp = exp;
|
|
2036
|
-
const ast = simpleExp
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
const
|
|
2043
|
-
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;
|
|
2044
2523
|
if (isStatic) {
|
|
2045
|
-
return { value:
|
|
2524
|
+
return { value: unwrappedTemplateLiteral.template, isDynamic: false, isStaticLiteral: true };
|
|
2046
2525
|
}
|
|
2526
|
+
const templateValue = templateAttributeValue(unwrappedTemplateLiteral.template);
|
|
2047
2527
|
return {
|
|
2048
|
-
value:
|
|
2528
|
+
value: templateValue.template,
|
|
2049
2529
|
isDynamic: true,
|
|
2050
2530
|
isStaticLiteral: false,
|
|
2051
|
-
template:
|
|
2052
|
-
|
|
2531
|
+
template: templateValue.template,
|
|
2532
|
+
parsedTemplate: templateValue.parsedTemplate,
|
|
2533
|
+
templateExpressionCount: unwrappedTemplateLiteral.expressionCount
|
|
2053
2534
|
};
|
|
2054
2535
|
}
|
|
2055
2536
|
if (ast && typeof ast === "object" && "type" in ast && ast.type === "StringLiteral") {
|
|
@@ -2062,59 +2543,27 @@ function tryGetExistingElementDataTestId(node, attributeName = "data-testid") {
|
|
|
2062
2543
|
}
|
|
2063
2544
|
const preservableReference = tryGetPreservableDynamicReferenceExpression(ast);
|
|
2064
2545
|
if (preservableReference) {
|
|
2546
|
+
const templateValue = templateAttributeValue(`\${${preservableReference}}`);
|
|
2065
2547
|
return {
|
|
2066
2548
|
value: preservableReference,
|
|
2067
|
-
isDynamic: true,
|
|
2068
|
-
isStaticLiteral: false,
|
|
2069
|
-
template:
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
try {
|
|
2079
|
-
const ast2 = parser.parseExpression(raw, { plugins: ["typescript"] });
|
|
2080
|
-
if (ast2 && typeof ast2 === "object" && "type" in ast2 && ast2.type === "TemplateLiteral") {
|
|
2081
|
-
const tl = ast2;
|
|
2082
|
-
const cooked = (tl.quasis ?? []).map((q) => q.value?.cooked ?? "").join("");
|
|
2083
|
-
const expressionCount = (tl.expressions ?? []).length;
|
|
2084
|
-
const isStatic = expressionCount === 0;
|
|
2085
|
-
const unwrappedTemplate = raw.startsWith("`") && raw.endsWith("`") && raw.length >= 2 ? raw.slice(1, -1) : cooked;
|
|
2086
|
-
if (isStatic) {
|
|
2087
|
-
return { value: unwrappedTemplate, isDynamic: false, isStaticLiteral: true };
|
|
2088
|
-
}
|
|
2089
|
-
return {
|
|
2090
|
-
value: unwrappedTemplate,
|
|
2091
|
-
isDynamic: true,
|
|
2092
|
-
isStaticLiteral: false,
|
|
2093
|
-
template: unwrappedTemplate,
|
|
2094
|
-
templateExpressionCount: expressionCount
|
|
2095
|
-
};
|
|
2096
|
-
}
|
|
2097
|
-
if (ast2 && typeof ast2 === "object" && "type" in ast2 && ast2.type === "StringLiteral") {
|
|
2098
|
-
const sl = ast2;
|
|
2099
|
-
return { value: sl.value ?? "", isDynamic: false, isStaticLiteral: true };
|
|
2100
|
-
}
|
|
2101
|
-
const preservableReference2 = tryGetPreservableDynamicReferenceExpression(ast2);
|
|
2102
|
-
if (preservableReference2) {
|
|
2103
|
-
return {
|
|
2104
|
-
value: preservableReference2,
|
|
2105
|
-
isDynamic: true,
|
|
2106
|
-
isStaticLiteral: false,
|
|
2107
|
-
template: `\${${preservableReference2}}`,
|
|
2108
|
-
templateExpressionCount: 1,
|
|
2109
|
-
rawExpression: preservableReference2
|
|
2110
|
-
};
|
|
2111
|
-
}
|
|
2112
|
-
} 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;
|
|
2113
2560
|
}
|
|
2114
2561
|
return { value: raw, isDynamic: true, isStaticLiteral: false, rawExpression: raw };
|
|
2115
2562
|
}
|
|
2116
2563
|
function isTemplatePlaceholder(part) {
|
|
2117
|
-
|
|
2564
|
+
const parsedTemplate = tryParseTemplateFragment(part);
|
|
2565
|
+
const templateFragment = parsedTemplate ? getSingleExpressionTemplateFragment(parsedTemplate) : null;
|
|
2566
|
+
return !!templateFragment && templateFragment.prefix === "" && templateFragment.suffix === "";
|
|
2118
2567
|
}
|
|
2119
2568
|
function isAllCapsOrDigits(value) {
|
|
2120
2569
|
if (value.length <= 1) {
|
|
@@ -2167,33 +2616,14 @@ function safeMethodNameFromParts(parts) {
|
|
|
2167
2616
|
}
|
|
2168
2617
|
return name;
|
|
2169
2618
|
}
|
|
2170
|
-
function
|
|
2619
|
+
function toPomKeyPattern(templateValue) {
|
|
2620
|
+
const { templateLiteral } = templateValue.parsedTemplate;
|
|
2171
2621
|
let out = "";
|
|
2172
|
-
let i = 0;
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
out += template.slice(i);
|
|
2177
|
-
break;
|
|
2178
|
-
}
|
|
2179
|
-
out += template.slice(i, start);
|
|
2180
|
-
let depth = 1;
|
|
2181
|
-
let j = start + 2;
|
|
2182
|
-
while (j < template.length && depth > 0) {
|
|
2183
|
-
if (template[j] === "{") {
|
|
2184
|
-
depth++;
|
|
2185
|
-
} else if (template[j] === "}") {
|
|
2186
|
-
depth--;
|
|
2187
|
-
}
|
|
2188
|
-
j++;
|
|
2189
|
-
}
|
|
2190
|
-
const end = depth === 0 ? j - 1 : -1;
|
|
2191
|
-
if (end < 0) {
|
|
2192
|
-
out += template.slice(start);
|
|
2193
|
-
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}";
|
|
2194
2626
|
}
|
|
2195
|
-
out += "${key}";
|
|
2196
|
-
i = end + 1;
|
|
2197
2627
|
}
|
|
2198
2628
|
return out;
|
|
2199
2629
|
}
|
|
@@ -2201,8 +2631,8 @@ function applyResolvedDataTestId(args) {
|
|
|
2201
2631
|
const addHtmlAttribute = args.addHtmlAttribute ?? true;
|
|
2202
2632
|
const entryOverrides = args.entryOverrides ?? {};
|
|
2203
2633
|
const testIdAttribute = args.testIdAttribute ?? "data-testid";
|
|
2204
|
-
const existingIdBehavior = args.existingIdBehavior ?? "
|
|
2205
|
-
const nameCollisionBehavior = args.nameCollisionBehavior ?? "
|
|
2634
|
+
const existingIdBehavior = args.existingIdBehavior ?? "error";
|
|
2635
|
+
const nameCollisionBehavior = args.nameCollisionBehavior ?? "error";
|
|
2206
2636
|
const warn = args.warn;
|
|
2207
2637
|
const getBestKeyAccessCandidates = (expr) => {
|
|
2208
2638
|
if (!expr) {
|
|
@@ -2211,7 +2641,10 @@ function applyResolvedDataTestId(args) {
|
|
|
2211
2641
|
return splitNullishCoalescingExpression(expr);
|
|
2212
2642
|
};
|
|
2213
2643
|
let dataTestId = args.preferredGeneratedValue;
|
|
2644
|
+
let runtimeDataTestId = args.preferredRuntimeValue ?? args.preferredGeneratedValue;
|
|
2214
2645
|
let fromExisting = false;
|
|
2646
|
+
const bestKeyPreservePlaceholder = args.keyInfo?.runtimeFragment ?? null;
|
|
2647
|
+
const bestKeyVariable = args.keyInfo?.rawExpression ?? null;
|
|
2215
2648
|
const existing = tryGetExistingElementDataTestId(args.element, testIdAttribute);
|
|
2216
2649
|
if (existing) {
|
|
2217
2650
|
const loc = args.element.loc?.start;
|
|
@@ -2232,8 +2665,10 @@ Bulk cleanup: run ESLint with the @immense/vue-pom-generator/remove-existing-tes
|
|
|
2232
2665
|
if (existingIdBehavior === "preserve") {
|
|
2233
2666
|
if (existing.isDynamic) {
|
|
2234
2667
|
if (existing.template) {
|
|
2235
|
-
const
|
|
2236
|
-
|
|
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) {
|
|
2237
2672
|
throw new Error(
|
|
2238
2673
|
`[vue-pom-generator] Existing ${attrLabel} is a template literal with multiple interpolations and cannot be preserved safely.
|
|
2239
2674
|
Component: ${args.componentName}
|
|
@@ -2243,20 +2678,21 @@ Existing ${attrLabel}: ${JSON.stringify(existing.value)}
|
|
|
2243
2678
|
Fix: reduce the template to a single key-based interpolation, or remove the explicit ${attrLabel} so it can be auto-generated.`
|
|
2244
2679
|
);
|
|
2245
2680
|
}
|
|
2246
|
-
const hasExact =
|
|
2247
|
-
const hasVarAccess = getBestKeyAccessCandidates(
|
|
2248
|
-
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) {
|
|
2249
2684
|
throw new Error(
|
|
2250
2685
|
`[vue-pom-generator] Existing ${attrLabel} appears to be missing the key placeholder needed to keep it unique.
|
|
2251
2686
|
Component: ${args.componentName}
|
|
2252
2687
|
File: ${file}:${locationHint}
|
|
2253
2688
|
Existing ${attrLabel}: ${JSON.stringify(existing.value)}
|
|
2254
|
-
Required placeholder: ${JSON.stringify(
|
|
2689
|
+
Required placeholder: ${JSON.stringify(bestKeyPreservePlaceholder)}${bestKeyVariable ? ` or an access on "${bestKeyVariable}"` : ""}
|
|
2255
2690
|
|
|
2256
|
-
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.`
|
|
2257
2692
|
);
|
|
2258
2693
|
}
|
|
2259
|
-
dataTestId =
|
|
2694
|
+
dataTestId = existingTemplateValue;
|
|
2695
|
+
runtimeDataTestId = existingTemplateValue;
|
|
2260
2696
|
fromExisting = true;
|
|
2261
2697
|
} else {
|
|
2262
2698
|
throw new Error(
|
|
@@ -2270,18 +2706,19 @@ If you really need a computed id, do not set existingIdBehavior="preserve".`
|
|
|
2270
2706
|
);
|
|
2271
2707
|
}
|
|
2272
2708
|
} else {
|
|
2273
|
-
if (
|
|
2709
|
+
if (bestKeyPreservePlaceholder && existing.isStaticLiteral) {
|
|
2274
2710
|
throw new Error(
|
|
2275
2711
|
`[vue-pom-generator] Existing ${attrLabel} appears to be missing the key placeholder needed to keep it unique.
|
|
2276
2712
|
Component: ${args.componentName}
|
|
2277
2713
|
File: ${file}:${locationHint}
|
|
2278
2714
|
Existing ${attrLabel}: ${JSON.stringify(existing.value)}
|
|
2279
|
-
Required placeholder: ${JSON.stringify(
|
|
2715
|
+
Required placeholder: ${JSON.stringify(bestKeyPreservePlaceholder)}${bestKeyVariable ? ` or an access on "${bestKeyVariable}"` : ""}
|
|
2280
2716
|
|
|
2281
|
-
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.`
|
|
2282
2718
|
);
|
|
2283
2719
|
}
|
|
2284
2720
|
dataTestId = staticAttributeValue(existing.value);
|
|
2721
|
+
runtimeDataTestId = staticAttributeValue(existing.value);
|
|
2285
2722
|
fromExisting = true;
|
|
2286
2723
|
}
|
|
2287
2724
|
}
|
|
@@ -2311,8 +2748,10 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2311
2748
|
};
|
|
2312
2749
|
const normalizedRole = normalizeNativeRole(args.nativeRole) ?? "button";
|
|
2313
2750
|
const targetPageObjectModelClass = entryOverrides.targetPageObjectModelClass;
|
|
2314
|
-
const formattedDataTestIdForPom = dataTestId.kind === "template" ?
|
|
2315
|
-
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";
|
|
2316
2755
|
const deriveBaseMethodNameFromHint = (hint) => {
|
|
2317
2756
|
const hintRaw = (hint ?? "").trim();
|
|
2318
2757
|
const trimEdgeSeparators = (value) => {
|
|
@@ -2360,13 +2799,13 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2360
2799
|
const roleSuffix = upperFirst(normalizedRole || "Element");
|
|
2361
2800
|
const baseName = upperFirst(primaryMethodName);
|
|
2362
2801
|
const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
|
|
2363
|
-
return
|
|
2802
|
+
return selectorIsParameterized ? removeByKeySegment2(propertyName) : propertyName;
|
|
2364
2803
|
};
|
|
2365
2804
|
const getPrimaryGetterNameCandidates = (primaryMethodName) => {
|
|
2366
2805
|
const roleSuffix = upperFirst(normalizedRole || "Element");
|
|
2367
2806
|
const baseName = upperFirst(primaryMethodName);
|
|
2368
2807
|
const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
|
|
2369
|
-
if (!
|
|
2808
|
+
if (!selectorIsParameterized) {
|
|
2370
2809
|
return { primary: propertyName };
|
|
2371
2810
|
}
|
|
2372
2811
|
const stripped = removeByKeySegment2(propertyName);
|
|
@@ -2428,11 +2867,11 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2428
2867
|
return false;
|
|
2429
2868
|
}
|
|
2430
2869
|
const existingSelectors = [
|
|
2431
|
-
existingPom.
|
|
2432
|
-
...existingPom.
|
|
2870
|
+
existingPom.selector,
|
|
2871
|
+
...existingPom.alternateSelectors ?? []
|
|
2433
2872
|
];
|
|
2434
|
-
const sharesSelectorIdentity = existingSelectors.
|
|
2435
|
-
if (
|
|
2873
|
+
const sharesSelectorIdentity = existingSelectors.some((existingSelector) => pomStringPatternEquals(existingSelector, selectorPattern));
|
|
2874
|
+
if (selectorIsParameterized && !sharesSelectorIdentity) {
|
|
2436
2875
|
return false;
|
|
2437
2876
|
}
|
|
2438
2877
|
if (!mergeKey && !sharesSelectorIdentity) {
|
|
@@ -2447,12 +2886,15 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2447
2886
|
if ((existingEntry.targetPageObjectModelClass ?? null) !== (targetPageObjectModelClass ?? null)) {
|
|
2448
2887
|
return false;
|
|
2449
2888
|
}
|
|
2450
|
-
if (existingPom.
|
|
2451
|
-
existingPom.
|
|
2452
|
-
if (!existingPom.
|
|
2453
|
-
existingPom.
|
|
2889
|
+
if (!pomStringPatternEquals(existingPom.selector, selectorPattern)) {
|
|
2890
|
+
existingPom.alternateSelectors ??= [];
|
|
2891
|
+
if (!(existingPom.alternateSelectors ?? []).some((existingSelector) => pomStringPatternEquals(existingSelector, selectorPattern))) {
|
|
2892
|
+
existingPom.alternateSelectors.push(selectorPattern);
|
|
2454
2893
|
}
|
|
2455
2894
|
}
|
|
2895
|
+
if (selectorIsParameterized && !existingPom.parameters.some((param) => param.name === "key")) {
|
|
2896
|
+
existingPom.parameters = [createPomParameterSpec("key", keyTypeFromValues), ...existingPom.parameters];
|
|
2897
|
+
}
|
|
2456
2898
|
return true;
|
|
2457
2899
|
};
|
|
2458
2900
|
let methodName = "";
|
|
@@ -2465,7 +2907,7 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2465
2907
|
let suffix = 1;
|
|
2466
2908
|
while (true) {
|
|
2467
2909
|
const baseWithSuffix = suffix === 1 ? base : `${base}${suffix}`;
|
|
2468
|
-
const candidate =
|
|
2910
|
+
const candidate = selectorIsParameterized ? `${baseWithSuffix}ByKey` : baseWithSuffix;
|
|
2469
2911
|
const actionName = getPrimaryActionMethodName(candidate);
|
|
2470
2912
|
const getterCandidates = getPrimaryGetterNameCandidates(candidate);
|
|
2471
2913
|
let chosenGetterName = getterCandidates.primary;
|
|
@@ -2491,7 +2933,7 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2491
2933
|
const baseNameUpper = upperFirst(baseWithSuffix);
|
|
2492
2934
|
if (!hasRoleSuffix(baseNameUpper, roleSuffix)) {
|
|
2493
2935
|
const baseWithRoleSuffix = `${baseWithSuffix}${roleSuffix}`;
|
|
2494
|
-
const candidateWithRoleSuffix =
|
|
2936
|
+
const candidateWithRoleSuffix = selectorIsParameterized ? `${baseWithRoleSuffix}ByKey` : baseWithRoleSuffix;
|
|
2495
2937
|
const actionNameWithRoleSuffix = getPrimaryActionMethodName(candidateWithRoleSuffix);
|
|
2496
2938
|
const getterCandidatesWithRoleSuffix = getPrimaryGetterNameCandidates(candidateWithRoleSuffix);
|
|
2497
2939
|
let chosenGetterNameWithRoleSuffix = getterCandidatesWithRoleSuffix.primary;
|
|
@@ -2559,51 +3001,46 @@ Conflicts: getter=${last.getterName}, method=${last.actionName}
|
|
|
2559
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".`
|
|
2560
3002
|
);
|
|
2561
3003
|
}
|
|
2562
|
-
|
|
2563
|
-
if (isKeyed) {
|
|
2564
|
-
params.key = keyTypeFromValues;
|
|
2565
|
-
}
|
|
3004
|
+
let parameters = selectorIsParameterized ? [createPomParameterSpec("key", keyTypeFromValues)] : [];
|
|
2566
3005
|
switch (normalizedRole) {
|
|
2567
3006
|
case "input":
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
if (!isKeyed) delete params.key;
|
|
3007
|
+
parameters = setPomParameter(parameters, "text", "string");
|
|
3008
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2571
3009
|
break;
|
|
2572
3010
|
case "select":
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
if (!isKeyed) delete params.key;
|
|
3011
|
+
parameters = setPomParameter(parameters, "value", "string");
|
|
3012
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2576
3013
|
break;
|
|
2577
3014
|
case "vselect":
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
if (!isKeyed) delete params.key;
|
|
3015
|
+
parameters = setPomParameter(parameters, "value", "string");
|
|
3016
|
+
parameters = setPomParameter(parameters, "timeOut", "number = 500");
|
|
3017
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2582
3018
|
break;
|
|
2583
3019
|
case "radio":
|
|
2584
|
-
|
|
3020
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2585
3021
|
break;
|
|
2586
3022
|
}
|
|
2587
|
-
|
|
2588
|
-
params.key = keyTypeFromValues;
|
|
2589
|
-
}
|
|
3023
|
+
const normalizedParameters = selectorIsParameterized ? setPomParameter(parameters, "key", keyTypeFromValues) : removePomParameter(parameters, "key");
|
|
2590
3024
|
if (addHtmlAttribute && !fromExisting) {
|
|
2591
3025
|
upsertAttribute(args.element, testIdAttribute, dataTestId);
|
|
2592
3026
|
}
|
|
2593
3027
|
const childComponentName = args.element.tag;
|
|
2594
3028
|
const dataTestIdEntry = {
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
3029
|
+
selectorValue: entryOverrides.selectorValue ?? createPomStringPattern(
|
|
3030
|
+
getAttributeValueText(dataTestId),
|
|
3031
|
+
dataTestId.kind === "template" ? "parameterized" : "static"
|
|
3032
|
+
),
|
|
3033
|
+
templateLiteral: entryOverrides.templateLiteral,
|
|
3034
|
+
targetPageObjectModelClass: entryOverrides.targetPageObjectModelClass
|
|
2598
3035
|
};
|
|
2599
3036
|
dataTestIdEntry.pom = {
|
|
2600
3037
|
nativeRole: normalizedRole,
|
|
2601
3038
|
methodName,
|
|
2602
3039
|
getterNameOverride,
|
|
2603
|
-
|
|
2604
|
-
|
|
3040
|
+
selector: selectorPattern,
|
|
3041
|
+
alternateSelectors: void 0,
|
|
2605
3042
|
mergeKey: args.pomMergeKey,
|
|
2606
|
-
|
|
3043
|
+
parameters: normalizedParameters,
|
|
2607
3044
|
keyValuesOverride: args.keyValuesOverride ?? null
|
|
2608
3045
|
// emitPrimary defaults to true; special cases (including merge) may set it to false below.
|
|
2609
3046
|
};
|
|
@@ -2634,43 +3071,18 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2634
3071
|
}
|
|
2635
3072
|
};
|
|
2636
3073
|
const getSignatureForGeneratedMethod = () => {
|
|
2637
|
-
|
|
2638
|
-
const isNavigation2 = !!dataTestIdEntry.targetPageObjectModelClass;
|
|
2639
|
-
const needsKey2 = Object.prototype.hasOwnProperty.call(params, "key");
|
|
2640
|
-
const keyType = keyTypeFromValues;
|
|
2641
|
-
if (isNavigation2) {
|
|
2642
|
-
if (needsKey2) {
|
|
2643
|
-
return { params: `key: ${keyType}`, argNames: ["key"] };
|
|
2644
|
-
}
|
|
2645
|
-
return { params: "", argNames: [] };
|
|
2646
|
-
}
|
|
2647
|
-
switch (role) {
|
|
2648
|
-
case "input":
|
|
2649
|
-
return needsKey2 ? { params: `key: ${keyType}, text: string, annotationText: string = ""`, argNames: ["key", "text", "annotationText"] } : { params: 'text: string, annotationText: string = ""', argNames: ["text", "annotationText"] };
|
|
2650
|
-
case "select":
|
|
2651
|
-
return needsKey2 ? { params: `key: ${keyType}, value: string, annotationText: string = ""`, argNames: ["key", "value", "annotationText"] } : { params: 'value: string, annotationText: string = ""', argNames: ["value", "annotationText"] };
|
|
2652
|
-
case "vselect":
|
|
2653
|
-
return needsKey2 ? { params: `key: ${keyType}, value: string, timeOut = 500`, argNames: ["key", "value", "timeOut"] } : { params: "value: string, timeOut = 500", argNames: ["value", "timeOut"] };
|
|
2654
|
-
case "radio":
|
|
2655
|
-
return needsKey2 ? { params: `key: ${keyType}, annotationText: string = ""`, argNames: ["key", "annotationText"] } : { params: 'annotationText: string = ""', argNames: ["annotationText"] };
|
|
2656
|
-
default:
|
|
2657
|
-
if (needsKey2) {
|
|
2658
|
-
return { params: `key: ${keyType}`, argNames: ["key"] };
|
|
2659
|
-
}
|
|
2660
|
-
return { params: "", argNames: [] };
|
|
2661
|
-
}
|
|
3074
|
+
return createPomMethodSignature(normalizedParameters);
|
|
2662
3075
|
};
|
|
2663
3076
|
const registerPrimaryOnce = (pom) => {
|
|
2664
|
-
const
|
|
2665
|
-
const alternates = (pom.alternateFormattedDataTestIds ?? []).slice().sort();
|
|
3077
|
+
const alternates = (pom.alternateSelectors ?? []).slice().sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
2666
3078
|
const key = JSON.stringify({
|
|
2667
3079
|
kind: "primary",
|
|
2668
3080
|
role: pom.nativeRole,
|
|
2669
3081
|
methodName: pom.methodName,
|
|
2670
3082
|
getterNameOverride: pom.getterNameOverride ?? null,
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
3083
|
+
selector: pom.selector,
|
|
3084
|
+
alternateSelectors: alternates.length ? alternates : void 0,
|
|
3085
|
+
parameters: pom.parameters,
|
|
2674
3086
|
target: dataTestIdEntry.targetPageObjectModelClass ?? null,
|
|
2675
3087
|
emitPrimary: pom.emitPrimary ?? true
|
|
2676
3088
|
});
|
|
@@ -2684,8 +3096,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2684
3096
|
}
|
|
2685
3097
|
};
|
|
2686
3098
|
const addExtraClickMethod = (spec) => {
|
|
2687
|
-
const
|
|
2688
|
-
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 });
|
|
2689
3100
|
const seen = args.generatedMethodContentByComponent.get(args.parentComponentName) ?? /* @__PURE__ */ new Set();
|
|
2690
3101
|
if (!args.generatedMethodContentByComponent.has(args.parentComponentName)) {
|
|
2691
3102
|
args.generatedMethodContentByComponent.set(args.parentComponentName, seen);
|
|
@@ -2708,7 +3119,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2708
3119
|
if (prev === null) {
|
|
2709
3120
|
return;
|
|
2710
3121
|
}
|
|
2711
|
-
if (signature === null || prev
|
|
3122
|
+
if (signature === null || !pomMethodSignatureEquals(prev, signature)) {
|
|
2712
3123
|
args.dependencies.generatedMethods.set(name, null);
|
|
2713
3124
|
}
|
|
2714
3125
|
};
|
|
@@ -2724,23 +3135,10 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2724
3135
|
return candidate;
|
|
2725
3136
|
};
|
|
2726
3137
|
const tryGetDirectiveExpressionAst = (dir) => {
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
}
|
|
2731
|
-
if (exp.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION) {
|
|
2732
|
-
const simple = exp;
|
|
2733
|
-
const ast = simple.ast;
|
|
2734
|
-
if (ast && "type" in ast) {
|
|
2735
|
-
return ast;
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
try {
|
|
2739
|
-
const raw = args.context ? compilerCore.stringifyExpression(exp) : exp.loc.source;
|
|
2740
|
-
return parser.parseExpression(raw, { plugins: ["typescript"] });
|
|
2741
|
-
} catch {
|
|
2742
|
-
return null;
|
|
2743
|
-
}
|
|
3138
|
+
return tryGetDirectiveBabelAst(dir, {
|
|
3139
|
+
preferredViews: args.context ? ["compiled", "loc"] : ["loc", "compiled"],
|
|
3140
|
+
plugins: ["typescript"]
|
|
3141
|
+
});
|
|
2744
3142
|
};
|
|
2745
3143
|
const tryGetStaticStringFromBabel = (node) => {
|
|
2746
3144
|
if (!node) {
|
|
@@ -2837,17 +3235,17 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2837
3235
|
name: generatedName2,
|
|
2838
3236
|
selector: {
|
|
2839
3237
|
kind: "withinTestIdByLabel",
|
|
2840
|
-
|
|
2841
|
-
|
|
3238
|
+
rootTestId: createPomStringPattern(wrapperTestId, selectorPatternKind),
|
|
3239
|
+
label: createPomStringPattern(label, "static"),
|
|
2842
3240
|
exact: true
|
|
2843
3241
|
},
|
|
2844
|
-
|
|
3242
|
+
parameters: [createPomParameterSpec("annotationText", `string = ""`)]
|
|
2845
3243
|
});
|
|
2846
3244
|
if (added2) {
|
|
2847
|
-
registerGeneratedMethodSignature(generatedName2,
|
|
3245
|
+
registerGeneratedMethodSignature(generatedName2, createPomMethodSignature([createPomParameterSpec("annotationText", `string = ""`)]));
|
|
2848
3246
|
}
|
|
2849
3247
|
}
|
|
2850
|
-
return;
|
|
3248
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2851
3249
|
}
|
|
2852
3250
|
const generatedName = ensureUniqueGeneratedName(`select${upperFirst(methodName || "Radio")}`);
|
|
2853
3251
|
if (dataTestIdEntry.pom) {
|
|
@@ -2859,19 +3257,25 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2859
3257
|
name: generatedName,
|
|
2860
3258
|
selector: {
|
|
2861
3259
|
kind: "withinTestIdByLabel",
|
|
2862
|
-
|
|
2863
|
-
|
|
3260
|
+
rootTestId: createPomStringPattern(wrapperTestId, selectorPatternKind),
|
|
3261
|
+
label: createPomStringPattern("${value}", "parameterized"),
|
|
2864
3262
|
exact: true
|
|
2865
3263
|
},
|
|
2866
|
-
|
|
3264
|
+
parameters: [
|
|
3265
|
+
createPomParameterSpec("value", "string"),
|
|
3266
|
+
createPomParameterSpec("annotationText", `string = ""`)
|
|
3267
|
+
]
|
|
2867
3268
|
});
|
|
2868
3269
|
if (added) {
|
|
2869
|
-
registerGeneratedMethodSignature(generatedName,
|
|
3270
|
+
registerGeneratedMethodSignature(generatedName, createPomMethodSignature([
|
|
3271
|
+
createPomParameterSpec("value", "string"),
|
|
3272
|
+
createPomParameterSpec("annotationText", `string = ""`)
|
|
3273
|
+
]));
|
|
2870
3274
|
}
|
|
2871
|
-
return;
|
|
3275
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2872
3276
|
}
|
|
2873
3277
|
const staticKeyValues = args.keyValuesOverride ?? null;
|
|
2874
|
-
const needsKey =
|
|
3278
|
+
const needsKey = hasPomParameter(normalizedParameters, "key") && selectorIsParameterized;
|
|
2875
3279
|
const isNavigation = !!dataTestIdEntry.targetPageObjectModelClass;
|
|
2876
3280
|
if (staticKeyValues && staticKeyValues.length > 0 && needsKey && !isNavigation && normalizedRole !== "input" && normalizedRole !== "select" && normalizedRole !== "vselect" && normalizedRole !== "radio") {
|
|
2877
3281
|
if (dataTestIdEntry.pom) {
|
|
@@ -2890,16 +3294,19 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2890
3294
|
name: generatedName,
|
|
2891
3295
|
selector: {
|
|
2892
3296
|
kind: "testId",
|
|
2893
|
-
|
|
3297
|
+
testId: selectorPattern
|
|
2894
3298
|
},
|
|
2895
3299
|
keyLiteral: rawValue,
|
|
2896
|
-
|
|
3300
|
+
parameters: [createPomParameterSpec("wait", "boolean = true"), createPomParameterSpec("annotationText", 'string = ""')]
|
|
2897
3301
|
});
|
|
2898
3302
|
if (added) {
|
|
2899
|
-
registerGeneratedMethodSignature(generatedName,
|
|
3303
|
+
registerGeneratedMethodSignature(generatedName, createPomMethodSignature([
|
|
3304
|
+
createPomParameterSpec("wait", "boolean = true"),
|
|
3305
|
+
createPomParameterSpec("annotationText", 'string = ""')
|
|
3306
|
+
]));
|
|
2900
3307
|
}
|
|
2901
3308
|
}
|
|
2902
|
-
return;
|
|
3309
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2903
3310
|
}
|
|
2904
3311
|
if (dataTestIdEntry.pom) {
|
|
2905
3312
|
if (dataTestIdEntry.pom.emitPrimary !== false) {
|
|
@@ -2913,6 +3320,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2913
3320
|
const generatedName = getGeneratedMethodName();
|
|
2914
3321
|
registerGeneratedMethodSignature(generatedName, signature);
|
|
2915
3322
|
}
|
|
3323
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2916
3324
|
}
|
|
2917
3325
|
function safeRealpath(value) {
|
|
2918
3326
|
try {
|
|
@@ -3900,113 +4308,6 @@ class VuePomGeneratorError extends Error {
|
|
|
3900
4308
|
this.name = "VuePomGeneratorError";
|
|
3901
4309
|
}
|
|
3902
4310
|
}
|
|
3903
|
-
function splitParameterList(parameters) {
|
|
3904
|
-
const parts = [];
|
|
3905
|
-
let current = "";
|
|
3906
|
-
let braceDepth = 0;
|
|
3907
|
-
let bracketDepth = 0;
|
|
3908
|
-
let parenDepth = 0;
|
|
3909
|
-
let angleDepth = 0;
|
|
3910
|
-
let inSingleQuote = false;
|
|
3911
|
-
let inDoubleQuote = false;
|
|
3912
|
-
let inTemplateString = false;
|
|
3913
|
-
for (let index = 0; index < parameters.length; index += 1) {
|
|
3914
|
-
const char = parameters[index];
|
|
3915
|
-
const previous = index > 0 ? parameters[index - 1] : "";
|
|
3916
|
-
if (char === "'" && !inDoubleQuote && !inTemplateString && previous !== "\\") {
|
|
3917
|
-
inSingleQuote = !inSingleQuote;
|
|
3918
|
-
current += char;
|
|
3919
|
-
continue;
|
|
3920
|
-
}
|
|
3921
|
-
if (char === '"' && !inSingleQuote && !inTemplateString && previous !== "\\") {
|
|
3922
|
-
inDoubleQuote = !inDoubleQuote;
|
|
3923
|
-
current += char;
|
|
3924
|
-
continue;
|
|
3925
|
-
}
|
|
3926
|
-
if (char === "`" && !inSingleQuote && !inDoubleQuote && previous !== "\\") {
|
|
3927
|
-
inTemplateString = !inTemplateString;
|
|
3928
|
-
current += char;
|
|
3929
|
-
continue;
|
|
3930
|
-
}
|
|
3931
|
-
if (inSingleQuote || inDoubleQuote || inTemplateString) {
|
|
3932
|
-
current += char;
|
|
3933
|
-
continue;
|
|
3934
|
-
}
|
|
3935
|
-
switch (char) {
|
|
3936
|
-
case "{":
|
|
3937
|
-
braceDepth += 1;
|
|
3938
|
-
break;
|
|
3939
|
-
case "}":
|
|
3940
|
-
braceDepth -= 1;
|
|
3941
|
-
break;
|
|
3942
|
-
case "[":
|
|
3943
|
-
bracketDepth += 1;
|
|
3944
|
-
break;
|
|
3945
|
-
case "]":
|
|
3946
|
-
bracketDepth -= 1;
|
|
3947
|
-
break;
|
|
3948
|
-
case "(":
|
|
3949
|
-
parenDepth += 1;
|
|
3950
|
-
break;
|
|
3951
|
-
case ")":
|
|
3952
|
-
parenDepth -= 1;
|
|
3953
|
-
break;
|
|
3954
|
-
case "<":
|
|
3955
|
-
angleDepth += 1;
|
|
3956
|
-
break;
|
|
3957
|
-
case ">":
|
|
3958
|
-
angleDepth -= 1;
|
|
3959
|
-
break;
|
|
3960
|
-
case ",":
|
|
3961
|
-
if (braceDepth === 0 && bracketDepth === 0 && parenDepth === 0 && angleDepth === 0) {
|
|
3962
|
-
const trimmed2 = current.trim();
|
|
3963
|
-
if (trimmed2) {
|
|
3964
|
-
parts.push(trimmed2);
|
|
3965
|
-
}
|
|
3966
|
-
current = "";
|
|
3967
|
-
continue;
|
|
3968
|
-
}
|
|
3969
|
-
break;
|
|
3970
|
-
}
|
|
3971
|
-
current += char;
|
|
3972
|
-
}
|
|
3973
|
-
const trimmed = current.trim();
|
|
3974
|
-
if (trimmed) {
|
|
3975
|
-
parts.push(trimmed);
|
|
3976
|
-
}
|
|
3977
|
-
return parts;
|
|
3978
|
-
}
|
|
3979
|
-
function parseParameterSignature(parameter) {
|
|
3980
|
-
const colonIndex = parameter.indexOf(":");
|
|
3981
|
-
if (colonIndex < 0) {
|
|
3982
|
-
return { name: parameter.trim() };
|
|
3983
|
-
}
|
|
3984
|
-
const rawName = parameter.slice(0, colonIndex).trim();
|
|
3985
|
-
const hasQuestionToken = rawName.endsWith("?");
|
|
3986
|
-
const name = hasQuestionToken ? rawName.slice(0, -1).trim() : rawName;
|
|
3987
|
-
const remainder = parameter.slice(colonIndex + 1).trim();
|
|
3988
|
-
const initializerIndex = remainder.lastIndexOf("=");
|
|
3989
|
-
if (initializerIndex < 0) {
|
|
3990
|
-
return {
|
|
3991
|
-
name,
|
|
3992
|
-
hasQuestionToken,
|
|
3993
|
-
type: remainder || void 0
|
|
3994
|
-
};
|
|
3995
|
-
}
|
|
3996
|
-
return {
|
|
3997
|
-
name,
|
|
3998
|
-
hasQuestionToken,
|
|
3999
|
-
type: remainder.slice(0, initializerIndex).trim() || void 0,
|
|
4000
|
-
initializer: remainder.slice(initializerIndex + 1).trim() || void 0
|
|
4001
|
-
};
|
|
4002
|
-
}
|
|
4003
|
-
function parseParameterSignatures(parameters) {
|
|
4004
|
-
const trimmed = parameters.trim();
|
|
4005
|
-
if (!trimmed) {
|
|
4006
|
-
return [];
|
|
4007
|
-
}
|
|
4008
|
-
return splitParameterList(trimmed).map(parseParameterSignature);
|
|
4009
|
-
}
|
|
4010
4311
|
function toPosixRelativePath(fromDir, toFile) {
|
|
4011
4312
|
let rel = path.relative(fromDir, toFile).replace(/\\/g, "/");
|
|
4012
4313
|
if (!rel.startsWith(".")) {
|
|
@@ -4026,6 +4327,21 @@ function resolveRouterEntry(projectRoot, routerEntry) {
|
|
|
4026
4327
|
const root = projectRoot ?? process.cwd();
|
|
4027
4328
|
return path.isAbsolute(routerEntry) ? routerEntry : path.resolve(root, routerEntry);
|
|
4028
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
|
+
}
|
|
4029
4345
|
function createCustomPomImportCollisionError(exportName, requested) {
|
|
4030
4346
|
return new VuePomGeneratorError(
|
|
4031
4347
|
`Custom POM import name collision detected for "${exportName}".
|
|
@@ -4164,35 +4480,28 @@ function generateGoToSelfMethod(componentName) {
|
|
|
4164
4480
|
})
|
|
4165
4481
|
];
|
|
4166
4482
|
}
|
|
4167
|
-
function
|
|
4168
|
-
|
|
4169
|
-
return "";
|
|
4170
|
-
const preferredOrder = ["key", "value", "text", "timeOut", "annotationText", "wait"];
|
|
4171
|
-
const entries = Object.entries(params);
|
|
4172
|
-
if (!entries.length)
|
|
4173
|
-
return "";
|
|
4174
|
-
const score = (name) => {
|
|
4175
|
-
const idx = preferredOrder.indexOf(name);
|
|
4176
|
-
return idx < 0 ? 999 : idx;
|
|
4177
|
-
};
|
|
4178
|
-
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];
|
|
4179
4485
|
}
|
|
4180
4486
|
function generateExtraClickMethodMembers(spec) {
|
|
4181
4487
|
if (spec.kind !== "click") {
|
|
4182
4488
|
return [];
|
|
4183
4489
|
}
|
|
4184
|
-
const
|
|
4185
|
-
const
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
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");
|
|
4189
4499
|
const annotationArg = hasAnnotationText ? "annotationText" : '""';
|
|
4190
4500
|
const waitArg = hasWait ? "wait" : "true";
|
|
4191
4501
|
if (spec.selector.kind === "testId") {
|
|
4192
|
-
const
|
|
4193
|
-
const testIdExpr = needsTemplate ? `\`${spec.selector.formattedDataTestId}\`` : JSON.stringify(spec.selector.formattedDataTestId);
|
|
4502
|
+
const testIdBinding = bindTypeScriptPomPattern(spec.selector.testId, "testId");
|
|
4194
4503
|
const clickArgs = [];
|
|
4195
|
-
clickArgs.push(
|
|
4504
|
+
clickArgs.push(testIdBinding.expression);
|
|
4196
4505
|
if (hasAnnotationText || hasWait) {
|
|
4197
4506
|
clickArgs.push(annotationArg);
|
|
4198
4507
|
}
|
|
@@ -4208,20 +4517,16 @@ function generateExtraClickMethodMembers(spec) {
|
|
|
4208
4517
|
if (spec.keyLiteral !== void 0) {
|
|
4209
4518
|
writer.writeLine(`const key = ${JSON.stringify(spec.keyLiteral)};`);
|
|
4210
4519
|
}
|
|
4211
|
-
|
|
4212
|
-
writer.writeLine(
|
|
4520
|
+
for (const statement of testIdBinding.setupStatements) {
|
|
4521
|
+
writer.writeLine(statement);
|
|
4213
4522
|
}
|
|
4214
4523
|
writer.writeLine(`await this.clickByTestId(${clickArgs.join(", ")});`);
|
|
4215
4524
|
}
|
|
4216
4525
|
})
|
|
4217
4526
|
];
|
|
4218
4527
|
}
|
|
4219
|
-
const
|
|
4220
|
-
const
|
|
4221
|
-
const rootExpr = rootNeedsTemplate ? `\`${spec.selector.rootFormattedDataTestId}\`` : JSON.stringify(spec.selector.rootFormattedDataTestId);
|
|
4222
|
-
const labelExpr = labelNeedsTemplate ? `\`${spec.selector.formattedLabel}\`` : JSON.stringify(spec.selector.formattedLabel);
|
|
4223
|
-
const rootArg = rootNeedsTemplate ? "rootTestId" : rootExpr;
|
|
4224
|
-
const labelArg = labelNeedsTemplate ? "label" : labelExpr;
|
|
4528
|
+
const rootBinding = bindTypeScriptPomPattern(spec.selector.rootTestId, "rootTestId");
|
|
4529
|
+
const labelBinding = bindTypeScriptPomPattern(spec.selector.label, "label");
|
|
4225
4530
|
return [
|
|
4226
4531
|
createClassMethod({
|
|
4227
4532
|
name: spec.name,
|
|
@@ -4231,13 +4536,13 @@ function generateExtraClickMethodMembers(spec) {
|
|
|
4231
4536
|
if (spec.keyLiteral !== void 0) {
|
|
4232
4537
|
writer.writeLine(`const key = ${JSON.stringify(spec.keyLiteral)};`);
|
|
4233
4538
|
}
|
|
4234
|
-
|
|
4235
|
-
writer.writeLine(
|
|
4539
|
+
for (const statement of rootBinding.setupStatements) {
|
|
4540
|
+
writer.writeLine(statement);
|
|
4236
4541
|
}
|
|
4237
|
-
|
|
4238
|
-
writer.writeLine(
|
|
4542
|
+
for (const statement of labelBinding.setupStatements) {
|
|
4543
|
+
writer.writeLine(statement);
|
|
4239
4544
|
}
|
|
4240
|
-
writer.writeLine(`await this.clickWithinTestIdByLabel(${
|
|
4545
|
+
writer.writeLine(`await this.clickWithinTestIdByLabel(${rootBinding.expression}, ${labelBinding.expression}, ${annotationArg}, ${waitArg});`);
|
|
4241
4546
|
}
|
|
4242
4547
|
})
|
|
4243
4548
|
];
|
|
@@ -4250,10 +4555,10 @@ function generateMethodMembersFromPom(primary, targetPageObjectModelClass) {
|
|
|
4250
4555
|
targetPageObjectModelClass,
|
|
4251
4556
|
primary.methodName,
|
|
4252
4557
|
primary.nativeRole,
|
|
4253
|
-
primary.
|
|
4254
|
-
primary.
|
|
4558
|
+
primary.selector,
|
|
4559
|
+
primary.alternateSelectors,
|
|
4255
4560
|
primary.getterNameOverride,
|
|
4256
|
-
primary.
|
|
4561
|
+
primary.parameters
|
|
4257
4562
|
);
|
|
4258
4563
|
}
|
|
4259
4564
|
function generateMethodsContentForDependencies(dependencies) {
|
|
@@ -4261,15 +4566,14 @@ function generateMethodsContentForDependencies(dependencies) {
|
|
|
4261
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));
|
|
4262
4567
|
const seenPrimaryKeys = /* @__PURE__ */ new Set();
|
|
4263
4568
|
const primarySpecs = primarySpecsAll.filter(({ pom, target }) => {
|
|
4264
|
-
const
|
|
4265
|
-
const alternates = (pom.alternateFormattedDataTestIds ?? []).slice().sort();
|
|
4569
|
+
const alternates = (pom.alternateSelectors ?? []).slice().sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
4266
4570
|
const key = JSON.stringify({
|
|
4267
4571
|
role: pom.nativeRole,
|
|
4268
4572
|
methodName: pom.methodName,
|
|
4269
4573
|
getterNameOverride: pom.getterNameOverride ?? null,
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4574
|
+
selector: pom.selector,
|
|
4575
|
+
alternateSelectors: alternates.length ? alternates : void 0,
|
|
4576
|
+
parameters: pom.parameters,
|
|
4273
4577
|
target: target ?? null,
|
|
4274
4578
|
emitPrimary: pom.emitPrimary ?? true
|
|
4275
4579
|
});
|
|
@@ -4296,6 +4600,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
|
|
|
4296
4600
|
customPomAttachments = [],
|
|
4297
4601
|
projectRoot,
|
|
4298
4602
|
customPomDir,
|
|
4603
|
+
requireCustomPomDir,
|
|
4299
4604
|
customPomImportAliases,
|
|
4300
4605
|
customPomImportNameCollisionBehavior = "error",
|
|
4301
4606
|
testIdAttribute,
|
|
@@ -4328,6 +4633,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
|
|
|
4328
4633
|
customPomAttachments,
|
|
4329
4634
|
projectRoot,
|
|
4330
4635
|
customPomDir,
|
|
4636
|
+
requireCustomPomDir,
|
|
4331
4637
|
customPomImportAliases,
|
|
4332
4638
|
customPomImportNameCollisionBehavior,
|
|
4333
4639
|
testIdAttribute,
|
|
@@ -4337,6 +4643,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
|
|
|
4337
4643
|
customPomAttachments,
|
|
4338
4644
|
projectRoot,
|
|
4339
4645
|
customPomDir,
|
|
4646
|
+
requireCustomPomDir,
|
|
4340
4647
|
customPomImportAliases,
|
|
4341
4648
|
customPomImportNameCollisionBehavior,
|
|
4342
4649
|
testIdAttribute,
|
|
@@ -4392,9 +4699,15 @@ async function generateSplitTypeScriptFiles(componentHierarchyMap, vueFilesPathM
|
|
|
4392
4699
|
}
|
|
4393
4700
|
const customPomImportResolution = resolveCustomPomImportResolution(generatedClassNames, projectRoot, {
|
|
4394
4701
|
customPomDir: options.customPomDir,
|
|
4702
|
+
requireCustomPomDir: options.requireCustomPomDir,
|
|
4395
4703
|
customPomImportAliases: options.customPomImportAliases,
|
|
4396
4704
|
customPomImportNameCollisionBehavior: options.customPomImportNameCollisionBehavior
|
|
4397
4705
|
});
|
|
4706
|
+
assertCustomPomAttachmentsResolved(
|
|
4707
|
+
options.customPomAttachments ?? [],
|
|
4708
|
+
customPomImportResolution.classIdentifierMap,
|
|
4709
|
+
options.customPomDir ?? "tests/playwright/pom/custom"
|
|
4710
|
+
);
|
|
4398
4711
|
const runtimeBasePagePath = path.join(base, "_pom-runtime", "class-generation", "base-page.ts");
|
|
4399
4712
|
const files = [];
|
|
4400
4713
|
for (const [name, deps] of entries) {
|
|
@@ -4553,21 +4866,8 @@ function buildGeneratedGitAttributesFiles(generatedFilePaths) {
|
|
|
4553
4866
|
return { filePath, content };
|
|
4554
4867
|
});
|
|
4555
4868
|
}
|
|
4556
|
-
function
|
|
4557
|
-
const
|
|
4558
|
-
if (!needsInterpolation) {
|
|
4559
|
-
return JSON.stringify(formattedDataTestId);
|
|
4560
|
-
}
|
|
4561
|
-
const inner = formattedDataTestId.replace(/\$\{/g, "{");
|
|
4562
|
-
const quoted = JSON.stringify(inner);
|
|
4563
|
-
return `$${quoted}`;
|
|
4564
|
-
}
|
|
4565
|
-
function toCSharpParam(paramTypeExpr) {
|
|
4566
|
-
const trimmed = (paramTypeExpr ?? "").trim();
|
|
4567
|
-
const eqIdx = trimmed.indexOf("=");
|
|
4568
|
-
const left = eqIdx >= 0 ? trimmed.slice(0, eqIdx).trim() : trimmed;
|
|
4569
|
-
const right = eqIdx >= 0 ? trimmed.slice(eqIdx + 1).trim() : void 0;
|
|
4570
|
-
const typePart = left.includes("|") ? "string" : left;
|
|
4869
|
+
function toCSharpParam(param) {
|
|
4870
|
+
const typePart = param.type?.includes("|") ? "string" : param.type ?? "string";
|
|
4571
4871
|
let type = "string";
|
|
4572
4872
|
if (/(?:^|\s)boolean(?:\s|$)/.test(typePart))
|
|
4573
4873
|
type = "bool";
|
|
@@ -4575,19 +4875,17 @@ function toCSharpParam(paramTypeExpr) {
|
|
|
4575
4875
|
type = "string";
|
|
4576
4876
|
else if (/(?:^|\s)number(?:\s|$)/.test(typePart))
|
|
4577
4877
|
type = "int";
|
|
4578
|
-
else if (/\d+/.test(typePart) && typePart === "")
|
|
4579
|
-
type = "int";
|
|
4580
4878
|
else if (/\btimeOut\b/i.test(typePart))
|
|
4581
4879
|
type = "int";
|
|
4582
4880
|
let defaultExpr;
|
|
4583
|
-
if (
|
|
4881
|
+
if (param.initializer !== void 0) {
|
|
4584
4882
|
if (type === "bool") {
|
|
4585
|
-
defaultExpr =
|
|
4883
|
+
defaultExpr = param.initializer.includes("true") ? "true" : param.initializer.includes("false") ? "false" : void 0;
|
|
4586
4884
|
} else if (type === "int") {
|
|
4587
|
-
const m =
|
|
4885
|
+
const m = param.initializer.match(/\d+/);
|
|
4588
4886
|
defaultExpr = m ? m[0] : void 0;
|
|
4589
4887
|
} else {
|
|
4590
|
-
if (
|
|
4888
|
+
if (param.initializer === '""' || param.initializer === "''") {
|
|
4591
4889
|
defaultExpr = '""';
|
|
4592
4890
|
}
|
|
4593
4891
|
}
|
|
@@ -4595,17 +4893,15 @@ function toCSharpParam(paramTypeExpr) {
|
|
|
4595
4893
|
return { type, defaultExpr };
|
|
4596
4894
|
}
|
|
4597
4895
|
function formatCSharpParams(params) {
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
const entries = Object.entries(params);
|
|
4601
|
-
if (!entries.length)
|
|
4896
|
+
const normalizedParams = normalizePomParameters(params);
|
|
4897
|
+
if (!normalizedParams.length)
|
|
4602
4898
|
return { signature: "", argNames: [] };
|
|
4603
4899
|
const signatureParts = [];
|
|
4604
4900
|
const argNames = [];
|
|
4605
|
-
for (const
|
|
4606
|
-
const { type, defaultExpr } = toCSharpParam(
|
|
4607
|
-
argNames.push(name);
|
|
4608
|
-
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}`);
|
|
4609
4905
|
}
|
|
4610
4906
|
return { signature: signatureParts.join(", "), argNames };
|
|
4611
4907
|
}
|
|
@@ -4696,23 +4992,13 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4696
4992
|
const baseMethodName = upperFirst(pom.methodName);
|
|
4697
4993
|
const baseGetterName = upperFirst(pom.getterNameOverride ?? pom.methodName);
|
|
4698
4994
|
const locatorName = baseGetterName.endsWith(roleSuffix) ? baseGetterName : `${baseGetterName}${roleSuffix}`;
|
|
4699
|
-
const
|
|
4700
|
-
const
|
|
4701
|
-
const
|
|
4702
|
-
const augmentedParams = { ...pom.params };
|
|
4703
|
-
for (const v of templateVars) {
|
|
4704
|
-
if (!Object.prototype.hasOwnProperty.call(augmentedParams, v)) {
|
|
4705
|
-
augmentedParams[v] = "string";
|
|
4706
|
-
}
|
|
4707
|
-
}
|
|
4708
|
-
const orderedParams = Object.fromEntries([
|
|
4709
|
-
...templateVars.map((v) => [v, augmentedParams[v]]),
|
|
4710
|
-
...Object.entries(augmentedParams).filter(([k]) => !templateVars.includes(k))
|
|
4711
|
-
]);
|
|
4995
|
+
const selectorIsParameterized = isParameterizedPomPattern(pom.selector.patternKind);
|
|
4996
|
+
const testIdExpr = toCSharpPomPatternExpression(pom.selector);
|
|
4997
|
+
const orderedParams = orderPomPatternParameters(pom.parameters, [pom.selector]);
|
|
4712
4998
|
const { signature, argNames } = formatCSharpParams(orderedParams);
|
|
4713
4999
|
const args = argNames.join(", ");
|
|
4714
|
-
const allTestIds =
|
|
4715
|
-
if (
|
|
5000
|
+
const allTestIds = uniquePomStringPatterns(pom.selector, pom.alternateSelectors);
|
|
5001
|
+
if (selectorIsParameterized) {
|
|
4716
5002
|
chunks.push(` public ILocator ${locatorName}(${signature}) => LocatorByTestId(${testIdExpr});`);
|
|
4717
5003
|
} else {
|
|
4718
5004
|
chunks.push(` public ILocator ${locatorName} => LocatorByTestId(${testIdExpr});`);
|
|
@@ -4723,12 +5009,12 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4723
5009
|
if (target) {
|
|
4724
5010
|
chunks.push(` public async Task<${target}> ${actionName}(${sig})`);
|
|
4725
5011
|
chunks.push(" {");
|
|
4726
|
-
if (
|
|
4727
|
-
chunks.push(` await ${locatorName}${
|
|
5012
|
+
if (selectorIsParameterized || allTestIds.length <= 1) {
|
|
5013
|
+
chunks.push(` await ${locatorName}${selectorIsParameterized ? `(${args})` : ""}.ClickAsync();`);
|
|
4728
5014
|
chunks.push(` return new ${target}(Page);`);
|
|
4729
5015
|
} else {
|
|
4730
5016
|
chunks.push(" Exception? lastError = null;");
|
|
4731
|
-
chunks.push(` foreach (var testId in new[] { ${allTestIds.map(
|
|
5017
|
+
chunks.push(` foreach (var testId in new[] { ${allTestIds.map((testId) => toCSharpPomPatternExpression(testId)).join(", ")} })`);
|
|
4732
5018
|
chunks.push(" {");
|
|
4733
5019
|
chunks.push(" try");
|
|
4734
5020
|
chunks.push(" {");
|
|
@@ -4752,7 +5038,7 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4752
5038
|
}
|
|
4753
5039
|
chunks.push(` public async Task ${actionName}(${sig})`);
|
|
4754
5040
|
chunks.push(" {");
|
|
4755
|
-
const callSuffix =
|
|
5041
|
+
const callSuffix = selectorIsParameterized ? `(${args})` : "";
|
|
4756
5042
|
const emitActionCall = (locatorAccess) => {
|
|
4757
5043
|
if (pom.nativeRole === "input") {
|
|
4758
5044
|
chunks.push(` var editableLocator = await ResolveEditableLocatorAsync(${locatorAccess});`);
|
|
@@ -4765,9 +5051,9 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4765
5051
|
chunks.push(` await ${locatorAccess}.ClickAsync();`);
|
|
4766
5052
|
}
|
|
4767
5053
|
};
|
|
4768
|
-
if (!
|
|
5054
|
+
if (!selectorIsParameterized && allTestIds.length > 1) {
|
|
4769
5055
|
chunks.push(" Exception? lastError = null;");
|
|
4770
|
-
chunks.push(` foreach (var testId in new[] { ${allTestIds.map(
|
|
5056
|
+
chunks.push(` foreach (var testId in new[] { ${allTestIds.map((testId) => toCSharpPomPatternExpression(testId)).join(", ")} })`);
|
|
4771
5057
|
chunks.push(" {");
|
|
4772
5058
|
chunks.push(" try");
|
|
4773
5059
|
chunks.push(" {");
|
|
@@ -4813,7 +5099,12 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4813
5099
|
for (const extra of extras) {
|
|
4814
5100
|
if (extra.kind !== "click")
|
|
4815
5101
|
continue;
|
|
4816
|
-
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);
|
|
4817
5108
|
const extraName = upperFirst(extra.name);
|
|
4818
5109
|
chunks.push(` public async Task ${extraName}Async(${signature})`);
|
|
4819
5110
|
chunks.push(" {");
|
|
@@ -4821,29 +5112,22 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4821
5112
|
chunks.push(` var key = ${JSON.stringify(extra.keyLiteral)};`);
|
|
4822
5113
|
}
|
|
4823
5114
|
if (extra.selector.kind === "testId") {
|
|
4824
|
-
const
|
|
4825
|
-
const
|
|
4826
|
-
|
|
4827
|
-
chunks.push(` var testId = ${testIdExpr};`);
|
|
4828
|
-
chunks.push(" await LocatorByTestId(testId).ClickAsync();");
|
|
4829
|
-
} else {
|
|
4830
|
-
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}`);
|
|
4831
5118
|
}
|
|
5119
|
+
chunks.push(` await LocatorByTestId(${testIdBinding.expression}).ClickAsync();`);
|
|
4832
5120
|
} else {
|
|
4833
|
-
const
|
|
4834
|
-
const
|
|
4835
|
-
const rootExpr = toCSharpTestIdExpression(extra.selector.rootFormattedDataTestId);
|
|
4836
|
-
const labelExpr = toCSharpTestIdExpression(extra.selector.formattedLabel);
|
|
5121
|
+
const rootBinding = bindCSharpPomPattern(extra.selector.rootTestId, "rootTestId");
|
|
5122
|
+
const labelBinding = bindCSharpPomPattern(extra.selector.label, "label");
|
|
4837
5123
|
const exactArg = extra.selector.exact === false ? "false" : "true";
|
|
4838
|
-
|
|
4839
|
-
chunks.push(`
|
|
5124
|
+
for (const statement of rootBinding.setupStatements) {
|
|
5125
|
+
chunks.push(` ${statement}`);
|
|
4840
5126
|
}
|
|
4841
|
-
|
|
4842
|
-
chunks.push(`
|
|
5127
|
+
for (const statement of labelBinding.setupStatements) {
|
|
5128
|
+
chunks.push(` ${statement}`);
|
|
4843
5129
|
}
|
|
4844
|
-
|
|
4845
|
-
const labelArg = labelNeedsTemplate ? "label" : labelExpr;
|
|
4846
|
-
chunks.push(` await ClickWithinTestIdByLabelAsync(${rootArg}, ${labelArg}, ${exactArg});`);
|
|
5130
|
+
chunks.push(` await ClickWithinTestIdByLabelAsync(${rootBinding.expression}, ${labelBinding.expression}, ${exactArg});`);
|
|
4847
5131
|
}
|
|
4848
5132
|
chunks.push(" }");
|
|
4849
5133
|
chunks.push("");
|
|
@@ -5287,7 +5571,7 @@ function getViewPassthroughMethods(viewName, viewDependencies, childrenComponent
|
|
|
5287
5571
|
if (existingOnView.has(name) || blockedMethodNames.has(name))
|
|
5288
5572
|
continue;
|
|
5289
5573
|
const list = methodToChildren.get(name) ?? [];
|
|
5290
|
-
list.push({ childProp,
|
|
5574
|
+
list.push({ childProp, signature: sig });
|
|
5291
5575
|
methodToChildren.set(name, list);
|
|
5292
5576
|
}
|
|
5293
5577
|
}
|
|
@@ -5297,12 +5581,12 @@ function getViewPassthroughMethods(viewName, viewDependencies, childrenComponent
|
|
|
5297
5581
|
return [];
|
|
5298
5582
|
}
|
|
5299
5583
|
return passthroughs.map(([methodName, candidates]) => {
|
|
5300
|
-
const { childProp,
|
|
5301
|
-
const callArgs =
|
|
5584
|
+
const { childProp, signature } = candidates[0];
|
|
5585
|
+
const callArgs = getPomParameterArgumentNames(signature.parameters).join(", ");
|
|
5302
5586
|
return createClassMethod({
|
|
5303
5587
|
name: methodName,
|
|
5304
5588
|
isAsync: true,
|
|
5305
|
-
parameters:
|
|
5589
|
+
parameters: toTypeScriptPomParameterStructures(signature.parameters),
|
|
5306
5590
|
statements: [
|
|
5307
5591
|
`return await this.${childProp}.${methodName}(${callArgs});`
|
|
5308
5592
|
]
|
|
@@ -5326,8 +5610,7 @@ function getAttachmentPassthroughMethods(ownerName, ownerDependencies, attachmen
|
|
|
5326
5610
|
const list = methodToAttachments.get(methodName) ?? [];
|
|
5327
5611
|
list.push({
|
|
5328
5612
|
propertyName: attachment.propertyName,
|
|
5329
|
-
|
|
5330
|
-
argNames: signature.argNames
|
|
5613
|
+
signature
|
|
5331
5614
|
});
|
|
5332
5615
|
methodToAttachments.set(methodName, list);
|
|
5333
5616
|
}
|
|
@@ -5338,12 +5621,12 @@ function getAttachmentPassthroughMethods(ownerName, ownerDependencies, attachmen
|
|
|
5338
5621
|
return [];
|
|
5339
5622
|
}
|
|
5340
5623
|
return passthroughs.map(([methodName, candidates]) => {
|
|
5341
|
-
const { propertyName,
|
|
5342
|
-
const callArgs =
|
|
5624
|
+
const { propertyName, signature } = candidates[0];
|
|
5625
|
+
const callArgs = getPomParameterArgumentNames(signature.parameters).join(", ");
|
|
5343
5626
|
const invocation = callArgs ? `this.${propertyName}.${methodName}(${callArgs})` : `this.${propertyName}.${methodName}()`;
|
|
5344
5627
|
return createClassMethod({
|
|
5345
5628
|
name: methodName,
|
|
5346
|
-
parameters:
|
|
5629
|
+
parameters: toTypeScriptPomParameterStructures(signature.parameters),
|
|
5347
5630
|
statements: [
|
|
5348
5631
|
`return ${invocation};`
|
|
5349
5632
|
]
|
|
@@ -5357,15 +5640,44 @@ function sliceNodeSource(source, node) {
|
|
|
5357
5640
|
const snippet = source.slice(node.start, node.end).trim();
|
|
5358
5641
|
return snippet.length ? snippet : null;
|
|
5359
5642
|
}
|
|
5360
|
-
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) {
|
|
5361
5652
|
if (param.type === "Identifier") {
|
|
5362
|
-
return param.name
|
|
5653
|
+
return createPomParameterSpec(param.name, getTypeAnnotationSource(source, param), {
|
|
5654
|
+
hasQuestionToken: !!param.optional
|
|
5655
|
+
});
|
|
5363
5656
|
}
|
|
5364
5657
|
if (param.type === "AssignmentPattern") {
|
|
5365
|
-
|
|
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
|
+
});
|
|
5366
5669
|
}
|
|
5367
5670
|
if (param.type === "RestElement") {
|
|
5368
|
-
|
|
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
|
+
});
|
|
5369
5681
|
}
|
|
5370
5682
|
return null;
|
|
5371
5683
|
}
|
|
@@ -5398,29 +5710,23 @@ function extractCustomPomMethodSignatures(source, exportName) {
|
|
|
5398
5710
|
if (member.key.type !== "Identifier") {
|
|
5399
5711
|
continue;
|
|
5400
5712
|
}
|
|
5401
|
-
const
|
|
5402
|
-
const argNames = [];
|
|
5713
|
+
const parameters = [];
|
|
5403
5714
|
let supported = true;
|
|
5404
5715
|
member.params.forEach((param) => {
|
|
5405
5716
|
if (!supported) {
|
|
5406
5717
|
return;
|
|
5407
5718
|
}
|
|
5408
|
-
const
|
|
5409
|
-
|
|
5410
|
-
if (!paramSource || !argName) {
|
|
5719
|
+
const parameter = getCustomPomParameterSpec(source, param);
|
|
5720
|
+
if (!parameter) {
|
|
5411
5721
|
supported = false;
|
|
5412
5722
|
return;
|
|
5413
5723
|
}
|
|
5414
|
-
|
|
5415
|
-
argNames.push(argName);
|
|
5724
|
+
parameters.push(parameter);
|
|
5416
5725
|
});
|
|
5417
5726
|
if (!supported) {
|
|
5418
5727
|
continue;
|
|
5419
5728
|
}
|
|
5420
|
-
signatures.set(member.key.name,
|
|
5421
|
-
params: params.join(", "),
|
|
5422
|
-
argNames
|
|
5423
|
-
});
|
|
5729
|
+
signatures.set(member.key.name, createPomMethodSignature(parameters));
|
|
5424
5730
|
}
|
|
5425
5731
|
}
|
|
5426
5732
|
return signatures;
|
|
@@ -5603,6 +5909,9 @@ function resolveCustomPomImportResolution(generatedClassNames, projectRoot, opti
|
|
|
5603
5909
|
const customDirRelOrAbs = options.customPomDir ?? "tests/playwright/pom/custom";
|
|
5604
5910
|
const customDirAbs = path.isAbsolute(customDirRelOrAbs) ? customDirRelOrAbs : path.resolve(projectRoot, customDirRelOrAbs);
|
|
5605
5911
|
if (!fs.existsSync(customDirAbs)) {
|
|
5912
|
+
if (options.requireCustomPomDir) {
|
|
5913
|
+
throw createMissingCustomPomDirectoryError(customDirRelOrAbs, customDirAbs);
|
|
5914
|
+
}
|
|
5606
5915
|
return {
|
|
5607
5916
|
classIdentifierMap,
|
|
5608
5917
|
methodSignaturesByClass,
|
|
@@ -5645,6 +5954,14 @@ function resolveCustomPomImportResolution(generatedClassNames, projectRoot, opti
|
|
|
5645
5954
|
importSpecifiersByClass
|
|
5646
5955
|
};
|
|
5647
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
|
+
}
|
|
5648
5965
|
function getComposedStubBody(targetClassName, availableClassNames, depsByClassName, vueFilesPathMap, projectRoot) {
|
|
5649
5966
|
const filePath = resolveVueSourcePath(targetClassName, vueFilesPathMap, projectRoot);
|
|
5650
5967
|
if (!filePath)
|
|
@@ -5673,7 +5990,7 @@ function getComposedStubBody(targetClassName, availableClassNames, depsByClassNa
|
|
|
5673
5990
|
if (!sig)
|
|
5674
5991
|
continue;
|
|
5675
5992
|
const list = methodToChildren.get(name) ?? [];
|
|
5676
|
-
list.push({ child,
|
|
5993
|
+
list.push({ child, signature: sig });
|
|
5677
5994
|
methodToChildren.set(name, list);
|
|
5678
5995
|
}
|
|
5679
5996
|
}
|
|
@@ -5681,12 +5998,12 @@ function getComposedStubBody(targetClassName, availableClassNames, depsByClassNa
|
|
|
5681
5998
|
for (const [methodName, candidatesForMethod] of methodToChildren.entries()) {
|
|
5682
5999
|
if (candidatesForMethod.length !== 1 || methodName === "constructor")
|
|
5683
6000
|
continue;
|
|
5684
|
-
const { child,
|
|
5685
|
-
const callArgs =
|
|
6001
|
+
const { child, signature } = candidatesForMethod[0];
|
|
6002
|
+
const callArgs = getPomParameterArgumentNames(signature.parameters).join(", ");
|
|
5686
6003
|
passthroughMembers.push(createClassMethod({
|
|
5687
6004
|
name: methodName,
|
|
5688
6005
|
isAsync: true,
|
|
5689
|
-
parameters:
|
|
6006
|
+
parameters: toTypeScriptPomParameterStructures(signature.parameters),
|
|
5690
6007
|
statements: [
|
|
5691
6008
|
`return await this.${child}.${methodName}(${callArgs});`
|
|
5692
6009
|
]
|
|
@@ -5735,9 +6052,15 @@ async function generateAggregatedFiles(componentHierarchyMap, vueFilesPathMap, b
|
|
|
5735
6052
|
imports.push(`export * from "${runtimeClassGenRel}/base-page";`);
|
|
5736
6053
|
const customPomImportResolution = resolveCustomPomImportResolution(generatedClassNames, projectRoot, {
|
|
5737
6054
|
customPomDir: options.customPomDir,
|
|
6055
|
+
requireCustomPomDir: options.requireCustomPomDir,
|
|
5738
6056
|
customPomImportAliases: options.customPomImportAliases,
|
|
5739
6057
|
customPomImportNameCollisionBehavior: options.customPomImportNameCollisionBehavior
|
|
5740
6058
|
});
|
|
6059
|
+
assertCustomPomAttachmentsResolved(
|
|
6060
|
+
options.customPomAttachments ?? [],
|
|
6061
|
+
customPomImportResolution.classIdentifierMap,
|
|
6062
|
+
options.customPomDir ?? "tests/playwright/pom/custom"
|
|
6063
|
+
);
|
|
5741
6064
|
const customPomClassIdentifierMap = customPomImportResolution.classIdentifierMap;
|
|
5742
6065
|
const customPomMethodSignaturesByClass = customPomImportResolution.methodSignaturesByClass;
|
|
5743
6066
|
const customPomAvailableClassIdentifiers = customPomImportResolution.availableClassIdentifiers;
|
|
@@ -5887,8 +6210,8 @@ function getWidgetInstancesForView(componentName, dataTestIdSet, availableClassI
|
|
|
5887
6210
|
return candidate;
|
|
5888
6211
|
};
|
|
5889
6212
|
for (const dt of dataTestIdSet) {
|
|
5890
|
-
const raw = dt.
|
|
5891
|
-
if (
|
|
6213
|
+
const raw = dt.selectorValue.formatted;
|
|
6214
|
+
if (isParameterizedPomPattern(dt.selectorValue.patternKind)) {
|
|
5892
6215
|
continue;
|
|
5893
6216
|
}
|
|
5894
6217
|
const toggleSuffix = "-toggle";
|
|
@@ -5978,7 +6301,6 @@ function getConstructor(childrenComponent, componentHierarchyMap, attachmentsFor
|
|
|
5978
6301
|
});
|
|
5979
6302
|
}
|
|
5980
6303
|
const TESTID_CLICK_EVENT_NAME = "__testid_event__";
|
|
5981
|
-
const TESTID_CLICK_EVENT_STRICT_FLAG = "__testid_click_event_strict__";
|
|
5982
6304
|
const CLICK_EVENT_NAME = TESTID_CLICK_EVENT_NAME;
|
|
5983
6305
|
const inferredNativeWrapperConfigByLookup = /* @__PURE__ */ new Map();
|
|
5984
6306
|
const inferredSfcPathByLookup = /* @__PURE__ */ new Map();
|
|
@@ -6240,7 +6562,7 @@ function getConditionalDirectiveInfo(element) {
|
|
|
6240
6562
|
if (directive.name === "else") {
|
|
6241
6563
|
const exp2 = directive.exp;
|
|
6242
6564
|
if (exp2 && (exp2.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION || exp2.type === compilerCore.NodeTypes.COMPOUND_EXPRESSION)) {
|
|
6243
|
-
const source2 = (exp2
|
|
6565
|
+
const source2 = getVueExpressionSource(exp2, "content", "compiled");
|
|
6244
6566
|
return { kind: "else-if", source: source2 };
|
|
6245
6567
|
}
|
|
6246
6568
|
return { kind: "else", source: "" };
|
|
@@ -6249,13 +6571,13 @@ function getConditionalDirectiveInfo(element) {
|
|
|
6249
6571
|
const exp2 = directive.exp;
|
|
6250
6572
|
if (!exp2 || exp2.type !== compilerCore.NodeTypes.SIMPLE_EXPRESSION && exp2.type !== compilerCore.NodeTypes.COMPOUND_EXPRESSION)
|
|
6251
6573
|
return null;
|
|
6252
|
-
const source2 = (exp2
|
|
6574
|
+
const source2 = getVueExpressionSource(exp2, "content", "compiled");
|
|
6253
6575
|
return { kind: "else-if", source: source2 };
|
|
6254
6576
|
}
|
|
6255
6577
|
const exp = directive.exp;
|
|
6256
6578
|
if (!exp || exp.type !== compilerCore.NodeTypes.SIMPLE_EXPRESSION && exp.type !== compilerCore.NodeTypes.COMPOUND_EXPRESSION)
|
|
6257
6579
|
return null;
|
|
6258
|
-
const source = (exp
|
|
6580
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
6259
6581
|
return { kind: directive.name, source };
|
|
6260
6582
|
}
|
|
6261
6583
|
function tryExtractStableHintFromConditionalExpressionSource(source) {
|
|
@@ -6281,26 +6603,26 @@ function tryExtractStableHintFromConditionalExpressionSource(source) {
|
|
|
6281
6603
|
};
|
|
6282
6604
|
try {
|
|
6283
6605
|
const expr = parser.parseExpression(src, { plugins: ["typescript"] });
|
|
6284
|
-
const
|
|
6606
|
+
const isNodeType2 = (n, type) => {
|
|
6285
6607
|
return n !== null && n.type === type;
|
|
6286
6608
|
};
|
|
6287
|
-
const
|
|
6288
|
-
return
|
|
6609
|
+
const isStringLiteralNode2 = (n) => {
|
|
6610
|
+
return isNodeType2(n, "StringLiteral") && typeof n.value === "string";
|
|
6289
6611
|
};
|
|
6290
|
-
const
|
|
6291
|
-
return
|
|
6612
|
+
const isIdentifierNode2 = (n) => {
|
|
6613
|
+
return isNodeType2(n, "Identifier") && typeof n.name === "string";
|
|
6292
6614
|
};
|
|
6293
6615
|
const results = [];
|
|
6294
6616
|
const walk = (n) => {
|
|
6295
6617
|
if (!n)
|
|
6296
6618
|
return;
|
|
6297
|
-
if (
|
|
6619
|
+
if (isStringLiteralNode2(n)) {
|
|
6298
6620
|
const v = (n.value ?? "").trim();
|
|
6299
6621
|
if (isIdentifierish(v)) {
|
|
6300
6622
|
results.push(v);
|
|
6301
6623
|
}
|
|
6302
6624
|
}
|
|
6303
|
-
if (
|
|
6625
|
+
if (isIdentifierNode2(n)) {
|
|
6304
6626
|
const v = (n.name ?? "").trim();
|
|
6305
6627
|
if (isIdentifierish(v)) {
|
|
6306
6628
|
results.push(v);
|
|
@@ -6450,34 +6772,20 @@ ${buildSearchRootsKey(normalizedSearchRoots)}`;
|
|
|
6450
6772
|
inferredNativeWrapperConfigByLookup.set(cacheKey, { role: "" });
|
|
6451
6773
|
return null;
|
|
6452
6774
|
}
|
|
6453
|
-
function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
6775
|
+
function tryWrapClickDirectiveForTestEvents(element, testIdAttribute, resolvedRuntimeTestId) {
|
|
6454
6776
|
const jsStringLiteral = (value) => {
|
|
6455
6777
|
return JSON.stringify(value);
|
|
6456
6778
|
};
|
|
6457
6779
|
const getTestIdExpressionForNode = () => {
|
|
6458
|
-
|
|
6459
|
-
if (!existing) {
|
|
6460
|
-
return "undefined";
|
|
6461
|
-
}
|
|
6462
|
-
if (existing.type === compilerCore.NodeTypes.ATTRIBUTE) {
|
|
6463
|
-
const v = existing.value?.content;
|
|
6464
|
-
if (!v) {
|
|
6465
|
-
return "undefined";
|
|
6466
|
-
}
|
|
6467
|
-
return jsStringLiteral(v);
|
|
6468
|
-
}
|
|
6469
|
-
const directive = existing;
|
|
6470
|
-
const exp2 = directive.exp;
|
|
6471
|
-
if (!exp2 || exp2.type !== compilerCore.NodeTypes.SIMPLE_EXPRESSION) {
|
|
6780
|
+
if (!resolvedRuntimeTestId) {
|
|
6472
6781
|
return "undefined";
|
|
6473
6782
|
}
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
return "undefined";
|
|
6783
|
+
if (resolvedRuntimeTestId.kind === "static") {
|
|
6784
|
+
return jsStringLiteral(resolvedRuntimeTestId.value);
|
|
6477
6785
|
}
|
|
6478
|
-
return `(${
|
|
6786
|
+
return `(${renderTemplateLiteralExpression(resolvedRuntimeTestId)})`;
|
|
6479
6787
|
};
|
|
6480
|
-
const
|
|
6788
|
+
const testIdExpression = getTestIdExpressionForNode();
|
|
6481
6789
|
const clickDirective = tryGetClickDirective(element);
|
|
6482
6790
|
if (!clickDirective)
|
|
6483
6791
|
return;
|
|
@@ -6488,10 +6796,10 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6488
6796
|
const exp = clickDirective.exp;
|
|
6489
6797
|
if (!exp)
|
|
6490
6798
|
return;
|
|
6491
|
-
const existingSource = (exp
|
|
6799
|
+
const existingSource = getVueExpressionSource(exp, "loc", "content");
|
|
6492
6800
|
if (existingSource.includes(CLICK_EVENT_NAME))
|
|
6493
6801
|
return;
|
|
6494
|
-
const originalExpression = (exp
|
|
6802
|
+
const originalExpression = getVueExpressionSource(exp, "content", "loc");
|
|
6495
6803
|
if (!originalExpression)
|
|
6496
6804
|
return;
|
|
6497
6805
|
const isStatementBody = (() => {
|
|
@@ -6508,7 +6816,7 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6508
6816
|
const statementWrappedHandler = `($event) => {
|
|
6509
6817
|
const __win = ($event && $event.view) ? $event.view : undefined;
|
|
6510
6818
|
const __target = ($event && $event.currentTarget) ? $event.currentTarget : undefined;
|
|
6511
|
-
const __testIdFromNode = ${
|
|
6819
|
+
const __testIdFromNode = ${testIdExpression};
|
|
6512
6820
|
const __testIdFromTarget = (__target && typeof __target.getAttribute === 'function') ? __target.getAttribute(${jsStringLiteral(testIdAttribute)}) : undefined;
|
|
6513
6821
|
const __testId = (__testIdFromNode ?? __testIdFromTarget);
|
|
6514
6822
|
const __emit = (phase, err) => {
|
|
@@ -6519,16 +6827,12 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6519
6827
|
__w.dispatchEvent(new __CustomEvent('${CLICK_EVENT_NAME}', { detail: { testId: __testId, phase, err: err ? String(err) : undefined } }));
|
|
6520
6828
|
}
|
|
6521
6829
|
} catch (e) {
|
|
6522
|
-
// Instrumentation
|
|
6523
|
-
// In strict mode we rethrow so tests fail fast and the underlying problem is visible.
|
|
6524
|
-
// 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.
|
|
6525
6831
|
const __w = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
6526
6832
|
if (__w && __w.console && typeof __w.console.error === 'function') {
|
|
6527
6833
|
__w.console.error('[testid-click-event] failed to emit ${CLICK_EVENT_NAME}', e);
|
|
6528
6834
|
}
|
|
6529
|
-
|
|
6530
|
-
throw e;
|
|
6531
|
-
}
|
|
6835
|
+
throw e;
|
|
6532
6836
|
}
|
|
6533
6837
|
};
|
|
6534
6838
|
const __w2 = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
@@ -6559,7 +6863,7 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6559
6863
|
const expressionWrappedHandler = `($event) => {
|
|
6560
6864
|
const __win = ($event && $event.view) ? $event.view : undefined;
|
|
6561
6865
|
const __target = ($event && $event.currentTarget) ? $event.currentTarget : undefined;
|
|
6562
|
-
const __testIdFromNode = ${
|
|
6866
|
+
const __testIdFromNode = ${testIdExpression};
|
|
6563
6867
|
const __testIdFromTarget = (__target && typeof __target.getAttribute === 'function') ? __target.getAttribute(${jsStringLiteral(testIdAttribute)}) : undefined;
|
|
6564
6868
|
const __testId = (__testIdFromNode ?? __testIdFromTarget);
|
|
6565
6869
|
const __emit = (phase, err) => {
|
|
@@ -6570,16 +6874,12 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6570
6874
|
__w.dispatchEvent(new __CustomEvent('${CLICK_EVENT_NAME}', { detail: { testId: __testId, phase, err: err ? String(err) : undefined } }));
|
|
6571
6875
|
}
|
|
6572
6876
|
} catch (e) {
|
|
6573
|
-
// Instrumentation
|
|
6574
|
-
// In strict mode we rethrow so tests fail fast and the underlying problem is visible.
|
|
6575
|
-
// 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.
|
|
6576
6878
|
const __w = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
6577
6879
|
if (__w && __w.console && typeof __w.console.error === 'function') {
|
|
6578
6880
|
__w.console.error('[testid-click-event] failed to emit ${CLICK_EVENT_NAME}', e);
|
|
6579
6881
|
}
|
|
6580
|
-
|
|
6581
|
-
throw e;
|
|
6582
|
-
}
|
|
6882
|
+
throw e;
|
|
6583
6883
|
}
|
|
6584
6884
|
};
|
|
6585
6885
|
const __w2 = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
@@ -6617,10 +6917,9 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6617
6917
|
let previousFileName = "";
|
|
6618
6918
|
const hierarchyMap = /* @__PURE__ */ new Map();
|
|
6619
6919
|
function createTestIdTransform(componentName, componentHierarchyMap, nativeWrappers = {}, excludedComponents = [], viewsDirAbs, options = {}) {
|
|
6620
|
-
const existingIdBehavior = options.existingIdBehavior ?? "
|
|
6920
|
+
const existingIdBehavior = options.existingIdBehavior ?? "error";
|
|
6621
6921
|
const testIdAttribute = (options.testIdAttribute || "data-testid").trim() || "data-testid";
|
|
6622
|
-
const nameCollisionBehavior = options.nameCollisionBehavior ?? "
|
|
6623
|
-
const missingSemanticNameBehavior = options.missingSemanticNameBehavior ?? "error";
|
|
6922
|
+
const nameCollisionBehavior = options.nameCollisionBehavior ?? "error";
|
|
6624
6923
|
const warn = options.warn;
|
|
6625
6924
|
const vueFilesPathMap = options.vueFilesPathMap;
|
|
6626
6925
|
const wrapperSearchRoots = options.wrapperSearchRoots ?? [];
|
|
@@ -6696,7 +6995,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6696
6995
|
conditionalHintByIfBranch.set(branch, hint);
|
|
6697
6996
|
continue;
|
|
6698
6997
|
}
|
|
6699
|
-
const condSource = (cond
|
|
6998
|
+
const condSource = getVueExpressionSource(cond, "content", "compiled");
|
|
6700
6999
|
const stable = tryExtractStableHintFromConditionalExpressionSource(condSource);
|
|
6701
7000
|
if (stable) {
|
|
6702
7001
|
conditionalHintByIfBranch.set(branch, stable);
|
|
@@ -6758,17 +7057,18 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6758
7057
|
nativeWrappers[element.tag] = { role: "grid" };
|
|
6759
7058
|
}
|
|
6760
7059
|
}
|
|
6761
|
-
const
|
|
7060
|
+
const getBestAvailableKeyInfo = () => {
|
|
6762
7061
|
const parentNode = context.parent && typeof context.parent === "object" ? context.parent : null;
|
|
6763
7062
|
const isDirectVForChild = parentNode?.type === compilerCore.NodeTypes.FOR;
|
|
6764
|
-
const
|
|
6765
|
-
if (
|
|
6766
|
-
|
|
7063
|
+
const vForKeyInfo = (isDirectVForChild ? getKeyDirectiveInfo(element) : null) || getContainedInVForDirectiveKeyInfo(context, element, hierarchyMap);
|
|
7064
|
+
if (vForKeyInfo) {
|
|
7065
|
+
return vForKeyInfo;
|
|
7066
|
+
}
|
|
7067
|
+
return getContainedInSlotDataKeyInfo(element, hierarchyMap);
|
|
6767
7068
|
};
|
|
6768
|
-
const
|
|
6769
|
-
const
|
|
6770
|
-
const
|
|
6771
|
-
const bestKeyVariable = isSlotKey ? bestKeyInferred : null;
|
|
7069
|
+
const bestKeyInfo = getBestAvailableKeyInfo();
|
|
7070
|
+
const bestKeyPlaceholder = bestKeyInfo?.selectorFragment ?? null;
|
|
7071
|
+
const bestRuntimeKeyPlaceholder = bestKeyInfo?.runtimeFragment ?? null;
|
|
6772
7072
|
const keyValuesOverride = tryGetContainedInStaticVForSourceLiteralValues(context);
|
|
6773
7073
|
const parentKey = context?.parent ? context.parent : null;
|
|
6774
7074
|
const conditional = getConditionalDirectiveInfo(element);
|
|
@@ -6811,7 +7111,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6811
7111
|
if (!cond) {
|
|
6812
7112
|
conditionalHint = "else";
|
|
6813
7113
|
} else {
|
|
6814
|
-
const condSource = (cond
|
|
7114
|
+
const condSource = getVueExpressionSource(cond, "content", "compiled");
|
|
6815
7115
|
conditionalHint = tryExtractStableHintFromConditionalExpressionSource(condSource) ?? "if";
|
|
6816
7116
|
}
|
|
6817
7117
|
}
|
|
@@ -6821,7 +7121,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6821
7121
|
});
|
|
6822
7122
|
if (showDirective?.exp && (showDirective.exp.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION || showDirective.exp.type === compilerCore.NodeTypes.COMPOUND_EXPRESSION)) {
|
|
6823
7123
|
const exp = showDirective.exp;
|
|
6824
|
-
const source = (exp
|
|
7124
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
6825
7125
|
const showHint = tryExtractStableHintFromConditionalExpressionSource(source);
|
|
6826
7126
|
if (showHint) {
|
|
6827
7127
|
conditionalHint = conditionalHint ? `${conditionalHint} ${showHint}` : showHint;
|
|
@@ -6855,13 +7155,17 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6855
7155
|
const tagSuffix = getTagSuffix();
|
|
6856
7156
|
return bestKeyPlaceholder ? templateAttributeValue(`${componentName}-${bestKeyPlaceholder}${clickSuffix}${tagSuffix}`) : staticAttributeValue(`${componentName}${clickSuffix}${tagSuffix}`);
|
|
6857
7157
|
};
|
|
7158
|
+
const getClickRuntimeDataTestId = (clickSuffix) => {
|
|
7159
|
+
const tagSuffix = getTagSuffix();
|
|
7160
|
+
return bestRuntimeKeyPlaceholder ? templateAttributeValue(`${componentName}-${bestRuntimeKeyPlaceholder}${clickSuffix}${tagSuffix}`) : staticAttributeValue(`${componentName}${clickSuffix}${tagSuffix}`);
|
|
7161
|
+
};
|
|
6858
7162
|
const getSubmitDataTestId = (identifier) => {
|
|
6859
7163
|
const tagSuffix = getTagSuffix();
|
|
6860
7164
|
return `${componentName}-${identifier}${tagSuffix}`;
|
|
6861
7165
|
};
|
|
6862
7166
|
const applyResolvedDataTestIdForElement = (args) => {
|
|
6863
7167
|
const nativeRole = args.nativeRoleOverride ?? getNativeRoleFromTagSuffix();
|
|
6864
|
-
applyResolvedDataTestId({
|
|
7168
|
+
return applyResolvedDataTestId({
|
|
6865
7169
|
element,
|
|
6866
7170
|
componentName,
|
|
6867
7171
|
parentComponentName,
|
|
@@ -6871,8 +7175,8 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6871
7175
|
generatedMethodContentByComponent,
|
|
6872
7176
|
nativeRole,
|
|
6873
7177
|
preferredGeneratedValue: args.preferredGeneratedValue,
|
|
6874
|
-
|
|
6875
|
-
|
|
7178
|
+
preferredRuntimeValue: args.preferredRuntimeValue,
|
|
7179
|
+
keyInfo: bestKeyInfo,
|
|
6876
7180
|
keyValuesOverride,
|
|
6877
7181
|
entryOverrides: args.entryOverrides,
|
|
6878
7182
|
semanticNameHint: args.semanticNameHint,
|
|
@@ -6890,7 +7194,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6890
7194
|
return p.type === compilerCore.NodeTypes.DIRECTIVE && p.name === "bind" && p.arg?.type === compilerCore.NodeTypes.SIMPLE_EXPRESSION && p.arg.content === "handler" && !!p.exp;
|
|
6891
7195
|
}) ?? null;
|
|
6892
7196
|
const handlerInfo = handlerDirective ? nodeHandlerAttributeInfo(element) : null;
|
|
6893
|
-
if (
|
|
7197
|
+
if (nativeWrappers[element.tag]?.role === "button" && handlerDirective && !handlerInfo) {
|
|
6894
7198
|
const loc = element.loc?.start;
|
|
6895
7199
|
const locationHint = loc ? `${loc.line}:${loc.column}` : "unknown";
|
|
6896
7200
|
const handlerSource = (handlerDirective.exp?.loc?.source ?? "").trim() || "<unknown>";
|
|
@@ -6899,7 +7203,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6899
7203
|
Element: <${element.tag}>
|
|
6900
7204
|
Handler: ${handlerSource}
|
|
6901
7205
|
|
|
6902
|
-
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.`
|
|
6903
7207
|
);
|
|
6904
7208
|
}
|
|
6905
7209
|
if (nativeWrappersValue) {
|
|
@@ -7026,20 +7330,20 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
|
|
|
7026
7330
|
contextFilename: context.filename
|
|
7027
7331
|
});
|
|
7028
7332
|
const clickHint = trimLeadingSeparators(clickSuffix) || void 0;
|
|
7029
|
-
const idOrName =
|
|
7333
|
+
const idOrName = getStaticIdOrNameHint(element) || void 0;
|
|
7030
7334
|
const semanticHintCandidates = [clickHint, idOrName, innerText, conditionalHint].map((value) => (value ?? "").trim()).filter(Boolean).filter((value, index, values) => values.indexOf(value) === index);
|
|
7031
7335
|
const [semanticNameHint2, ...semanticNameHintAlternates] = semanticHintCandidates;
|
|
7032
7336
|
const pomMergeKey = clickHint ? `click:hint:${clickHint}` : void 0;
|
|
7033
7337
|
const testId = getClickDataTestId(clickSuffix);
|
|
7034
|
-
|
|
7338
|
+
const runtimeTestId = getClickRuntimeDataTestId(clickSuffix);
|
|
7339
|
+
const resolvedDataTestId = applyResolvedDataTestIdForElement({
|
|
7035
7340
|
preferredGeneratedValue: testId,
|
|
7341
|
+
preferredRuntimeValue: runtimeTestId,
|
|
7036
7342
|
semanticNameHint: semanticNameHint2,
|
|
7037
7343
|
semanticNameHintAlternates,
|
|
7038
7344
|
pomMergeKey
|
|
7039
7345
|
});
|
|
7040
|
-
|
|
7041
|
-
tryWrapClickDirectiveForTestEvents(element, testIdAttribute);
|
|
7042
|
-
}
|
|
7346
|
+
tryWrapClickDirectiveForTestEvents(element, testIdAttribute, resolvedDataTestId.runtimeValue);
|
|
7043
7347
|
return;
|
|
7044
7348
|
}
|
|
7045
7349
|
const existingElementDataTestId = tryGetExistingElementDataTestId(element, testIdAttribute);
|
|
@@ -7049,7 +7353,7 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
|
|
|
7049
7353
|
if (!isRecognizedInteractiveRole) {
|
|
7050
7354
|
return;
|
|
7051
7355
|
}
|
|
7052
|
-
const identifierHint =
|
|
7356
|
+
const identifierHint = getStaticIdOrNameHint(element) || nodeHandlerAttributeValue(element) || innerText || existingElementDataTestId.value || conditionalHint || void 0;
|
|
7053
7357
|
const preferredGeneratedValue = existingElementDataTestId.isDynamic ? templateAttributeValue(existingElementDataTestId.template) : staticAttributeValue(existingElementDataTestId.value);
|
|
7054
7358
|
applyResolvedDataTestIdForElement({
|
|
7055
7359
|
preferredGeneratedValue,
|
|
@@ -7059,7 +7363,7 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
|
|
|
7059
7363
|
}
|
|
7060
7364
|
const isSubmit = element.props.find((p) => p.type === compilerCore.NodeTypes.ATTRIBUTE && p.name === "type")?.value?.content === "submit";
|
|
7061
7365
|
if (isSubmit) {
|
|
7062
|
-
const identifier =
|
|
7366
|
+
const identifier = getStaticIdOrNameHint(element) || innerText;
|
|
7063
7367
|
if (!identifier) {
|
|
7064
7368
|
const loc = element.loc?.start;
|
|
7065
7369
|
const locationHint = loc ? `${loc.line}:${loc.column}` : "unknown";
|
|
@@ -7111,29 +7415,32 @@ function createBuildProcessorPlugin(options) {
|
|
|
7111
7415
|
getSourceDirs,
|
|
7112
7416
|
basePageClassPath,
|
|
7113
7417
|
normalizedBasePagePath,
|
|
7418
|
+
generation,
|
|
7419
|
+
projectRootRef,
|
|
7420
|
+
nativeWrappers,
|
|
7421
|
+
excludedComponents,
|
|
7422
|
+
getWrapperSearchRoots,
|
|
7423
|
+
getResolvedRouterEntry,
|
|
7424
|
+
loggerRef
|
|
7425
|
+
} = options;
|
|
7426
|
+
const {
|
|
7114
7427
|
outDir,
|
|
7115
7428
|
emitLanguages,
|
|
7116
7429
|
typescriptOutputStructure,
|
|
7117
7430
|
csharp,
|
|
7118
7431
|
generateFixtures,
|
|
7119
7432
|
customPomAttachments,
|
|
7120
|
-
projectRootRef,
|
|
7121
7433
|
customPomDir,
|
|
7434
|
+
requireCustomPomDir,
|
|
7122
7435
|
customPomImportAliases,
|
|
7123
7436
|
customPomImportNameCollisionBehavior,
|
|
7124
7437
|
testIdAttribute,
|
|
7125
7438
|
nameCollisionBehavior,
|
|
7126
|
-
missingSemanticNameBehavior = "error",
|
|
7127
7439
|
existingIdBehavior,
|
|
7128
|
-
nativeWrappers,
|
|
7129
|
-
excludedComponents,
|
|
7130
|
-
getWrapperSearchRoots,
|
|
7131
7440
|
routerAwarePoms,
|
|
7132
|
-
getResolvedRouterEntry,
|
|
7133
7441
|
routerType,
|
|
7134
|
-
routerModuleShims
|
|
7135
|
-
|
|
7136
|
-
} = options;
|
|
7442
|
+
routerModuleShims
|
|
7443
|
+
} = generation;
|
|
7137
7444
|
let lastGeneratedMetrics = {
|
|
7138
7445
|
entryCount: 0,
|
|
7139
7446
|
interactiveComponentCount: 0,
|
|
@@ -7232,10 +7539,9 @@ function createBuildProcessorPlugin(options) {
|
|
|
7232
7539
|
excludedComponents,
|
|
7233
7540
|
getViewsDirAbs(),
|
|
7234
7541
|
{
|
|
7235
|
-
existingIdBehavior: existingIdBehavior ?? "
|
|
7542
|
+
existingIdBehavior: existingIdBehavior ?? "error",
|
|
7236
7543
|
testIdAttribute,
|
|
7237
7544
|
nameCollisionBehavior,
|
|
7238
|
-
missingSemanticNameBehavior,
|
|
7239
7545
|
warn: (message) => loggerRef.current.warn(message),
|
|
7240
7546
|
vueFilesPathMap,
|
|
7241
7547
|
wrapperSearchRoots: getWrapperSearchRoots()
|
|
@@ -7335,6 +7641,7 @@ function createBuildProcessorPlugin(options) {
|
|
|
7335
7641
|
customPomAttachments,
|
|
7336
7642
|
projectRoot: projectRootRef.current,
|
|
7337
7643
|
customPomDir,
|
|
7644
|
+
requireCustomPomDir,
|
|
7338
7645
|
customPomImportAliases,
|
|
7339
7646
|
customPomImportNameCollisionBehavior,
|
|
7340
7647
|
testIdAttribute,
|
|
@@ -7366,6 +7673,11 @@ function createDevProcessorPlugin(options) {
|
|
|
7366
7673
|
projectRootRef,
|
|
7367
7674
|
normalizedBasePagePath,
|
|
7368
7675
|
basePageClassPath,
|
|
7676
|
+
generation,
|
|
7677
|
+
getResolvedRouterEntry,
|
|
7678
|
+
loggerRef
|
|
7679
|
+
} = options;
|
|
7680
|
+
const {
|
|
7369
7681
|
outDir,
|
|
7370
7682
|
emitLanguages,
|
|
7371
7683
|
typescriptOutputStructure,
|
|
@@ -7373,18 +7685,16 @@ function createDevProcessorPlugin(options) {
|
|
|
7373
7685
|
generateFixtures,
|
|
7374
7686
|
customPomAttachments,
|
|
7375
7687
|
customPomDir,
|
|
7688
|
+
requireCustomPomDir,
|
|
7376
7689
|
customPomImportAliases,
|
|
7377
7690
|
customPomImportNameCollisionBehavior,
|
|
7378
|
-
nameCollisionBehavior
|
|
7379
|
-
missingSemanticNameBehavior = "error",
|
|
7691
|
+
nameCollisionBehavior,
|
|
7380
7692
|
existingIdBehavior,
|
|
7381
7693
|
testIdAttribute,
|
|
7382
7694
|
routerAwarePoms,
|
|
7383
|
-
getResolvedRouterEntry,
|
|
7384
7695
|
routerType,
|
|
7385
|
-
routerModuleShims
|
|
7386
|
-
|
|
7387
|
-
} = options;
|
|
7696
|
+
routerModuleShims
|
|
7697
|
+
} = generation;
|
|
7388
7698
|
let scheduleVueFileRegen = null;
|
|
7389
7699
|
const getProjectRootCandidates = () => Array.from(/* @__PURE__ */ new Set([
|
|
7390
7700
|
path.resolve(projectRootRef.current),
|
|
@@ -7571,9 +7881,8 @@ function createDevProcessorPlugin(options) {
|
|
|
7571
7881
|
excludedComponents,
|
|
7572
7882
|
getViewsDirAbs(),
|
|
7573
7883
|
{
|
|
7574
|
-
existingIdBehavior: existingIdBehavior ?? "
|
|
7884
|
+
existingIdBehavior: existingIdBehavior ?? "error",
|
|
7575
7885
|
nameCollisionBehavior,
|
|
7576
|
-
missingSemanticNameBehavior,
|
|
7577
7886
|
testIdAttribute,
|
|
7578
7887
|
warn: (message) => loggerRef.current.warn(message),
|
|
7579
7888
|
vueFilesPathMap: provisionalVuePathMap,
|
|
@@ -7624,6 +7933,7 @@ function createDevProcessorPlugin(options) {
|
|
|
7624
7933
|
customPomAttachments,
|
|
7625
7934
|
projectRoot: projectRootRef.current,
|
|
7626
7935
|
customPomDir,
|
|
7936
|
+
requireCustomPomDir,
|
|
7627
7937
|
customPomImportAliases,
|
|
7628
7938
|
customPomImportNameCollisionBehavior,
|
|
7629
7939
|
pageDirs: getPageDirs(),
|
|
@@ -7859,27 +8169,30 @@ function createSupportPlugins(options) {
|
|
|
7859
8169
|
getViewsDir,
|
|
7860
8170
|
getSourceDirs,
|
|
7861
8171
|
getWrapperSearchRoots,
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
8172
|
+
generation,
|
|
8173
|
+
projectRootRef,
|
|
8174
|
+
basePageClassPath: basePageClassPathOverride,
|
|
8175
|
+
loggerRef
|
|
8176
|
+
} = options;
|
|
8177
|
+
const {
|
|
7865
8178
|
outDir,
|
|
7866
8179
|
emitLanguages,
|
|
7867
8180
|
typescriptOutputStructure,
|
|
7868
8181
|
csharp,
|
|
7869
|
-
routerAwarePoms,
|
|
7870
|
-
routerEntry,
|
|
7871
|
-
routerType,
|
|
7872
|
-
routerModuleShims,
|
|
7873
8182
|
generateFixtures,
|
|
7874
8183
|
customPomAttachments,
|
|
7875
|
-
projectRootRef,
|
|
7876
|
-
basePageClassPath: basePageClassPathOverride,
|
|
7877
8184
|
customPomDir,
|
|
8185
|
+
requireCustomPomDir,
|
|
7878
8186
|
customPomImportAliases,
|
|
7879
8187
|
customPomImportNameCollisionBehavior,
|
|
8188
|
+
nameCollisionBehavior,
|
|
8189
|
+
existingIdBehavior,
|
|
7880
8190
|
testIdAttribute,
|
|
7881
|
-
|
|
7882
|
-
|
|
8191
|
+
routerAwarePoms,
|
|
8192
|
+
routerEntry,
|
|
8193
|
+
routerType,
|
|
8194
|
+
routerModuleShims
|
|
8195
|
+
} = generation;
|
|
7883
8196
|
const resolveRouterEntry2 = () => {
|
|
7884
8197
|
if (!routerAwarePoms)
|
|
7885
8198
|
return void 0;
|
|
@@ -7908,27 +8221,12 @@ function createSupportPlugins(options) {
|
|
|
7908
8221
|
getSourceDirs,
|
|
7909
8222
|
basePageClassPath,
|
|
7910
8223
|
normalizedBasePagePath,
|
|
7911
|
-
|
|
7912
|
-
emitLanguages,
|
|
7913
|
-
typescriptOutputStructure,
|
|
7914
|
-
csharp,
|
|
7915
|
-
generateFixtures,
|
|
7916
|
-
customPomAttachments,
|
|
8224
|
+
generation,
|
|
7917
8225
|
projectRootRef,
|
|
7918
|
-
customPomDir,
|
|
7919
|
-
customPomImportAliases,
|
|
7920
|
-
customPomImportNameCollisionBehavior,
|
|
7921
|
-
testIdAttribute,
|
|
7922
|
-
nameCollisionBehavior,
|
|
7923
|
-
missingSemanticNameBehavior,
|
|
7924
|
-
existingIdBehavior,
|
|
7925
8226
|
nativeWrappers,
|
|
7926
8227
|
excludedComponents,
|
|
7927
8228
|
getWrapperSearchRoots,
|
|
7928
|
-
routerAwarePoms,
|
|
7929
|
-
routerType,
|
|
7930
8229
|
getResolvedRouterEntry: resolveRouterEntry2,
|
|
7931
|
-
routerModuleShims,
|
|
7932
8230
|
loggerRef
|
|
7933
8231
|
});
|
|
7934
8232
|
const devProcessor = createDevProcessorPlugin({
|
|
@@ -7943,23 +8241,8 @@ function createSupportPlugins(options) {
|
|
|
7943
8241
|
projectRootRef,
|
|
7944
8242
|
normalizedBasePagePath,
|
|
7945
8243
|
basePageClassPath,
|
|
7946
|
-
|
|
7947
|
-
emitLanguages,
|
|
7948
|
-
typescriptOutputStructure,
|
|
7949
|
-
csharp,
|
|
7950
|
-
generateFixtures,
|
|
7951
|
-
customPomAttachments,
|
|
7952
|
-
customPomDir,
|
|
7953
|
-
customPomImportAliases,
|
|
7954
|
-
customPomImportNameCollisionBehavior,
|
|
7955
|
-
nameCollisionBehavior,
|
|
7956
|
-
missingSemanticNameBehavior,
|
|
7957
|
-
existingIdBehavior,
|
|
7958
|
-
testIdAttribute,
|
|
7959
|
-
routerAwarePoms,
|
|
7960
|
-
routerType,
|
|
8244
|
+
generation,
|
|
7961
8245
|
getResolvedRouterEntry: resolveRouterEntry2,
|
|
7962
|
-
routerModuleShims,
|
|
7963
8246
|
loggerRef
|
|
7964
8247
|
});
|
|
7965
8248
|
const virtualModules = createTestIdsVirtualModulesPlugin(componentTestIds);
|
|
@@ -8060,6 +8343,13 @@ function tryCreateElementMetadata(args) {
|
|
|
8060
8343
|
};
|
|
8061
8344
|
return metadata;
|
|
8062
8345
|
}
|
|
8346
|
+
function resolveCompilerSfcParse(compilerSfc2) {
|
|
8347
|
+
const parse = compilerSfc2.parse ?? compilerSfc2.default?.parse;
|
|
8348
|
+
if (typeof parse !== "function") {
|
|
8349
|
+
throw new TypeError("[vue-pom-generator] Failed to resolve @vue/compiler-sfc.parse.");
|
|
8350
|
+
}
|
|
8351
|
+
return parse;
|
|
8352
|
+
}
|
|
8063
8353
|
function extractMetadataAfterTransform(ast, componentName, elementMetadata, semanticNameMap, testIdAttribute) {
|
|
8064
8354
|
const componentMetadata = /* @__PURE__ */ new Map();
|
|
8065
8355
|
function traverseNode(node) {
|
|
@@ -8256,7 +8546,8 @@ function createVuePluginWithTestIds(options) {
|
|
|
8256
8546
|
}
|
|
8257
8547
|
const componentName = getComponentNameFromPath(cleanPath);
|
|
8258
8548
|
loggerRef.current.debug(`Collecting metadata for ${cleanPath} (component: ${componentName})`);
|
|
8259
|
-
const
|
|
8549
|
+
const compilerSfc2 = await import("@vue/compiler-sfc");
|
|
8550
|
+
const parse = resolveCompilerSfcParse(compilerSfc2);
|
|
8260
8551
|
const compilerDom2 = await import("@vue/compiler-dom");
|
|
8261
8552
|
const compile = compilerDom2.compile;
|
|
8262
8553
|
const { descriptor } = parse(code, { filename: cleanPath });
|
|
@@ -8288,8 +8579,7 @@ function createVuePluginWithTestIds(options) {
|
|
|
8288
8579
|
});
|
|
8289
8580
|
const api = viteVuePlugin?.api;
|
|
8290
8581
|
if (!api) {
|
|
8291
|
-
|
|
8292
|
-
return;
|
|
8582
|
+
throw new Error("[vue-pom-generator] Nuxt bridge could not find vite:vue plugin to patch.");
|
|
8293
8583
|
}
|
|
8294
8584
|
const currentOptions = api.options ?? {};
|
|
8295
8585
|
const currentTemplate = currentOptions.template ?? {};
|
|
@@ -8355,33 +8645,6 @@ function assertOneOf(value, allowed, name) {
|
|
|
8355
8645
|
}
|
|
8356
8646
|
throw new TypeError(`${name} must be one of: ${allowed.join(", ")}.`);
|
|
8357
8647
|
}
|
|
8358
|
-
function assertErrorBehavior(value, name) {
|
|
8359
|
-
if (!value) {
|
|
8360
|
-
return;
|
|
8361
|
-
}
|
|
8362
|
-
if (value === "ignore" || value === "error") {
|
|
8363
|
-
return;
|
|
8364
|
-
}
|
|
8365
|
-
if (typeof value !== "object" || Array.isArray(value)) {
|
|
8366
|
-
throw new TypeError(`${name} must be "ignore", "error", or an object.`);
|
|
8367
|
-
}
|
|
8368
|
-
const supportedKeys = /* @__PURE__ */ new Set(["missingSemanticNameBehavior"]);
|
|
8369
|
-
for (const key of Object.keys(value)) {
|
|
8370
|
-
if (!supportedKeys.has(key)) {
|
|
8371
|
-
throw new TypeError(`${name} contains unsupported key "${key}".`);
|
|
8372
|
-
}
|
|
8373
|
-
}
|
|
8374
|
-
assertOneOf(value.missingSemanticNameBehavior, ["ignore", "error"], `${name}.missingSemanticNameBehavior`);
|
|
8375
|
-
}
|
|
8376
|
-
function resolveMissingSemanticNameBehavior(value) {
|
|
8377
|
-
if (!value) {
|
|
8378
|
-
return "error";
|
|
8379
|
-
}
|
|
8380
|
-
if (value === "ignore" || value === "error") {
|
|
8381
|
-
return value;
|
|
8382
|
-
}
|
|
8383
|
-
return value.missingSemanticNameBehavior ?? "error";
|
|
8384
|
-
}
|
|
8385
8648
|
function readPackageJson(projectRoot) {
|
|
8386
8649
|
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
8387
8650
|
if (!fs.existsSync(packageJsonPath)) {
|
|
@@ -8490,7 +8753,7 @@ function applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompile
|
|
|
8490
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(...).'
|
|
8491
8754
|
);
|
|
8492
8755
|
}
|
|
8493
|
-
throw new Error("[vue-pom-generator] Nuxt
|
|
8756
|
+
throw new Error("[vue-pom-generator] Nuxt bridge could not find vite:vue plugin to patch.");
|
|
8494
8757
|
}
|
|
8495
8758
|
const currentOptions = viteVuePlugin.api.options ?? {};
|
|
8496
8759
|
const currentTemplate = currentOptions.template ?? {};
|
|
@@ -8547,18 +8810,23 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8547
8810
|
const vueGenerationOptions = generationOptions;
|
|
8548
8811
|
const verbosity = options.logging?.verbosity ?? "warn";
|
|
8549
8812
|
const vueOptions = options.vueOptions;
|
|
8550
|
-
const
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
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;
|
|
8562
8830
|
const routerEntry = !isNuxt ? vueGenerationOptions?.router?.entry : void 0;
|
|
8563
8831
|
const routerType = isNuxt ? "nuxt" : vueGenerationOptions?.router?.type ?? "vue-router";
|
|
8564
8832
|
const routerModuleShims = !isNuxt ? vueGenerationOptions?.router?.moduleShims : void 0;
|
|
@@ -8567,27 +8835,41 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8567
8835
|
}
|
|
8568
8836
|
const vuePluginOwnership = isNuxt ? "external" : options.vuePluginOwnership ?? "internal";
|
|
8569
8837
|
const usesExternalVuePlugin = vuePluginOwnership === "external";
|
|
8570
|
-
const csharp = generationOptions?.csharp;
|
|
8571
|
-
const errorBehavior = options.errorBehavior;
|
|
8572
|
-
const missingSemanticNameBehavior = resolveMissingSemanticNameBehavior(errorBehavior);
|
|
8573
|
-
const typescriptOutputStructure = generationOptions?.playwright?.outputStructure ?? "aggregated";
|
|
8574
8838
|
const generateFixtures = generationOptions?.playwright?.fixtures;
|
|
8575
8839
|
const customPoms = generationOptions?.playwright?.customPoms;
|
|
8576
8840
|
const resolvedCustomPomAttachments = customPoms?.attachments ?? [];
|
|
8577
|
-
const resolvedCustomPomDir = customPoms?.dir ?? "tests/playwright/pom/custom";
|
|
8578
8841
|
const resolvedCustomPomImportAliases = customPoms?.importAliases;
|
|
8579
|
-
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
|
+
});
|
|
8580
8862
|
const basePageClassPathOverride = generationOptions?.basePageClassPath;
|
|
8581
|
-
const getPageDirs = () =>
|
|
8863
|
+
const getPageDirs = () => resolvedInjectionOptionsRef.current.pageDirs;
|
|
8582
8864
|
const getViewsDir = () => getPageDirs()[0] ?? "src/views";
|
|
8583
|
-
const getComponentDirs = () =>
|
|
8584
|
-
const getLayoutDirs = () =>
|
|
8865
|
+
const getComponentDirs = () => resolvedInjectionOptionsRef.current.componentDirs;
|
|
8866
|
+
const getLayoutDirs = () => resolvedInjectionOptionsRef.current.layoutDirs;
|
|
8585
8867
|
const getSourceDirs = () => Array.from(/* @__PURE__ */ new Set([
|
|
8586
8868
|
...getPageDirs(),
|
|
8587
8869
|
...getComponentDirs(),
|
|
8588
8870
|
...getLayoutDirs()
|
|
8589
8871
|
]));
|
|
8590
|
-
const getWrapperSearchRoots = () =>
|
|
8872
|
+
const getWrapperSearchRoots = () => resolvedInjectionOptionsRef.current.wrapperSearchRoots;
|
|
8591
8873
|
const sharedStateKey = JSON.stringify({
|
|
8592
8874
|
cwd: process.cwd(),
|
|
8593
8875
|
mode: isNuxt ? "nuxt" : "vue",
|
|
@@ -8595,9 +8877,9 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8595
8877
|
componentDirs: isNuxt ? null : getComponentDirs(),
|
|
8596
8878
|
layoutDirs: isNuxt ? null : getLayoutDirs(),
|
|
8597
8879
|
wrapperSearchRoots: isNuxt ? null : getWrapperSearchRoots(),
|
|
8598
|
-
outDir,
|
|
8880
|
+
outDir: resolvedGenerationOptions.outDir,
|
|
8599
8881
|
testIdAttribute,
|
|
8600
|
-
routerType,
|
|
8882
|
+
routerType: resolvedGenerationOptions.routerType,
|
|
8601
8883
|
vuePluginOwnership
|
|
8602
8884
|
});
|
|
8603
8885
|
const sharedState = getSharedGeneratorState(sharedStateKey);
|
|
@@ -8618,23 +8900,22 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8618
8900
|
if (isNuxt) {
|
|
8619
8901
|
const nuxtDiscovery = await loadNuxtProjectDiscovery(process.cwd());
|
|
8620
8902
|
projectRootRef.current = nuxtDiscovery.rootDir;
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8903
|
+
resolvedInjectionOptionsRef.current = applyNuxtDiscoveryToInjectionOptions(
|
|
8904
|
+
resolvedInjectionOptionsRef.current,
|
|
8905
|
+
nuxtDiscovery
|
|
8906
|
+
);
|
|
8625
8907
|
}
|
|
8626
8908
|
assertNonEmptyString(testIdAttribute, "[vue-pom-generator] injection.attribute");
|
|
8627
8909
|
assertNonEmptyString(getViewsDir(), "[vue-pom-generator] injection.viewsDir");
|
|
8628
8910
|
assertNonEmptyStringArray(getComponentDirs(), "[vue-pom-generator] injection.componentDirs");
|
|
8629
8911
|
assertNonEmptyStringArray(getLayoutDirs(), "[vue-pom-generator] injection.layoutDirs");
|
|
8630
8912
|
assertNonEmptyStringArray(getWrapperSearchRoots(), "[vue-pom-generator] injection.wrapperSearchRoots");
|
|
8631
|
-
assertErrorBehavior(errorBehavior, "[vue-pom-generator] errorBehavior");
|
|
8632
8913
|
if (generationEnabled) {
|
|
8633
|
-
assertNonEmptyString(outDir, "[vue-pom-generator] generation.outDir");
|
|
8634
|
-
assertOneOf(typescriptOutputStructure, ["aggregated", "split"], "[vue-pom-generator] generation.playwright.outputStructure");
|
|
8635
|
-
assertRouterModuleShims(routerModuleShims, "[vue-pom-generator] generation.router.moduleShims");
|
|
8636
|
-
if (!isNuxt && vueGenerationOptions?.router && routerType === "vue-router") {
|
|
8637
|
-
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");
|
|
8638
8919
|
}
|
|
8639
8920
|
}
|
|
8640
8921
|
if (usesExternalVuePlugin) {
|
|
@@ -8656,8 +8937,8 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8656
8937
|
const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
|
|
8657
8938
|
const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
|
|
8658
8939
|
vueOptions,
|
|
8659
|
-
existingIdBehavior,
|
|
8660
|
-
nameCollisionBehavior,
|
|
8940
|
+
existingIdBehavior: resolvedGenerationOptions.existingIdBehavior,
|
|
8941
|
+
nameCollisionBehavior: resolvedGenerationOptions.nameCollisionBehavior,
|
|
8661
8942
|
nativeWrappers,
|
|
8662
8943
|
elementMetadata,
|
|
8663
8944
|
semanticNameMap,
|
|
@@ -8672,7 +8953,6 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8672
8953
|
getProjectRoot: () => projectRootRef.current
|
|
8673
8954
|
});
|
|
8674
8955
|
templateCompilerOptionsForResolvedPlugin = templateCompilerOptions;
|
|
8675
|
-
const routerAwarePoms = typeof routerEntry === "string" && routerEntry.trim().length > 0 || routerType === "nuxt";
|
|
8676
8956
|
const supportPlugins = createSupportPlugins({
|
|
8677
8957
|
componentTestIds,
|
|
8678
8958
|
componentHierarchyMap,
|
|
@@ -8685,26 +8965,10 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8685
8965
|
getViewsDir,
|
|
8686
8966
|
getSourceDirs,
|
|
8687
8967
|
getWrapperSearchRoots: getWrapperSearchRootsAbs,
|
|
8688
|
-
|
|
8689
|
-
missingSemanticNameBehavior,
|
|
8690
|
-
existingIdBehavior,
|
|
8691
|
-
outDir,
|
|
8692
|
-
emitLanguages,
|
|
8693
|
-
typescriptOutputStructure,
|
|
8694
|
-
csharp,
|
|
8695
|
-
routerAwarePoms,
|
|
8696
|
-
routerEntry,
|
|
8697
|
-
generateFixtures,
|
|
8968
|
+
generation: resolvedGenerationOptions,
|
|
8698
8969
|
projectRootRef,
|
|
8699
8970
|
basePageClassPath: basePageClassPathOverride,
|
|
8700
|
-
|
|
8701
|
-
customPomDir: resolvedCustomPomDir,
|
|
8702
|
-
customPomImportAliases: resolvedCustomPomImportAliases,
|
|
8703
|
-
customPomImportNameCollisionBehavior: resolvedCustomPomImportCollisionBehavior,
|
|
8704
|
-
testIdAttribute,
|
|
8705
|
-
loggerRef,
|
|
8706
|
-
routerType,
|
|
8707
|
-
routerModuleShims
|
|
8971
|
+
loggerRef
|
|
8708
8972
|
});
|
|
8709
8973
|
if (isNuxt) {
|
|
8710
8974
|
loggerRef.current.info("Nuxt environment detected. Skipping internal @vitejs/plugin-vue to avoid conflicts.");
|