@immense/vue-pom-generator 1.0.58 → 1.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -18
- package/RELEASE_NOTES.md +75 -29
- package/class-generation/base-page.ts +6 -13
- package/class-generation/index.ts +226 -317
- package/class-generation/playwright-types.ts +1 -1
- package/click-instrumentation.ts +0 -4
- package/dist/class-generation/base-page.d.ts +1 -0
- package/dist/class-generation/base-page.d.ts.map +1 -1
- package/dist/class-generation/index.d.ts +2 -0
- package/dist/class-generation/index.d.ts.map +1 -1
- package/dist/class-generation/playwright-types.d.ts +1 -1
- package/dist/class-generation/playwright-types.d.ts.map +1 -1
- package/dist/click-instrumentation.d.ts +0 -1
- package/dist/click-instrumentation.d.ts.map +1 -1
- package/dist/index.cjs +1216 -1008
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1218 -1010
- package/dist/index.mjs.map +1 -1
- package/dist/method-generation.d.ts +4 -2
- package/dist/method-generation.d.ts.map +1 -1
- package/dist/plugin/create-vue-pom-generator-plugins.d.ts.map +1 -1
- package/dist/plugin/resolved-generation-options.d.ts +33 -0
- package/dist/plugin/resolved-generation-options.d.ts.map +1 -0
- package/dist/plugin/resolved-injection-options.d.ts +27 -0
- package/dist/plugin/resolved-injection-options.d.ts.map +1 -0
- package/dist/plugin/support/build-plugin.d.ts +2 -29
- package/dist/plugin/support/build-plugin.d.ts.map +1 -1
- package/dist/plugin/support/dev-plugin.d.ts +2 -28
- package/dist/plugin/support/dev-plugin.d.ts.map +1 -1
- package/dist/plugin/support-plugins.d.ts +2 -32
- package/dist/plugin/support-plugins.d.ts.map +1 -1
- package/dist/plugin/types.d.ts +6 -23
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/plugin/vue-plugin.d.ts.map +1 -1
- package/dist/pom-params.d.ts +40 -0
- package/dist/pom-params.d.ts.map +1 -0
- package/dist/pom-patterns.d.ts +31 -0
- package/dist/pom-patterns.d.ts.map +1 -0
- package/dist/routing/to-directive.d.ts +21 -0
- package/dist/routing/to-directive.d.ts.map +1 -1
- package/dist/tests/base-page.test.d.ts +2 -0
- package/dist/tests/base-page.test.d.ts.map +1 -0
- package/dist/tests/resolved-injection-options.test.d.ts +2 -0
- package/dist/tests/resolved-injection-options.test.d.ts.map +1 -0
- package/dist/transform.d.ts +0 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/utils.d.ts +129 -63
- package/dist/utils.d.ts.map +1 -1
- package/package.json +6 -4
- package/sequence-diagram.md +6 -6
package/dist/index.mjs
CHANGED
|
@@ -8,8 +8,8 @@ import { parse as parse$2, NodeTypes as NodeTypes$1 } from "@vue/compiler-dom";
|
|
|
8
8
|
import { parse as parse$1, compileScript } from "@vue/compiler-sfc";
|
|
9
9
|
import { parseExpression, parse } from "@babel/parser";
|
|
10
10
|
import { NodeTypes, stringifyExpression, ConstantTypes, createSimpleExpression, ElementTypes } from "@vue/compiler-core";
|
|
11
|
-
import { Project, QuoteKind, NewLineKind, IndentationText,
|
|
12
|
-
import { isArrayExpression, isStringLiteral, isTemplateLiteral,
|
|
11
|
+
import { Project, QuoteKind, NewLineKind, IndentationText, CodeBlockWriter, StructureKind, VariableDeclarationKind } from "ts-morph";
|
|
12
|
+
import { isArrayExpression, isStringLiteral, isTemplateLiteral, isMemberExpression, isOptionalMemberExpression, isCallExpression, isOptionalCallExpression, isAssignmentExpression, isIdentifier, isExpressionStatement, isObjectExpression, isFile, isArrowFunctionExpression, isBlockStatement, isLogicalExpression, isConditionalExpression, isSequenceExpression, isAssignmentPattern, isRestElement, isObjectPattern, isObjectProperty, isProgram, VISITOR_KEYS, isBooleanLiteral, isNumericLiteral, isNullLiteral } from "@babel/types";
|
|
13
13
|
import { performance } from "node:perf_hooks";
|
|
14
14
|
import vue from "@vitejs/plugin-vue";
|
|
15
15
|
const VUE_POM_GENERATOR_LOG_PREFIX = "[vue-pom-generator]";
|
|
@@ -204,6 +204,49 @@ async function loadNuxtProjectDiscovery(cwd = process.cwd()) {
|
|
|
204
204
|
const nuxtOptions = await loadNuxtConfig({ cwd });
|
|
205
205
|
return resolveNuxtProjectDiscovery(nuxtOptions, getLayerDirectories, cwd);
|
|
206
206
|
}
|
|
207
|
+
function resolveGenerationSupportOptions(options) {
|
|
208
|
+
return {
|
|
209
|
+
outDir: (options.outDir ?? "tests/playwright/__generated__").trim(),
|
|
210
|
+
emitLanguages: options.emitLanguages?.length ? options.emitLanguages : ["ts"],
|
|
211
|
+
typescriptOutputStructure: options.typescriptOutputStructure ?? "aggregated",
|
|
212
|
+
csharp: options.csharp,
|
|
213
|
+
generateFixtures: options.generateFixtures,
|
|
214
|
+
customPomAttachments: options.customPomAttachments ?? [],
|
|
215
|
+
customPomDir: options.customPomDir ?? "tests/playwright/pom/custom",
|
|
216
|
+
requireCustomPomDir: options.requireCustomPomDir ?? false,
|
|
217
|
+
customPomImportAliases: options.customPomImportAliases,
|
|
218
|
+
customPomImportNameCollisionBehavior: options.customPomImportNameCollisionBehavior ?? "error",
|
|
219
|
+
nameCollisionBehavior: options.nameCollisionBehavior ?? "error",
|
|
220
|
+
existingIdBehavior: options.existingIdBehavior ?? "error",
|
|
221
|
+
testIdAttribute: (options.testIdAttribute ?? "data-testid").trim() || "data-testid",
|
|
222
|
+
routerAwarePoms: options.routerAwarePoms ?? false,
|
|
223
|
+
routerEntry: options.routerEntry,
|
|
224
|
+
routerType: options.routerType ?? "vue-router",
|
|
225
|
+
routerModuleShims: options.routerModuleShims
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function resolveInjectionSupportOptions(options) {
|
|
229
|
+
const isNuxt = options.isNuxt ?? false;
|
|
230
|
+
return {
|
|
231
|
+
pageDirs: isNuxt ? ["app/pages"] : [options.viewsDir ?? "src/views"],
|
|
232
|
+
componentDirs: isNuxt ? ["app/components"] : options.componentDirs ?? ["src/components"],
|
|
233
|
+
layoutDirs: isNuxt ? ["app/layouts"] : options.layoutDirs ?? ["src/layouts"],
|
|
234
|
+
wrapperSearchRoots: isNuxt ? [] : options.wrapperSearchRoots ?? [],
|
|
235
|
+
nativeWrappers: options.nativeWrappers ?? {},
|
|
236
|
+
excludedComponents: options.excludedComponents ?? [],
|
|
237
|
+
existingIdBehavior: options.existingIdBehavior ?? "error",
|
|
238
|
+
testIdAttribute: (options.testIdAttribute ?? "data-testid").trim() || "data-testid"
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function applyNuxtDiscoveryToInjectionOptions(options, discovery) {
|
|
242
|
+
return {
|
|
243
|
+
...options,
|
|
244
|
+
pageDirs: discovery.pageDirs.length ? discovery.pageDirs : [path.resolve(discovery.srcDir, "pages")],
|
|
245
|
+
componentDirs: discovery.componentDirs,
|
|
246
|
+
layoutDirs: discovery.layoutDirs,
|
|
247
|
+
wrapperSearchRoots: discovery.wrapperSearchRoots
|
|
248
|
+
};
|
|
249
|
+
}
|
|
207
250
|
function createTypeScriptProject() {
|
|
208
251
|
return new Project({
|
|
209
252
|
useInMemoryFileSystem: true,
|
|
@@ -295,16 +338,7 @@ function createClassConstructor(constructorDeclaration) {
|
|
|
295
338
|
...constructorDeclaration
|
|
296
339
|
};
|
|
297
340
|
}
|
|
298
|
-
function
|
|
299
|
-
if (!value) {
|
|
300
|
-
return value;
|
|
301
|
-
}
|
|
302
|
-
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
303
|
-
}
|
|
304
|
-
function hasParam(params, name) {
|
|
305
|
-
return Object.prototype.hasOwnProperty.call(params, name);
|
|
306
|
-
}
|
|
307
|
-
function splitTypeAndInitializer(typeExpression) {
|
|
341
|
+
function splitPomParameterTypeExpression(typeExpression) {
|
|
308
342
|
const trimmed = typeExpression.trim();
|
|
309
343
|
const initializerIndex = trimmed.lastIndexOf("=");
|
|
310
344
|
if (initializerIndex < 0) {
|
|
@@ -315,49 +349,237 @@ function splitTypeAndInitializer(typeExpression) {
|
|
|
315
349
|
initializer: trimmed.slice(initializerIndex + 1).trim()
|
|
316
350
|
};
|
|
317
351
|
}
|
|
318
|
-
function
|
|
319
|
-
const
|
|
352
|
+
function createPomParameterSpec(name, typeExpression, options = {}) {
|
|
353
|
+
const normalizedTypeExpression = typeExpression?.trim();
|
|
354
|
+
const { type, initializer } = normalizedTypeExpression ? splitPomParameterTypeExpression(normalizedTypeExpression) : { type: void 0, initializer: void 0 };
|
|
320
355
|
return {
|
|
321
356
|
name,
|
|
322
|
-
|
|
323
|
-
|
|
357
|
+
typeExpression: normalizedTypeExpression,
|
|
358
|
+
type,
|
|
359
|
+
initializer: options.initializer ?? initializer,
|
|
360
|
+
hasQuestionToken: options.hasQuestionToken,
|
|
361
|
+
isRestParameter: options.isRestParameter
|
|
324
362
|
};
|
|
325
363
|
}
|
|
326
|
-
function
|
|
327
|
-
|
|
364
|
+
function normalizePomParameters(params) {
|
|
365
|
+
if (!params) {
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
return params.map((param) => createPomParameterSpec(param.name, param.typeExpression ?? param.type, {
|
|
369
|
+
initializer: param.initializer,
|
|
370
|
+
hasQuestionToken: param.hasQuestionToken,
|
|
371
|
+
isRestParameter: param.isRestParameter
|
|
372
|
+
}));
|
|
328
373
|
}
|
|
329
|
-
function
|
|
374
|
+
function getPomParameterNames(params) {
|
|
375
|
+
return normalizePomParameters(params).map((param) => param.name);
|
|
376
|
+
}
|
|
377
|
+
function getPomParameter(params, name) {
|
|
378
|
+
return normalizePomParameters(params).find((param) => param.name === name);
|
|
379
|
+
}
|
|
380
|
+
function hasPomParameter(params, name) {
|
|
381
|
+
return !!getPomParameter(params, name);
|
|
382
|
+
}
|
|
383
|
+
function setPomParameter(params, name, typeExpression, options = {}) {
|
|
384
|
+
const nextParam = createPomParameterSpec(name, typeExpression, options);
|
|
385
|
+
const normalizedParams = normalizePomParameters(params);
|
|
386
|
+
const existingIndex = normalizedParams.findIndex((param) => param.name === name);
|
|
387
|
+
if (existingIndex < 0) {
|
|
388
|
+
return [...normalizedParams, nextParam];
|
|
389
|
+
}
|
|
390
|
+
const nextParams = normalizedParams.slice();
|
|
391
|
+
nextParams[existingIndex] = nextParam;
|
|
392
|
+
return nextParams;
|
|
393
|
+
}
|
|
394
|
+
function removePomParameter(params, name) {
|
|
395
|
+
return normalizePomParameters(params).filter((param) => param.name !== name);
|
|
396
|
+
}
|
|
397
|
+
function toTypeScriptPomParameterStructures(params) {
|
|
398
|
+
return normalizePomParameters(params).map((param) => ({
|
|
399
|
+
name: param.name,
|
|
400
|
+
type: param.type || void 0,
|
|
401
|
+
initializer: param.initializer,
|
|
402
|
+
hasQuestionToken: param.hasQuestionToken,
|
|
403
|
+
isRestParameter: param.isRestParameter
|
|
404
|
+
}));
|
|
405
|
+
}
|
|
406
|
+
function getPomParameterArgumentNames(params) {
|
|
407
|
+
return normalizePomParameters(params).map((param) => param.isRestParameter ? `...${param.name}` : param.name);
|
|
408
|
+
}
|
|
409
|
+
function createPomMethodSignature(parameters) {
|
|
330
410
|
return {
|
|
331
|
-
|
|
332
|
-
type: options.type,
|
|
333
|
-
initializer: options.initializer
|
|
411
|
+
parameters: normalizePomParameters(parameters)
|
|
334
412
|
};
|
|
335
413
|
}
|
|
336
|
-
function
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
414
|
+
function pomParameterSpecEquals(left, right) {
|
|
415
|
+
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;
|
|
416
|
+
}
|
|
417
|
+
function pomParameterListEquals(left, right) {
|
|
418
|
+
const leftParams = normalizePomParameters(left);
|
|
419
|
+
const rightParams = normalizePomParameters(right);
|
|
420
|
+
if (leftParams.length !== rightParams.length) {
|
|
421
|
+
return false;
|
|
340
422
|
}
|
|
341
|
-
return
|
|
423
|
+
return leftParams.every((param, index) => pomParameterSpecEquals(param, rightParams[index]));
|
|
342
424
|
}
|
|
343
|
-
function
|
|
425
|
+
function pomMethodSignatureEquals(left, right) {
|
|
426
|
+
return pomParameterListEquals(left.parameters, right.parameters);
|
|
427
|
+
}
|
|
428
|
+
function isParameterizedPomPattern(kind) {
|
|
429
|
+
return kind === "parameterized";
|
|
430
|
+
}
|
|
431
|
+
function getTemplateVariables(formatted) {
|
|
344
432
|
const out = [];
|
|
345
433
|
const seen = /* @__PURE__ */ new Set();
|
|
346
|
-
|
|
347
|
-
for (const
|
|
348
|
-
|
|
434
|
+
const matches = formatted.matchAll(/\$\{(\w+)\}/g);
|
|
435
|
+
for (const match of matches) {
|
|
436
|
+
const variableName = match[1];
|
|
437
|
+
if (seen.has(variableName)) {
|
|
349
438
|
continue;
|
|
350
439
|
}
|
|
351
|
-
|
|
440
|
+
seen.add(variableName);
|
|
441
|
+
out.push(variableName);
|
|
442
|
+
}
|
|
443
|
+
return out;
|
|
444
|
+
}
|
|
445
|
+
function createPomStringPattern(formatted, patternKind) {
|
|
446
|
+
return {
|
|
447
|
+
formatted,
|
|
448
|
+
patternKind,
|
|
449
|
+
templateVariables: getTemplateVariables(formatted)
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function getPomPatternVariables(patterns, options = {}) {
|
|
453
|
+
const out = [];
|
|
454
|
+
const seen = /* @__PURE__ */ new Set();
|
|
455
|
+
const omitted = new Set(options.omit ?? []);
|
|
456
|
+
for (const pattern of patterns) {
|
|
457
|
+
for (const variableName of pattern.templateVariables) {
|
|
458
|
+
if (omitted.has(variableName) || seen.has(variableName)) {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
seen.add(variableName);
|
|
462
|
+
out.push(variableName);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return out;
|
|
466
|
+
}
|
|
467
|
+
function orderPomPatternParameters(params, patterns, options = {}) {
|
|
468
|
+
const currentParams = normalizePomParameters(params);
|
|
469
|
+
const orderedParams = [];
|
|
470
|
+
const seen = /* @__PURE__ */ new Set();
|
|
471
|
+
const missingParams = [];
|
|
472
|
+
for (const variableName of getPomPatternVariables(patterns, options)) {
|
|
473
|
+
seen.add(variableName);
|
|
474
|
+
const existingParam = currentParams.find((param) => param.name === variableName);
|
|
475
|
+
if (!existingParam) {
|
|
476
|
+
missingParams.push(variableName);
|
|
352
477
|
continue;
|
|
353
478
|
}
|
|
354
|
-
|
|
355
|
-
|
|
479
|
+
orderedParams.push(existingParam);
|
|
480
|
+
}
|
|
481
|
+
if (missingParams.length > 0) {
|
|
482
|
+
const availableParams = currentParams.map((param) => JSON.stringify(param.name)).join(", ") || "<none>";
|
|
483
|
+
const patternSummary = patterns.map((pattern) => JSON.stringify(pattern.formatted)).join(", ");
|
|
484
|
+
throw new Error(
|
|
485
|
+
`[vue-pom-generator] Missing selector parameter(s) ${missingParams.map((name) => JSON.stringify(name)).join(", ")} for parameterized pattern(s) ${patternSummary}. Available parameters: ${availableParams}.`
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
for (const param of currentParams) {
|
|
489
|
+
if (seen.has(param.name)) {
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
seen.add(param.name);
|
|
493
|
+
orderedParams.push(param);
|
|
494
|
+
}
|
|
495
|
+
return orderedParams;
|
|
496
|
+
}
|
|
497
|
+
function getIndexedPomPatternVariable(pattern) {
|
|
498
|
+
if (!isParameterizedPomPattern(pattern.patternKind)) {
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
if (pattern.templateVariables.length !== 1) {
|
|
502
|
+
throw new Error(
|
|
503
|
+
`[vue-pom-generator] Parameterized locator getters require exactly one template variable; got ${pattern.templateVariables.length} in ${JSON.stringify(pattern.formatted)}.`
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
return pattern.templateVariables[0];
|
|
507
|
+
}
|
|
508
|
+
function hasPomPatternVariables(pattern) {
|
|
509
|
+
return pattern.templateVariables.length > 0;
|
|
510
|
+
}
|
|
511
|
+
function toTypeScriptPomPatternExpression(pattern) {
|
|
512
|
+
return isParameterizedPomPattern(pattern.patternKind) ? `\`${pattern.formatted}\`` : JSON.stringify(pattern.formatted);
|
|
513
|
+
}
|
|
514
|
+
function toCSharpPomPatternExpression(pattern) {
|
|
515
|
+
if (!isParameterizedPomPattern(pattern.patternKind)) {
|
|
516
|
+
return JSON.stringify(pattern.formatted);
|
|
517
|
+
}
|
|
518
|
+
const inner = pattern.formatted.replace(/\$\{/g, "{");
|
|
519
|
+
return `$${JSON.stringify(inner)}`;
|
|
520
|
+
}
|
|
521
|
+
function bindTypeScriptPomPattern(pattern, variableName) {
|
|
522
|
+
const expression = toTypeScriptPomPatternExpression(pattern);
|
|
523
|
+
if (!isParameterizedPomPattern(pattern.patternKind)) {
|
|
524
|
+
return { expression, setupStatements: [] };
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
expression: variableName,
|
|
528
|
+
setupStatements: [`const ${variableName} = ${expression};`]
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
function bindCSharpPomPattern(pattern, variableName) {
|
|
532
|
+
const expression = toCSharpPomPatternExpression(pattern);
|
|
533
|
+
if (!isParameterizedPomPattern(pattern.patternKind)) {
|
|
534
|
+
return { expression, setupStatements: [] };
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
expression: variableName,
|
|
538
|
+
setupStatements: [`var ${variableName} = ${expression};`]
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function pomStringPatternEquals(left, right) {
|
|
542
|
+
return left.formatted === right.formatted && left.patternKind === right.patternKind;
|
|
543
|
+
}
|
|
544
|
+
function uniquePomStringPatterns(primary, alternates) {
|
|
545
|
+
const out = [];
|
|
546
|
+
const seen = /* @__PURE__ */ new Set();
|
|
547
|
+
const add = (pattern) => {
|
|
548
|
+
const key = JSON.stringify(pattern);
|
|
549
|
+
if (seen.has(key)) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
seen.add(key);
|
|
553
|
+
out.push(pattern);
|
|
554
|
+
};
|
|
555
|
+
add(primary);
|
|
556
|
+
for (const alternate of alternates ?? []) {
|
|
557
|
+
add(alternate);
|
|
356
558
|
}
|
|
357
559
|
return out;
|
|
358
560
|
}
|
|
359
|
-
function
|
|
360
|
-
|
|
561
|
+
function upperFirst$1(value) {
|
|
562
|
+
if (!value) {
|
|
563
|
+
return value;
|
|
564
|
+
}
|
|
565
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
566
|
+
}
|
|
567
|
+
function createParameters(params) {
|
|
568
|
+
return toTypeScriptPomParameterStructures(params);
|
|
569
|
+
}
|
|
570
|
+
function createInlineParameter(name, options = {}) {
|
|
571
|
+
return {
|
|
572
|
+
name,
|
|
573
|
+
type: options.type,
|
|
574
|
+
initializer: options.initializer
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function removeByKeySegment(value) {
|
|
578
|
+
const idx = value.lastIndexOf("ByKey");
|
|
579
|
+
if (idx < 0) {
|
|
580
|
+
return value;
|
|
581
|
+
}
|
|
582
|
+
return value.slice(0, idx) + value.slice(idx + "ByKey".length);
|
|
361
583
|
}
|
|
362
584
|
function createAsyncMethod(name, parameters, statements) {
|
|
363
585
|
return createClassMethod({
|
|
@@ -367,17 +589,20 @@ function createAsyncMethod(name, parameters, statements) {
|
|
|
367
589
|
statements
|
|
368
590
|
});
|
|
369
591
|
}
|
|
370
|
-
function generateClickMethod(methodName,
|
|
592
|
+
function generateClickMethod(methodName, selector, alternateSelectors, parameters) {
|
|
371
593
|
const name = `click${methodName}`;
|
|
372
594
|
const noWaitName = `${name}NoWait`;
|
|
373
|
-
const
|
|
374
|
-
const
|
|
375
|
-
const
|
|
595
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
596
|
+
const hasSelectorVariables = hasPomPatternVariables(selector);
|
|
597
|
+
const baseParameters = createParameters(selectorParams);
|
|
598
|
+
const argsForForward = getPomParameterNames(selectorParams).join(", ");
|
|
599
|
+
const alternates = uniquePomStringPatterns(selector, alternateSelectors).slice(1);
|
|
600
|
+
const primaryTestIdExpr = toTypeScriptPomPatternExpression(selector);
|
|
376
601
|
if (alternates.length > 0) {
|
|
377
|
-
const candidatesExpr = [
|
|
602
|
+
const candidatesExpr = [primaryTestIdExpr, ...alternates.map((id) => toTypeScriptPomPatternExpression(id))].join(", ");
|
|
378
603
|
const clickMethod = createAsyncMethod(
|
|
379
604
|
name,
|
|
380
|
-
|
|
605
|
+
hasSelectorVariables ? [
|
|
381
606
|
...baseParameters,
|
|
382
607
|
createInlineParameter("wait", { type: "boolean", initializer: "true" }),
|
|
383
608
|
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
@@ -406,14 +631,14 @@ function generateClickMethod(methodName, formattedDataTestId, alternateFormatted
|
|
|
406
631
|
const noWaitArgs = argsForForward ? `${argsForForward}, false, annotationText` : "false, annotationText";
|
|
407
632
|
const noWaitMethod = createAsyncMethod(
|
|
408
633
|
noWaitName,
|
|
409
|
-
|
|
634
|
+
hasSelectorVariables ? [...baseParameters, createInlineParameter("annotationText", { type: "string", initializer: '""' })] : [createInlineParameter("annotationText", { type: "string", initializer: '""' })],
|
|
410
635
|
(writer) => {
|
|
411
636
|
writer.writeLine(`await this.${name}(${noWaitArgs});`);
|
|
412
637
|
}
|
|
413
638
|
);
|
|
414
639
|
return [clickMethod, noWaitMethod];
|
|
415
640
|
}
|
|
416
|
-
if (
|
|
641
|
+
if (hasSelectorVariables) {
|
|
417
642
|
return [
|
|
418
643
|
createAsyncMethod(
|
|
419
644
|
name,
|
|
@@ -423,7 +648,7 @@ function generateClickMethod(methodName, formattedDataTestId, alternateFormatted
|
|
|
423
648
|
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
424
649
|
],
|
|
425
650
|
(writer) => {
|
|
426
|
-
writer.writeLine(`await this.clickByTestId(
|
|
651
|
+
writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
|
|
427
652
|
}
|
|
428
653
|
),
|
|
429
654
|
createAsyncMethod(
|
|
@@ -443,7 +668,7 @@ function generateClickMethod(methodName, formattedDataTestId, alternateFormatted
|
|
|
443
668
|
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
444
669
|
],
|
|
445
670
|
(writer) => {
|
|
446
|
-
writer.writeLine(`await this.clickByTestId(
|
|
671
|
+
writer.writeLine(`await this.clickByTestId(${primaryTestIdExpr}, annotationText, wait);`);
|
|
447
672
|
}
|
|
448
673
|
),
|
|
449
674
|
createAsyncMethod(
|
|
@@ -455,31 +680,25 @@ function generateClickMethod(methodName, formattedDataTestId, alternateFormatted
|
|
|
455
680
|
)
|
|
456
681
|
];
|
|
457
682
|
}
|
|
458
|
-
function generateRadioMethod(methodName,
|
|
683
|
+
function generateRadioMethod(methodName, selector, parameters) {
|
|
459
684
|
const name = `select${methodName}`;
|
|
460
|
-
const
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
464
|
-
] : [createInlineParameter("annotationText", { type: "string", initializer: '""' })];
|
|
465
|
-
const testIdExpr = hasKey ? `\`${formattedDataTestId}\`` : `"${formattedDataTestId}"`;
|
|
685
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
686
|
+
const methodParameters = createParameters(selectorParams);
|
|
687
|
+
const testIdExpr = toTypeScriptPomPatternExpression(selector);
|
|
466
688
|
return [
|
|
467
|
-
createAsyncMethod(name,
|
|
689
|
+
createAsyncMethod(name, methodParameters, (writer) => {
|
|
468
690
|
writer.writeLine(`await this.clickByTestId(${testIdExpr}, annotationText);`);
|
|
469
691
|
})
|
|
470
692
|
];
|
|
471
693
|
}
|
|
472
|
-
function generateSelectMethod(methodName,
|
|
694
|
+
function generateSelectMethod(methodName, selector, parameters) {
|
|
473
695
|
const name = `select${methodName}`;
|
|
474
|
-
const
|
|
475
|
-
const selectorExpr =
|
|
696
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
697
|
+
const selectorExpr = `this.selectorForTestId(${toTypeScriptPomPatternExpression(selector)})`;
|
|
476
698
|
return [
|
|
477
699
|
createAsyncMethod(
|
|
478
700
|
name,
|
|
479
|
-
|
|
480
|
-
createInlineParameter("value", { type: "string" }),
|
|
481
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
482
|
-
],
|
|
701
|
+
createParameters(selectorParams),
|
|
483
702
|
(writer) => {
|
|
484
703
|
writer.writeLine(`const selector = ${selectorExpr};`);
|
|
485
704
|
writer.writeLine("await this.animateCursorToElement(selector, false, 500, annotationText);");
|
|
@@ -488,33 +707,28 @@ function generateSelectMethod(methodName, formattedDataTestId) {
|
|
|
488
707
|
)
|
|
489
708
|
];
|
|
490
709
|
}
|
|
491
|
-
function generateVSelectMethod(methodName,
|
|
710
|
+
function generateVSelectMethod(methodName, selector, parameters) {
|
|
492
711
|
const name = `select${methodName}`;
|
|
712
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
493
713
|
return [
|
|
494
714
|
createAsyncMethod(
|
|
495
715
|
name,
|
|
496
|
-
|
|
497
|
-
createInlineParameter("value", { type: "string" }),
|
|
498
|
-
createInlineParameter("timeOut", { type: "number", initializer: "500" }),
|
|
499
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
500
|
-
],
|
|
716
|
+
createParameters(selectorParams),
|
|
501
717
|
(writer) => {
|
|
502
|
-
writer.writeLine(`await this.selectVSelectByTestId(
|
|
718
|
+
writer.writeLine(`await this.selectVSelectByTestId(${toTypeScriptPomPatternExpression(selector)}, value, timeOut, annotationText);`);
|
|
503
719
|
}
|
|
504
720
|
)
|
|
505
721
|
];
|
|
506
722
|
}
|
|
507
|
-
function generateTypeMethod(methodName,
|
|
723
|
+
function generateTypeMethod(methodName, selector, parameters) {
|
|
508
724
|
const name = `type${methodName}`;
|
|
725
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
509
726
|
return [
|
|
510
727
|
createAsyncMethod(
|
|
511
728
|
name,
|
|
512
|
-
|
|
513
|
-
createInlineParameter("text", { type: "string" }),
|
|
514
|
-
createInlineParameter("annotationText", { type: "string", initializer: '""' })
|
|
515
|
-
],
|
|
729
|
+
createParameters(selectorParams),
|
|
516
730
|
(writer) => {
|
|
517
|
-
writer.writeLine(`await this.fillInputByTestId(
|
|
731
|
+
writer.writeLine(`await this.fillInputByTestId(${toTypeScriptPomPatternExpression(selector)}, text, annotationText);`);
|
|
518
732
|
}
|
|
519
733
|
)
|
|
520
734
|
];
|
|
@@ -529,30 +743,31 @@ function isAllDigits(value) {
|
|
|
529
743
|
}
|
|
530
744
|
return true;
|
|
531
745
|
}
|
|
532
|
-
function generateGetElementByDataTestId(methodName, nativeRole,
|
|
746
|
+
function generateGetElementByDataTestId(methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
|
|
533
747
|
const roleSuffix = upperFirst$1(nativeRole || "Element");
|
|
534
748
|
const baseName = upperFirst$1(methodName);
|
|
535
749
|
const numericSuffix = baseName.startsWith(roleSuffix) ? baseName.slice(roleSuffix.length) : "";
|
|
536
750
|
const hasRoleSuffix = baseName.endsWith(roleSuffix) || baseName.startsWith(roleSuffix) && isAllDigits(numericSuffix);
|
|
537
751
|
const propertyName = hasRoleSuffix ? `${baseName}` : `${baseName}${roleSuffix}`;
|
|
538
|
-
const
|
|
539
|
-
|
|
540
|
-
|
|
752
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
753
|
+
const indexedVariable = getIndexedPomPatternVariable(selector);
|
|
754
|
+
if (indexedVariable) {
|
|
755
|
+
const keyType = getPomParameter(selectorParams, indexedVariable)?.typeExpression || "string";
|
|
541
756
|
const keyedPropertyName = getterNameOverride ?? removeByKeySegment(propertyName);
|
|
542
757
|
return [
|
|
543
758
|
createClassGetter({
|
|
544
759
|
name: keyedPropertyName,
|
|
545
760
|
statements: [
|
|
546
|
-
`return this.keyedLocators((
|
|
761
|
+
`return this.keyedLocators((${indexedVariable}: ${keyType}) => this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)}));`
|
|
547
762
|
]
|
|
548
763
|
})
|
|
549
764
|
];
|
|
550
765
|
}
|
|
551
766
|
const finalPropertyName = getterNameOverride ?? propertyName;
|
|
552
|
-
const alternates =
|
|
767
|
+
const alternates = uniquePomStringPatterns(selector, alternateSelectors).slice(1);
|
|
553
768
|
if (alternates.length > 0) {
|
|
554
|
-
const all = [
|
|
555
|
-
const locatorExpr = all.map((id) => `this.locatorByTestId(${
|
|
769
|
+
const all = [selector, ...alternates];
|
|
770
|
+
const locatorExpr = all.map((id) => `this.locatorByTestId(${toTypeScriptPomPatternExpression(id)})`).reduce((acc, next) => `${acc}.or(${next})`);
|
|
556
771
|
return [
|
|
557
772
|
createClassGetter({
|
|
558
773
|
name: finalPropertyName,
|
|
@@ -563,21 +778,22 @@ function generateGetElementByDataTestId(methodName, nativeRole, formattedDataTes
|
|
|
563
778
|
return [
|
|
564
779
|
createClassGetter({
|
|
565
780
|
name: finalPropertyName,
|
|
566
|
-
statements: [`return this.locatorByTestId(
|
|
781
|
+
statements: [`return this.locatorByTestId(${toTypeScriptPomPatternExpression(selector)});`]
|
|
567
782
|
})
|
|
568
783
|
];
|
|
569
784
|
}
|
|
570
785
|
function generateNavigationMethod(args) {
|
|
571
|
-
const { targetPageObjectModelClass: target, baseMethodName,
|
|
786
|
+
const { targetPageObjectModelClass: target, baseMethodName, selector, alternateSelectors, parameters } = args;
|
|
572
787
|
const methodName = baseMethodName ? `goTo${upperFirst$1(baseMethodName)}` : `goTo${target.endsWith("Page") ? target.slice(0, -"Page".length) : target}`;
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
const
|
|
788
|
+
const selectorParams = orderPomPatternParameters(parameters, [selector]);
|
|
789
|
+
const methodParameters = createParameters(selectorParams);
|
|
790
|
+
const alternates = uniquePomStringPatterns(selector, alternateSelectors).slice(1);
|
|
791
|
+
const candidatesExpr = [toTypeScriptPomPatternExpression(selector), ...alternates.map((id) => toTypeScriptPomPatternExpression(id))].join(", ");
|
|
576
792
|
if (alternates.length > 0) {
|
|
577
793
|
return [
|
|
578
794
|
createClassMethod({
|
|
579
795
|
name: methodName,
|
|
580
|
-
parameters,
|
|
796
|
+
parameters: methodParameters,
|
|
581
797
|
returnType: `Fluent<${target}>`,
|
|
582
798
|
statements: (writer) => {
|
|
583
799
|
writer.write("return this.fluent(async () => ").block(() => {
|
|
@@ -605,11 +821,11 @@ function generateNavigationMethod(args) {
|
|
|
605
821
|
return [
|
|
606
822
|
createClassMethod({
|
|
607
823
|
name: methodName,
|
|
608
|
-
parameters,
|
|
824
|
+
parameters: methodParameters,
|
|
609
825
|
returnType: `Fluent<${target}>`,
|
|
610
826
|
statements: (writer) => {
|
|
611
827
|
writer.write("return this.fluent(async () => ").block(() => {
|
|
612
|
-
writer.writeLine(`await this.clickByTestId(
|
|
828
|
+
writer.writeLine(`await this.clickByTestId(${toTypeScriptPomPatternExpression(selector)});`);
|
|
613
829
|
writer.writeLine(`return new ${target}(this.page);`);
|
|
614
830
|
});
|
|
615
831
|
writer.writeLine(");");
|
|
@@ -617,15 +833,15 @@ function generateNavigationMethod(args) {
|
|
|
617
833
|
})
|
|
618
834
|
];
|
|
619
835
|
}
|
|
620
|
-
function generateViewObjectModelMembers(targetPageObjectModelClass, methodName, nativeRole,
|
|
836
|
+
function generateViewObjectModelMembers(targetPageObjectModelClass, methodName, nativeRole, selector, alternateSelectors, getterNameOverride, parameters) {
|
|
621
837
|
const baseMethodName = nativeRole === "radio" ? methodName || "Radio" : methodName;
|
|
622
838
|
const members = generateGetElementByDataTestId(
|
|
623
839
|
baseMethodName,
|
|
624
840
|
nativeRole,
|
|
625
|
-
|
|
626
|
-
|
|
841
|
+
selector,
|
|
842
|
+
alternateSelectors,
|
|
627
843
|
getterNameOverride,
|
|
628
|
-
|
|
844
|
+
parameters
|
|
629
845
|
);
|
|
630
846
|
if (targetPageObjectModelClass) {
|
|
631
847
|
return [
|
|
@@ -633,25 +849,25 @@ function generateViewObjectModelMembers(targetPageObjectModelClass, methodName,
|
|
|
633
849
|
...generateNavigationMethod({
|
|
634
850
|
targetPageObjectModelClass,
|
|
635
851
|
baseMethodName,
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
852
|
+
selector,
|
|
853
|
+
alternateSelectors,
|
|
854
|
+
parameters
|
|
639
855
|
})
|
|
640
856
|
];
|
|
641
857
|
}
|
|
642
858
|
if (nativeRole === "select") {
|
|
643
|
-
return [...members, ...generateSelectMethod(baseMethodName,
|
|
859
|
+
return [...members, ...generateSelectMethod(baseMethodName, selector, parameters)];
|
|
644
860
|
}
|
|
645
861
|
if (nativeRole === "vselect") {
|
|
646
|
-
return [...members, ...generateVSelectMethod(baseMethodName,
|
|
862
|
+
return [...members, ...generateVSelectMethod(baseMethodName, selector, parameters)];
|
|
647
863
|
}
|
|
648
864
|
if (nativeRole === "input") {
|
|
649
|
-
return [...members, ...generateTypeMethod(baseMethodName,
|
|
865
|
+
return [...members, ...generateTypeMethod(baseMethodName, selector, parameters)];
|
|
650
866
|
}
|
|
651
867
|
if (nativeRole === "radio") {
|
|
652
|
-
return [...members, ...generateRadioMethod(baseMethodName || "Radio",
|
|
868
|
+
return [...members, ...generateRadioMethod(baseMethodName || "Radio", selector, parameters)];
|
|
653
869
|
}
|
|
654
|
-
return [...members, ...generateClickMethod(baseMethodName,
|
|
870
|
+
return [...members, ...generateClickMethod(baseMethodName, selector, alternateSelectors, parameters)];
|
|
655
871
|
}
|
|
656
872
|
function isSimpleExpressionNode(value) {
|
|
657
873
|
return value !== null && "type" in value && value.type === NodeTypes.SIMPLE_EXPRESSION;
|
|
@@ -691,43 +907,72 @@ function buildPlaceholderParams(keys) {
|
|
|
691
907
|
params[k] = "__placeholder__";
|
|
692
908
|
return params;
|
|
693
909
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
910
|
+
const isNodeType = (node, type) => {
|
|
911
|
+
return node !== null && node.type === type;
|
|
912
|
+
};
|
|
913
|
+
const isStringLiteralNode = (node) => {
|
|
914
|
+
return isNodeType(node, "StringLiteral") && typeof node.value === "string";
|
|
915
|
+
};
|
|
916
|
+
const isIdentifierNode = (node) => {
|
|
917
|
+
return isNodeType(node, "Identifier") && typeof node.name === "string";
|
|
918
|
+
};
|
|
919
|
+
const isObjectPropertyNode = (node) => {
|
|
920
|
+
if (!isNodeType(node, "ObjectProperty"))
|
|
921
|
+
return false;
|
|
922
|
+
const n = node;
|
|
923
|
+
return typeof n.key === "object" && n.key !== null && typeof n.value === "object" && n.value !== null;
|
|
924
|
+
};
|
|
925
|
+
const isObjectExpressionNode = (node) => {
|
|
926
|
+
if (!isNodeType(node, "ObjectExpression"))
|
|
927
|
+
return false;
|
|
928
|
+
const n = node;
|
|
929
|
+
return Array.isArray(n.properties);
|
|
930
|
+
};
|
|
931
|
+
function materializeResolvedRouteTarget(target, paramKeys) {
|
|
932
|
+
if (typeof target === "string")
|
|
933
|
+
return target;
|
|
934
|
+
if (!paramKeys.length)
|
|
935
|
+
return target;
|
|
936
|
+
return {
|
|
937
|
+
...target,
|
|
938
|
+
params: buildPlaceholderParams(paramKeys)
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
function analyzeToDirectiveTarget(toDirective) {
|
|
942
|
+
if (!toDirective.exp) {
|
|
943
|
+
return {
|
|
944
|
+
kind: "unsupported",
|
|
945
|
+
rawSource: null,
|
|
946
|
+
reason: "missing-expression"
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
const rawSource = stringifyExpression(toDirective.exp).trim();
|
|
699
950
|
let expr;
|
|
700
951
|
try {
|
|
701
952
|
expr = parseExpression(rawSource, { plugins: ["typescript"] });
|
|
702
|
-
} catch {
|
|
703
|
-
return
|
|
953
|
+
} catch (error) {
|
|
954
|
+
return {
|
|
955
|
+
kind: "parse-error",
|
|
956
|
+
rawSource,
|
|
957
|
+
reason: "parse-error",
|
|
958
|
+
error: error instanceof Error ? error.message : String(error)
|
|
959
|
+
};
|
|
704
960
|
}
|
|
705
|
-
const isNodeType = (node, type) => {
|
|
706
|
-
return node !== null && node.type === type;
|
|
707
|
-
};
|
|
708
|
-
const isStringLiteralNode = (node) => {
|
|
709
|
-
return isNodeType(node, "StringLiteral") && typeof node.value === "string";
|
|
710
|
-
};
|
|
711
|
-
const isIdentifierNode = (node) => {
|
|
712
|
-
return isNodeType(node, "Identifier") && typeof node.name === "string";
|
|
713
|
-
};
|
|
714
|
-
const isObjectPropertyNode = (node) => {
|
|
715
|
-
if (!isNodeType(node, "ObjectProperty"))
|
|
716
|
-
return false;
|
|
717
|
-
const n = node;
|
|
718
|
-
return typeof n.key === "object" && n.key !== null && typeof n.value === "object" && n.value !== null;
|
|
719
|
-
};
|
|
720
|
-
const isObjectExpressionNode = (node) => {
|
|
721
|
-
if (!isNodeType(node, "ObjectExpression"))
|
|
722
|
-
return false;
|
|
723
|
-
const n = node;
|
|
724
|
-
return Array.isArray(n.properties);
|
|
725
|
-
};
|
|
726
961
|
if (isStringLiteralNode(expr)) {
|
|
727
|
-
return
|
|
962
|
+
return {
|
|
963
|
+
kind: "resolved",
|
|
964
|
+
rawSource,
|
|
965
|
+
target: expr.value,
|
|
966
|
+
routeNameKey: null,
|
|
967
|
+
paramKeys: []
|
|
968
|
+
};
|
|
728
969
|
}
|
|
729
970
|
if (!isObjectExpressionNode(expr)) {
|
|
730
|
-
return
|
|
971
|
+
return {
|
|
972
|
+
kind: "unsupported",
|
|
973
|
+
rawSource,
|
|
974
|
+
reason: "dynamic-expression"
|
|
975
|
+
};
|
|
731
976
|
}
|
|
732
977
|
const getStringField = (fieldName) => {
|
|
733
978
|
const prop = expr.properties.find((p) => {
|
|
@@ -748,7 +993,7 @@ function getRouteLocationLikeFromToDirective(toDirective) {
|
|
|
748
993
|
const key = p.key;
|
|
749
994
|
return isIdentifierNode(key) && key.name === "params" || isStringLiteralNode(key) && key.value === "params";
|
|
750
995
|
});
|
|
751
|
-
let
|
|
996
|
+
let paramKeys = [];
|
|
752
997
|
if (paramsProp && isObjectPropertyNode(paramsProp) && isObjectExpressionNode(paramsProp.value)) {
|
|
753
998
|
const keys = [];
|
|
754
999
|
for (const prop of paramsProp.value.properties) {
|
|
@@ -760,47 +1005,50 @@ function getRouteLocationLikeFromToDirective(toDirective) {
|
|
|
760
1005
|
else if (isStringLiteralNode(key))
|
|
761
1006
|
keys.push(key.value);
|
|
762
1007
|
}
|
|
763
|
-
|
|
764
|
-
params = buildPlaceholderParams(Array.from(new Set(keys)));
|
|
765
|
-
}
|
|
1008
|
+
paramKeys = Array.from(new Set(keys));
|
|
766
1009
|
}
|
|
767
1010
|
if (name) {
|
|
768
|
-
|
|
1011
|
+
const trimmed = name.trim();
|
|
1012
|
+
if (!trimmed.length) {
|
|
1013
|
+
return {
|
|
1014
|
+
kind: "unsupported",
|
|
1015
|
+
rawSource,
|
|
1016
|
+
reason: "missing-name-or-path"
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
return {
|
|
1020
|
+
kind: "resolved",
|
|
1021
|
+
rawSource,
|
|
1022
|
+
target: { name },
|
|
1023
|
+
routeNameKey: toPascalCaseRouteKey(trimmed),
|
|
1024
|
+
paramKeys
|
|
1025
|
+
};
|
|
769
1026
|
}
|
|
770
1027
|
if (path2) {
|
|
771
|
-
return {
|
|
1028
|
+
return {
|
|
1029
|
+
kind: "resolved",
|
|
1030
|
+
rawSource,
|
|
1031
|
+
target: { path: path2 },
|
|
1032
|
+
routeNameKey: null,
|
|
1033
|
+
paramKeys
|
|
1034
|
+
};
|
|
772
1035
|
}
|
|
773
|
-
return
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
return null;
|
|
779
|
-
const name = to.name;
|
|
780
|
-
if (typeof name !== "string")
|
|
781
|
-
return null;
|
|
782
|
-
const trimmed = name.trim();
|
|
783
|
-
if (!trimmed.length)
|
|
784
|
-
return null;
|
|
785
|
-
return toPascalCaseRouteKey(trimmed);
|
|
786
|
-
}
|
|
787
|
-
function getRouteNameKeyFromToDirective(toDirective) {
|
|
788
|
-
const objectName = toDirectiveObjectFieldNameValue$1(toDirective);
|
|
789
|
-
if (objectName)
|
|
790
|
-
return objectName;
|
|
791
|
-
return null;
|
|
1036
|
+
return {
|
|
1037
|
+
kind: "unsupported",
|
|
1038
|
+
rawSource,
|
|
1039
|
+
reason: "missing-name-or-path"
|
|
1040
|
+
};
|
|
792
1041
|
}
|
|
793
1042
|
function tryResolveToDirectiveTargetComponentName(toDirective) {
|
|
794
|
-
const
|
|
795
|
-
if (
|
|
796
|
-
const resolved = resolveToComponentName(
|
|
1043
|
+
const analysis = analyzeToDirectiveTarget(toDirective);
|
|
1044
|
+
if (analysis.kind === "resolved" && resolveToComponentName) {
|
|
1045
|
+
const resolved = resolveToComponentName(materializeResolvedRouteTarget(analysis.target, analysis.paramKeys));
|
|
797
1046
|
if (resolved)
|
|
798
1047
|
return resolved;
|
|
799
1048
|
}
|
|
800
|
-
|
|
801
|
-
if (!key || !routeNameToComponentName)
|
|
1049
|
+
if (analysis.kind !== "resolved" || !analysis.routeNameKey || !routeNameToComponentName)
|
|
802
1050
|
return null;
|
|
803
|
-
return routeNameToComponentName.get(
|
|
1051
|
+
return routeNameToComponentName.get(analysis.routeNameKey) ?? null;
|
|
804
1052
|
}
|
|
805
1053
|
function getDataTestIdFromGroupOption(text) {
|
|
806
1054
|
return text.replace(/[-_]/g, " ").split(" ").filter((a) => a).map((str) => {
|
|
@@ -835,11 +1083,81 @@ function staticAttributeValue(value) {
|
|
|
835
1083
|
return { kind: "static", value };
|
|
836
1084
|
}
|
|
837
1085
|
function templateAttributeValue(template) {
|
|
838
|
-
|
|
1086
|
+
const parsedTemplate = tryParseTemplateFragment(template);
|
|
1087
|
+
if (!parsedTemplate) {
|
|
1088
|
+
throw new Error(`[vue-pom-generator] Failed to parse generated template fragment: ${template}`);
|
|
1089
|
+
}
|
|
1090
|
+
return { kind: "template", template, parsedTemplate };
|
|
839
1091
|
}
|
|
840
1092
|
function getAttributeValueText(value) {
|
|
841
1093
|
return value.kind === "static" ? value.value : value.template;
|
|
842
1094
|
}
|
|
1095
|
+
function getVueExpressionSource(expression, ...preferredViews) {
|
|
1096
|
+
if (!expression) {
|
|
1097
|
+
return "";
|
|
1098
|
+
}
|
|
1099
|
+
for (const view of preferredViews) {
|
|
1100
|
+
let value = "";
|
|
1101
|
+
switch (view) {
|
|
1102
|
+
case "content":
|
|
1103
|
+
value = expression.type === NodeTypes.SIMPLE_EXPRESSION ? expression.content : "";
|
|
1104
|
+
break;
|
|
1105
|
+
case "loc":
|
|
1106
|
+
value = expression.loc?.source ?? "";
|
|
1107
|
+
break;
|
|
1108
|
+
case "compiled":
|
|
1109
|
+
try {
|
|
1110
|
+
value = stringifyExpression(expression);
|
|
1111
|
+
} catch {
|
|
1112
|
+
value = "";
|
|
1113
|
+
}
|
|
1114
|
+
break;
|
|
1115
|
+
default:
|
|
1116
|
+
value = "";
|
|
1117
|
+
break;
|
|
1118
|
+
}
|
|
1119
|
+
const trimmed = value.trim();
|
|
1120
|
+
if (trimmed) {
|
|
1121
|
+
return trimmed;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
return "";
|
|
1125
|
+
}
|
|
1126
|
+
function tryGetExistingVueExpressionAst(expression) {
|
|
1127
|
+
if (!expression) {
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
const ast = "ast" in expression ? expression.ast : null;
|
|
1131
|
+
return ast && "type" in ast ? ast : null;
|
|
1132
|
+
}
|
|
1133
|
+
function tryParseBabelExpressionFromSource(source, plugins) {
|
|
1134
|
+
const trimmed = source.trim();
|
|
1135
|
+
if (!trimmed) {
|
|
1136
|
+
return null;
|
|
1137
|
+
}
|
|
1138
|
+
try {
|
|
1139
|
+
return parseExpression(trimmed, { plugins });
|
|
1140
|
+
} catch {
|
|
1141
|
+
return null;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
function tryGetVueExpressionAst(expression, options) {
|
|
1145
|
+
if (!expression) {
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
if (options?.preferExistingAst !== false) {
|
|
1149
|
+
const existingAst = tryGetExistingVueExpressionAst(expression);
|
|
1150
|
+
if (existingAst) {
|
|
1151
|
+
return existingAst;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
const source = getVueExpressionSource(expression, ...options?.preferredViews ?? ["content", "loc", "compiled"]);
|
|
1155
|
+
return source ? tryParseBabelExpressionFromSource(source, options?.plugins ?? ["typescript"]) : null;
|
|
1156
|
+
}
|
|
1157
|
+
function tryGetDirectiveBabelAst(directive, options) {
|
|
1158
|
+
const exp = directive.exp && (directive.exp.type === NodeTypes.SIMPLE_EXPRESSION || directive.exp.type === NodeTypes.COMPOUND_EXPRESSION) ? directive.exp : null;
|
|
1159
|
+
return tryGetVueExpressionAst(exp, options);
|
|
1160
|
+
}
|
|
843
1161
|
function toPascalCase(str) {
|
|
844
1162
|
const cleaned = (str ?? "").replace(/\$\{[^}]*\}/g, " ").replace(/[^a-z0-9]+/gi, " ").trim();
|
|
845
1163
|
if (!cleaned) {
|
|
@@ -881,32 +1199,14 @@ function tryGetClickDirective(node) {
|
|
|
881
1199
|
function nodeHasClickDirective(node) {
|
|
882
1200
|
return tryGetClickDirective(node) !== void 0;
|
|
883
1201
|
}
|
|
884
|
-
function
|
|
1202
|
+
function findTemplateSlotScopeExpression(node) {
|
|
885
1203
|
if (node.tag !== "template") {
|
|
886
1204
|
return null;
|
|
887
1205
|
}
|
|
888
1206
|
const slotProp = node.props.find((prop) => {
|
|
889
1207
|
return prop.type === NodeTypes.DIRECTIVE && prop.name === "slot";
|
|
890
1208
|
});
|
|
891
|
-
|
|
892
|
-
if (slotProp.exp.type === NodeTypes.SIMPLE_EXPRESSION) {
|
|
893
|
-
return slotProp.exp.content;
|
|
894
|
-
}
|
|
895
|
-
if (slotProp.exp.type === NodeTypes.COMPOUND_EXPRESSION) {
|
|
896
|
-
return stringifyExpression(slotProp.exp);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
return null;
|
|
900
|
-
}
|
|
901
|
-
function isSimpleScopeIdentifier(value) {
|
|
902
|
-
if (!value) {
|
|
903
|
-
return false;
|
|
904
|
-
}
|
|
905
|
-
try {
|
|
906
|
-
return isIdentifier(parseExpression(value, { plugins: ["typescript"] }));
|
|
907
|
-
} catch {
|
|
908
|
-
return false;
|
|
909
|
-
}
|
|
1209
|
+
return slotProp?.exp && (slotProp.exp.type === NodeTypes.SIMPLE_EXPRESSION || slotProp.exp.type === NodeTypes.COMPOUND_EXPRESSION) ? slotProp.exp : null;
|
|
910
1210
|
}
|
|
911
1211
|
function buildSlotScopeFallbackKeyExpression(identifier) {
|
|
912
1212
|
return `${identifier}.key ?? ${identifier}.data?.id ?? ${identifier}.id ?? ${identifier}.value ?? ${identifier}`;
|
|
@@ -1008,43 +1308,64 @@ function tryGetSlotScopeKeyCandidate(node) {
|
|
|
1008
1308
|
}
|
|
1009
1309
|
return best;
|
|
1010
1310
|
}
|
|
1011
|
-
function
|
|
1012
|
-
const
|
|
1013
|
-
if (
|
|
1311
|
+
function tryGetTemplateSlotScopeBindingNode(expression) {
|
|
1312
|
+
const ast = tryGetExistingVueExpressionAst(expression);
|
|
1313
|
+
if (ast) {
|
|
1314
|
+
if (isArrowFunctionExpression(ast)) {
|
|
1315
|
+
return ast.params[0] ?? null;
|
|
1316
|
+
}
|
|
1317
|
+
return ast;
|
|
1318
|
+
}
|
|
1319
|
+
const rawSource = getVueExpressionSource(expression, "content", "loc", "compiled");
|
|
1320
|
+
if (!rawSource) {
|
|
1014
1321
|
return null;
|
|
1015
1322
|
}
|
|
1016
|
-
|
|
1017
|
-
return
|
|
1323
|
+
try {
|
|
1324
|
+
return parseExpression(rawSource, { plugins: ["typescript"] });
|
|
1325
|
+
} catch {
|
|
1018
1326
|
}
|
|
1019
1327
|
try {
|
|
1020
|
-
const parsed = parse(`(${
|
|
1328
|
+
const parsed = parse(`(${rawSource}) => {}`, {
|
|
1021
1329
|
sourceType: "module",
|
|
1022
1330
|
plugins: ["typescript"]
|
|
1023
1331
|
});
|
|
1024
1332
|
const statement = parsed.program.body[0];
|
|
1025
1333
|
if (statement && isExpressionStatement(statement) && isArrowFunctionExpression(statement.expression)) {
|
|
1026
|
-
return
|
|
1334
|
+
return statement.expression.params[0] ?? null;
|
|
1027
1335
|
}
|
|
1028
1336
|
} catch {
|
|
1337
|
+
return null;
|
|
1029
1338
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1339
|
+
return null;
|
|
1340
|
+
}
|
|
1341
|
+
function toResolvedTemplateFragment(source) {
|
|
1342
|
+
const templateLiteral = tryUnwrapTemplateLiteralSource(source);
|
|
1343
|
+
if (templateLiteral) {
|
|
1344
|
+
return {
|
|
1345
|
+
template: templateLiteral.template,
|
|
1346
|
+
rawExpression: null
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
return toInterpolatedTemplateFragment(source);
|
|
1350
|
+
}
|
|
1351
|
+
function toResolvedKeyInfo(selectorSource, runtimeSource = selectorSource) {
|
|
1352
|
+
const selectorFragment = selectorSource ? toResolvedTemplateFragment(selectorSource) : null;
|
|
1353
|
+
const runtimeFragment = runtimeSource ? toResolvedTemplateFragment(runtimeSource) : null;
|
|
1354
|
+
const selectorTemplate = selectorFragment?.template ?? runtimeFragment?.template ?? null;
|
|
1355
|
+
const runtimeTemplate = runtimeFragment?.template ?? selectorFragment?.template ?? null;
|
|
1356
|
+
if (!selectorTemplate || !runtimeTemplate) {
|
|
1357
|
+
return null;
|
|
1046
1358
|
}
|
|
1047
|
-
return
|
|
1359
|
+
return {
|
|
1360
|
+
selectorFragment: selectorTemplate,
|
|
1361
|
+
runtimeFragment: runtimeTemplate,
|
|
1362
|
+
rawExpression: runtimeFragment?.rawExpression ?? selectorFragment?.rawExpression ?? null
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
function tryGetTemplateSlotScopeKeyInfo(expression) {
|
|
1366
|
+
const bindingNode = tryGetTemplateSlotScopeBindingNode(expression);
|
|
1367
|
+
const candidateExpression = bindingNode ? tryGetSlotScopeKeyCandidate(bindingNode)?.expression ?? null : null;
|
|
1368
|
+
return candidateExpression ? toResolvedKeyInfo(candidateExpression) : null;
|
|
1048
1369
|
}
|
|
1049
1370
|
function nodeHasToDirective(node) {
|
|
1050
1371
|
const toDirective = findDirectiveByName(node, "bind", "to");
|
|
@@ -1053,51 +1374,160 @@ function nodeHasToDirective(node) {
|
|
|
1053
1374
|
}
|
|
1054
1375
|
return void 0;
|
|
1055
1376
|
}
|
|
1056
|
-
function nodeHasForDirective(node) {
|
|
1057
|
-
return node.props.some(
|
|
1058
|
-
(attr) => attr.type === NodeTypes.DIRECTIVE && attr.name === "for"
|
|
1059
|
-
);
|
|
1060
|
-
}
|
|
1061
1377
|
function getKeyDirective(node) {
|
|
1062
1378
|
return findDirectiveByName(node, "bind", "key") ?? null;
|
|
1063
1379
|
}
|
|
1064
|
-
function
|
|
1065
|
-
const
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
return `\${${rawSource}}`;
|
|
1380
|
+
function tryUnwrapTemplateLiteralSource(source) {
|
|
1381
|
+
const rawSource = source.trim();
|
|
1382
|
+
if (!rawSource) {
|
|
1383
|
+
return null;
|
|
1069
1384
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1385
|
+
let ast = null;
|
|
1386
|
+
try {
|
|
1387
|
+
ast = parseExpression(rawSource, { plugins: ["typescript"] });
|
|
1388
|
+
} catch {
|
|
1389
|
+
return null;
|
|
1390
|
+
}
|
|
1391
|
+
if (!ast || !isTemplateLiteral(ast)) {
|
|
1392
|
+
return null;
|
|
1393
|
+
}
|
|
1394
|
+
const cooked = ast.quasis.map((quasi) => quasi.value.cooked ?? "").join("");
|
|
1395
|
+
try {
|
|
1396
|
+
const start = typeof ast.start === "number" ? ast.start + 1 : 1;
|
|
1397
|
+
const end = typeof ast.end === "number" ? ast.end - 1 : rawSource.length - 1;
|
|
1398
|
+
return {
|
|
1399
|
+
template: rawSource.slice(start, end) || cooked,
|
|
1400
|
+
expressionCount: ast.expressions.length
|
|
1401
|
+
};
|
|
1402
|
+
} catch {
|
|
1403
|
+
return {
|
|
1404
|
+
template: cooked,
|
|
1405
|
+
expressionCount: ast.expressions.length
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
function tryUnwrapTemplateLiteralExpressionSource(expression) {
|
|
1410
|
+
const rawSource = getVueExpressionSource(expression, "loc", "compiled");
|
|
1411
|
+
return rawSource ? tryUnwrapTemplateLiteralSource(rawSource) : null;
|
|
1412
|
+
}
|
|
1413
|
+
function tryParseTemplateFragment(fragment) {
|
|
1414
|
+
if (!fragment) {
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1417
|
+
try {
|
|
1418
|
+
const source = `\`${fragment}\``;
|
|
1419
|
+
const ast = parseExpression(source, { plugins: ["typescript"] });
|
|
1420
|
+
return isTemplateLiteral(ast) ? { source, templateLiteral: ast } : null;
|
|
1421
|
+
} catch {
|
|
1422
|
+
return null;
|
|
1074
1423
|
}
|
|
1075
|
-
|
|
1424
|
+
}
|
|
1425
|
+
function getTemplateExpressionSource(parsedTemplate, index) {
|
|
1426
|
+
const expression = parsedTemplate.templateLiteral.expressions[index];
|
|
1427
|
+
if (!expression) {
|
|
1428
|
+
return null;
|
|
1429
|
+
}
|
|
1430
|
+
const start = typeof expression.start === "number" ? expression.start : null;
|
|
1431
|
+
const end = typeof expression.end === "number" ? expression.end : null;
|
|
1432
|
+
if (start === null || end === null) {
|
|
1433
|
+
return null;
|
|
1434
|
+
}
|
|
1435
|
+
return parsedTemplate.source.slice(start, end);
|
|
1436
|
+
}
|
|
1437
|
+
function getSingleExpressionTemplateFragment(parsedTemplate) {
|
|
1438
|
+
const { templateLiteral } = parsedTemplate;
|
|
1439
|
+
if (templateLiteral.expressions.length !== 1 || templateLiteral.quasis.length !== 2) {
|
|
1440
|
+
return null;
|
|
1441
|
+
}
|
|
1442
|
+
const expressionSource = getTemplateExpressionSource(parsedTemplate, 0);
|
|
1443
|
+
if (expressionSource === null) {
|
|
1444
|
+
return null;
|
|
1445
|
+
}
|
|
1446
|
+
return {
|
|
1447
|
+
prefix: templateLiteral.quasis[0]?.value.raw ?? "",
|
|
1448
|
+
expressionSource,
|
|
1449
|
+
suffix: templateLiteral.quasis[1]?.value.raw ?? ""
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
function templateFragmentContainsSingleExpression(container, candidate) {
|
|
1453
|
+
const containerFragment = getSingleExpressionTemplateFragment(container);
|
|
1454
|
+
const candidateFragment = getSingleExpressionTemplateFragment(candidate);
|
|
1455
|
+
if (!containerFragment || !candidateFragment) {
|
|
1456
|
+
return false;
|
|
1457
|
+
}
|
|
1458
|
+
return containerFragment.expressionSource === candidateFragment.expressionSource && containerFragment.prefix.endsWith(candidateFragment.prefix) && containerFragment.suffix.startsWith(candidateFragment.suffix);
|
|
1459
|
+
}
|
|
1460
|
+
function hasTemplateInterpolationExpressions(fragment) {
|
|
1461
|
+
return (tryParseTemplateFragment(fragment)?.templateLiteral.expressions.length ?? 0) > 0;
|
|
1462
|
+
}
|
|
1463
|
+
function toInterpolatedTemplateFragment(fragment) {
|
|
1464
|
+
if (!fragment) {
|
|
1465
|
+
return null;
|
|
1466
|
+
}
|
|
1467
|
+
if (hasTemplateInterpolationExpressions(fragment)) {
|
|
1468
|
+
return { template: fragment, rawExpression: null };
|
|
1469
|
+
}
|
|
1470
|
+
return {
|
|
1471
|
+
template: `\${${fragment}}`,
|
|
1472
|
+
rawExpression: fragment
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
function renderTemplateLiteralExpression(templateValue) {
|
|
1476
|
+
const templateLiteralSource = templateValue.parsedTemplate.source;
|
|
1477
|
+
const templateLiteral = templateValue.parsedTemplate.templateLiteral;
|
|
1478
|
+
const writer = createTypeScriptWriter();
|
|
1479
|
+
writer.write("`");
|
|
1480
|
+
for (let i = 0; i < templateLiteral.quasis.length; i += 1) {
|
|
1481
|
+
writer.write(templateLiteral.quasis[i]?.value.raw ?? "");
|
|
1482
|
+
const interpolation = templateLiteral.expressions[i];
|
|
1483
|
+
if (!interpolation) {
|
|
1484
|
+
continue;
|
|
1485
|
+
}
|
|
1486
|
+
const start = typeof interpolation.start === "number" ? interpolation.start : null;
|
|
1487
|
+
const end = typeof interpolation.end === "number" ? interpolation.end : null;
|
|
1488
|
+
if (start === null || end === null) {
|
|
1489
|
+
return templateLiteralSource;
|
|
1490
|
+
}
|
|
1491
|
+
writer.write("${");
|
|
1492
|
+
writer.write(templateLiteralSource.slice(start, end));
|
|
1493
|
+
writer.write("}");
|
|
1494
|
+
}
|
|
1495
|
+
writer.write("`");
|
|
1496
|
+
return writer.toString();
|
|
1497
|
+
}
|
|
1498
|
+
function getKeyDirectiveExpression(node) {
|
|
1499
|
+
const keyDirective = getKeyDirective(node);
|
|
1500
|
+
return keyDirective?.exp && (keyDirective.exp.type === NodeTypes.SIMPLE_EXPRESSION || keyDirective.exp.type === NodeTypes.COMPOUND_EXPRESSION) ? keyDirective.exp : null;
|
|
1501
|
+
}
|
|
1502
|
+
function getKeyDirectiveInfo(node) {
|
|
1503
|
+
const keyExpression = getKeyDirectiveExpression(node);
|
|
1504
|
+
if (!keyExpression) {
|
|
1505
|
+
return null;
|
|
1506
|
+
}
|
|
1507
|
+
const selectorSource = getVueExpressionSource(keyExpression, "compiled", "loc");
|
|
1508
|
+
const runtimeSource = getVueExpressionSource(keyExpression, "loc", "compiled");
|
|
1509
|
+
return toResolvedKeyInfo(selectorSource, runtimeSource);
|
|
1076
1510
|
}
|
|
1077
1511
|
function getModelBindingValues(node) {
|
|
1078
1512
|
let vModel = "";
|
|
1079
1513
|
const vModelDirective = findDirectiveByName(node, "model");
|
|
1080
|
-
if (vModelDirective?.exp
|
|
1081
|
-
vModel = toPascalCase(vModelDirective.exp
|
|
1514
|
+
if (vModelDirective?.exp && (vModelDirective.exp.type === NodeTypes.SIMPLE_EXPRESSION || vModelDirective.exp.type === NodeTypes.COMPOUND_EXPRESSION)) {
|
|
1515
|
+
vModel = toPascalCase(getVueExpressionSource(vModelDirective.exp, "loc", "content"));
|
|
1082
1516
|
}
|
|
1083
1517
|
let modelValue = null;
|
|
1084
1518
|
const modelValueDirective = findDirectiveByName(node, "bind", "modelValue");
|
|
1085
|
-
|
|
1086
|
-
|
|
1519
|
+
const modelValueAst = modelValueDirective ? tryGetDirectiveBabelAst(modelValueDirective, {
|
|
1520
|
+
preferredViews: ["loc", "compiled"],
|
|
1521
|
+
plugins: ["typescript"],
|
|
1522
|
+
preferExistingAst: false
|
|
1523
|
+
}) : null;
|
|
1524
|
+
if (modelValueAst) {
|
|
1525
|
+
const { name: mv } = getClickHandlerNameFromAst(modelValueAst);
|
|
1087
1526
|
modelValue = mv;
|
|
1088
1527
|
}
|
|
1089
1528
|
return { vModel, modelValue };
|
|
1090
1529
|
}
|
|
1091
|
-
function
|
|
1092
|
-
if (node.isSelfClosing) {
|
|
1093
|
-
const hasForDirective = nodeHasForDirective(node);
|
|
1094
|
-
if (hasForDirective) {
|
|
1095
|
-
return getKeyDirectiveValue(node);
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
return null;
|
|
1099
|
-
}
|
|
1100
|
-
function getIdOrName(node) {
|
|
1530
|
+
function getStaticIdOrNameHint(node) {
|
|
1101
1531
|
let idAttr = findAttributeByKey(node, "id");
|
|
1102
1532
|
if (!idAttr) {
|
|
1103
1533
|
idAttr = findAttributeByKey(node, "name");
|
|
@@ -1105,8 +1535,9 @@ function getIdOrName(node) {
|
|
|
1105
1535
|
let identifier = idAttr?.value?.content ?? "";
|
|
1106
1536
|
if (!identifier) {
|
|
1107
1537
|
const dynamicIdAttr = findDirectiveByName(node, "bind", "id");
|
|
1108
|
-
|
|
1109
|
-
|
|
1538
|
+
const dynamicNameAttr = findDirectiveByName(node, "bind", "name");
|
|
1539
|
+
if (dynamicIdAttr?.exp || dynamicNameAttr?.exp) {
|
|
1540
|
+
return "";
|
|
1110
1541
|
}
|
|
1111
1542
|
}
|
|
1112
1543
|
if (identifier.includes("-")) {
|
|
@@ -1117,20 +1548,20 @@ function getIdOrName(node) {
|
|
|
1117
1548
|
}
|
|
1118
1549
|
return identifier;
|
|
1119
1550
|
}
|
|
1120
|
-
function
|
|
1551
|
+
function getContainedInSlotDataKeyInfo(node, hierarchyMap2) {
|
|
1121
1552
|
let parent = getParent(hierarchyMap2, node);
|
|
1122
1553
|
while (parent) {
|
|
1123
1554
|
if (parent.type === NodeTypes.ELEMENT && parent.tag === "template") {
|
|
1124
|
-
const
|
|
1125
|
-
if (
|
|
1126
|
-
return
|
|
1555
|
+
const slotScopeExpression = findTemplateSlotScopeExpression(parent);
|
|
1556
|
+
if (slotScopeExpression) {
|
|
1557
|
+
return tryGetTemplateSlotScopeKeyInfo(slotScopeExpression);
|
|
1127
1558
|
}
|
|
1128
1559
|
}
|
|
1129
1560
|
parent = getParent(hierarchyMap2, parent);
|
|
1130
1561
|
}
|
|
1131
1562
|
return null;
|
|
1132
1563
|
}
|
|
1133
|
-
function
|
|
1564
|
+
function getContainedInVForDirectiveKeyInfo(context, node, hierarchyMap2) {
|
|
1134
1565
|
if (!context.scopes.vFor || context.scopes.vFor === 0) {
|
|
1135
1566
|
return null;
|
|
1136
1567
|
}
|
|
@@ -1139,8 +1570,7 @@ function getContainedInVForDirectiveKeyValue(context, node, hierarchyMap2) {
|
|
|
1139
1570
|
if (parent.type === NodeTypes.ELEMENT) {
|
|
1140
1571
|
const forDirective = findDirectiveByName(parent, "for");
|
|
1141
1572
|
if (forDirective) {
|
|
1142
|
-
|
|
1143
|
-
return keyValue;
|
|
1573
|
+
return getKeyDirectiveInfo(parent);
|
|
1144
1574
|
}
|
|
1145
1575
|
}
|
|
1146
1576
|
parent = getParent(hierarchyMap2, parent);
|
|
@@ -1166,13 +1596,7 @@ function tryGetContainedInStaticVForSourceLiteralValues(context, _node, _hierarc
|
|
|
1166
1596
|
if (simpleSourceExp.constType === ConstantTypes.NOT_CONSTANT) {
|
|
1167
1597
|
return null;
|
|
1168
1598
|
}
|
|
1169
|
-
const iterableRaw = (
|
|
1170
|
-
try {
|
|
1171
|
-
return stringifyExpression(simpleSourceExp).trim();
|
|
1172
|
-
} catch {
|
|
1173
|
-
return (simpleSourceExp.loc?.source ?? "").trim();
|
|
1174
|
-
}
|
|
1175
|
-
})();
|
|
1599
|
+
const iterableRaw = getVueExpressionSource(simpleSourceExp, "compiled", "loc");
|
|
1176
1600
|
if (!iterableRaw) {
|
|
1177
1601
|
return null;
|
|
1178
1602
|
}
|
|
@@ -1228,91 +1652,95 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1228
1652
|
return null;
|
|
1229
1653
|
}
|
|
1230
1654
|
const exp = handlerDirective.exp;
|
|
1231
|
-
const source = (exp
|
|
1655
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
1232
1656
|
if (!source) {
|
|
1233
1657
|
return null;
|
|
1234
1658
|
}
|
|
1235
1659
|
const mergeKey = `handler:expr:${source}`;
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1660
|
+
const expr = tryGetDirectiveBabelAst(handlerDirective, {
|
|
1661
|
+
preferredViews: ["content", "compiled"],
|
|
1662
|
+
plugins: ["typescript", "jsx"],
|
|
1663
|
+
// Vue's compiler AST can encode `_ctx.foo` as an Identifier name instead of a MemberExpression.
|
|
1664
|
+
// That is fine for Vue codegen, but our semantic-name extraction needs a normal Babel parse tree.
|
|
1665
|
+
preferExistingAst: false
|
|
1666
|
+
});
|
|
1667
|
+
if (!expr) {
|
|
1240
1668
|
return null;
|
|
1241
1669
|
}
|
|
1242
|
-
const
|
|
1670
|
+
const isNodeType2 = (node2, type) => {
|
|
1243
1671
|
return node2 !== null && node2.type === type;
|
|
1244
1672
|
};
|
|
1245
|
-
const
|
|
1246
|
-
return
|
|
1673
|
+
const isIdentifierNode2 = (node2) => {
|
|
1674
|
+
return isNodeType2(node2, "Identifier") && typeof node2.name === "string";
|
|
1247
1675
|
};
|
|
1248
|
-
const
|
|
1249
|
-
return
|
|
1676
|
+
const isStringLiteralNode2 = (node2) => {
|
|
1677
|
+
return isNodeType2(node2, "StringLiteral") && typeof node2.value === "string";
|
|
1250
1678
|
};
|
|
1251
1679
|
const isBooleanLiteralNode = (node2) => {
|
|
1252
|
-
return
|
|
1680
|
+
return isNodeType2(node2, "BooleanLiteral") && typeof node2.value === "boolean";
|
|
1253
1681
|
};
|
|
1254
1682
|
const isNumericLiteralNode = (node2) => {
|
|
1255
|
-
return
|
|
1683
|
+
return isNodeType2(node2, "NumericLiteral") && typeof node2.value === "number";
|
|
1256
1684
|
};
|
|
1257
1685
|
const isNullLiteralNode = (node2) => {
|
|
1258
|
-
return
|
|
1686
|
+
return isNodeType2(node2, "NullLiteral");
|
|
1259
1687
|
};
|
|
1260
1688
|
const isMemberExpressionNode = (node2) => {
|
|
1261
|
-
if (!
|
|
1689
|
+
if (!isNodeType2(node2, "MemberExpression"))
|
|
1262
1690
|
return false;
|
|
1263
1691
|
const n = node2;
|
|
1264
1692
|
return typeof n.computed === "boolean" && typeof n.object === "object" && n.object !== null && typeof n.property === "object" && n.property !== null;
|
|
1265
1693
|
};
|
|
1266
1694
|
const isCallExpressionNode = (node2) => {
|
|
1267
|
-
if (!
|
|
1695
|
+
if (!isNodeType2(node2, "CallExpression"))
|
|
1268
1696
|
return false;
|
|
1269
1697
|
const n = node2;
|
|
1270
1698
|
return typeof n.callee === "object" && n.callee !== null && Array.isArray(n.arguments);
|
|
1271
1699
|
};
|
|
1272
1700
|
const isAwaitExpressionNode = (node2) => {
|
|
1273
|
-
if (!
|
|
1701
|
+
if (!isNodeType2(node2, "AwaitExpression"))
|
|
1274
1702
|
return false;
|
|
1275
1703
|
const n = node2;
|
|
1276
1704
|
return typeof n.argument === "object" && n.argument !== null;
|
|
1277
1705
|
};
|
|
1278
1706
|
const isAssignmentExpressionNode = (node2) => {
|
|
1279
|
-
if (!
|
|
1707
|
+
if (!isNodeType2(node2, "AssignmentExpression"))
|
|
1280
1708
|
return false;
|
|
1281
1709
|
const n = node2;
|
|
1282
1710
|
return typeof n.left === "object" && n.left !== null && typeof n.right === "object" && n.right !== null;
|
|
1283
1711
|
};
|
|
1284
1712
|
const isArrowFunctionExpressionNode = (node2) => {
|
|
1285
|
-
if (!
|
|
1713
|
+
if (!isNodeType2(node2, "ArrowFunctionExpression"))
|
|
1286
1714
|
return false;
|
|
1287
1715
|
const n = node2;
|
|
1288
1716
|
return typeof n.body === "object" && n.body !== null;
|
|
1289
1717
|
};
|
|
1290
1718
|
const isBlockStatementNode = (node2) => {
|
|
1291
|
-
if (!
|
|
1719
|
+
if (!isNodeType2(node2, "BlockStatement"))
|
|
1292
1720
|
return false;
|
|
1293
1721
|
const n = node2;
|
|
1294
1722
|
return Array.isArray(n.body);
|
|
1295
1723
|
};
|
|
1296
1724
|
const isExpressionStatementNode = (node2) => {
|
|
1297
|
-
if (!
|
|
1725
|
+
if (!isNodeType2(node2, "ExpressionStatement"))
|
|
1298
1726
|
return false;
|
|
1299
1727
|
const n = node2;
|
|
1300
1728
|
return typeof n.expression === "object" && n.expression !== null;
|
|
1301
1729
|
};
|
|
1302
1730
|
const isReturnStatementNode = (node2) => {
|
|
1303
|
-
if (!
|
|
1731
|
+
if (!isNodeType2(node2, "ReturnStatement"))
|
|
1304
1732
|
return false;
|
|
1305
1733
|
const n = node2;
|
|
1306
1734
|
return typeof n.argument === "object" || n.argument === null;
|
|
1307
1735
|
};
|
|
1308
|
-
const
|
|
1309
|
-
if (!
|
|
1736
|
+
const isObjectExpressionNode2 = (node2) => {
|
|
1737
|
+
if (!isNodeType2(node2, "ObjectExpression"))
|
|
1310
1738
|
return false;
|
|
1311
1739
|
const n = node2;
|
|
1312
1740
|
return Array.isArray(n.properties);
|
|
1313
1741
|
};
|
|
1314
|
-
const
|
|
1315
|
-
if (!
|
|
1742
|
+
const isObjectPropertyNode2 = (node2) => {
|
|
1743
|
+
if (!isNodeType2(node2, "ObjectProperty"))
|
|
1316
1744
|
return false;
|
|
1317
1745
|
const n = node2;
|
|
1318
1746
|
return typeof n.computed === "boolean" && typeof n.key === "object" && n.key !== null && typeof n.value === "object" && n.value !== null;
|
|
@@ -1320,16 +1748,16 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1320
1748
|
const getLastIdentifierFromMemberChain = (node2) => {
|
|
1321
1749
|
if (!node2)
|
|
1322
1750
|
return null;
|
|
1323
|
-
if (
|
|
1751
|
+
if (isIdentifierNode2(node2))
|
|
1324
1752
|
return node2.name;
|
|
1325
1753
|
if (isMemberExpressionNode(node2)) {
|
|
1326
1754
|
const prop = node2.property;
|
|
1327
1755
|
if (node2.computed === false) {
|
|
1328
|
-
if (
|
|
1756
|
+
if (isIdentifierNode2(prop))
|
|
1329
1757
|
return prop.name;
|
|
1330
1758
|
}
|
|
1331
1759
|
if (node2.computed === true) {
|
|
1332
|
-
if (
|
|
1760
|
+
if (isStringLiteralNode2(prop))
|
|
1333
1761
|
return prop.value;
|
|
1334
1762
|
}
|
|
1335
1763
|
}
|
|
@@ -1339,7 +1767,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1339
1767
|
if (!node2) {
|
|
1340
1768
|
return null;
|
|
1341
1769
|
}
|
|
1342
|
-
if (
|
|
1770
|
+
if (isIdentifierNode2(node2)) {
|
|
1343
1771
|
return node2.name;
|
|
1344
1772
|
}
|
|
1345
1773
|
if (isMemberExpressionNode(node2)) {
|
|
@@ -1351,11 +1779,11 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1351
1779
|
if (!lhs) {
|
|
1352
1780
|
return null;
|
|
1353
1781
|
}
|
|
1354
|
-
if (
|
|
1782
|
+
if (isIdentifierNode2(lhs)) {
|
|
1355
1783
|
return lhs.name;
|
|
1356
1784
|
}
|
|
1357
1785
|
if (isMemberExpressionNode(lhs)) {
|
|
1358
|
-
if (lhs.computed === false &&
|
|
1786
|
+
if (lhs.computed === false && isIdentifierNode2(lhs.property) && lhs.property.name === "value") {
|
|
1359
1787
|
return getLastIdentifierFromMemberChain(lhs.object);
|
|
1360
1788
|
}
|
|
1361
1789
|
return getLastIdentifierFromMemberChain(lhs);
|
|
@@ -1363,7 +1791,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1363
1791
|
return null;
|
|
1364
1792
|
};
|
|
1365
1793
|
const isTemplateLiteralNode = (node2) => {
|
|
1366
|
-
if (!
|
|
1794
|
+
if (!isNodeType2(node2, "TemplateLiteral")) {
|
|
1367
1795
|
return false;
|
|
1368
1796
|
}
|
|
1369
1797
|
const n = node2;
|
|
@@ -1382,7 +1810,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1382
1810
|
if (isNullLiteralNode(arg)) {
|
|
1383
1811
|
return "Null";
|
|
1384
1812
|
}
|
|
1385
|
-
if (
|
|
1813
|
+
if (isStringLiteralNode2(arg)) {
|
|
1386
1814
|
const cleaned = (arg.value ?? "").trim();
|
|
1387
1815
|
if (!cleaned) {
|
|
1388
1816
|
return null;
|
|
@@ -1408,7 +1836,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1408
1836
|
return toPascalCase(stableName.slice(0, 24));
|
|
1409
1837
|
}
|
|
1410
1838
|
}
|
|
1411
|
-
if (
|
|
1839
|
+
if (isIdentifierNode2(arg)) {
|
|
1412
1840
|
const firstChar = arg.name.charAt(0);
|
|
1413
1841
|
const isUpperAlpha = firstChar !== "" && firstChar === firstChar.toUpperCase() && firstChar !== firstChar.toLowerCase();
|
|
1414
1842
|
if (isUpperAlpha) {
|
|
@@ -1420,7 +1848,7 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1420
1848
|
const getStableSuffixFromCall = (call) => {
|
|
1421
1849
|
const args = call.arguments ?? [];
|
|
1422
1850
|
const first = args.length > 0 ? args[0] : null;
|
|
1423
|
-
if (!
|
|
1851
|
+
if (!isObjectExpressionNode2(first)) {
|
|
1424
1852
|
const parts2 = [];
|
|
1425
1853
|
for (const arg of args.slice(0, 4)) {
|
|
1426
1854
|
const w = stableWordFromValue(arg ?? null);
|
|
@@ -1439,20 +1867,20 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1439
1867
|
}
|
|
1440
1868
|
const parts = [];
|
|
1441
1869
|
for (const prop of first.properties ?? []) {
|
|
1442
|
-
if (!
|
|
1870
|
+
if (!isObjectPropertyNode2(prop)) {
|
|
1443
1871
|
continue;
|
|
1444
1872
|
}
|
|
1445
1873
|
if (prop.computed) {
|
|
1446
1874
|
continue;
|
|
1447
1875
|
}
|
|
1448
|
-
const keyName =
|
|
1876
|
+
const keyName = isIdentifierNode2(prop.key) ? prop.key.name : isStringLiteralNode2(prop.key) ? prop.key.value : null;
|
|
1449
1877
|
if (!keyName) {
|
|
1450
1878
|
continue;
|
|
1451
1879
|
}
|
|
1452
1880
|
let valueWord = null;
|
|
1453
1881
|
if (isBooleanLiteralNode(prop.value)) {
|
|
1454
1882
|
valueWord = prop.value.value ? "True" : "False";
|
|
1455
|
-
} else if (
|
|
1883
|
+
} else if (isStringLiteralNode2(prop.value)) {
|
|
1456
1884
|
const cleaned = (prop.value.value ?? "").trim();
|
|
1457
1885
|
if (cleaned) {
|
|
1458
1886
|
valueWord = toPascalCase(cleaned.slice(0, 24));
|
|
@@ -1581,13 +2009,18 @@ function getDataTestIdValueFromValueAttribute(node, actualFileName, attributeKey
|
|
|
1581
2009
|
return staticAttributeValue(`${actualFileName}-${value}-${role}`);
|
|
1582
2010
|
}
|
|
1583
2011
|
const attrDynamic = findDirectiveByName(node, "bind", attributeKey);
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
2012
|
+
const attrDynamicAst = attrDynamic ? tryGetDirectiveBabelAst(attrDynamic, {
|
|
2013
|
+
preferredViews: ["loc", "compiled"],
|
|
2014
|
+
plugins: ["typescript"],
|
|
2015
|
+
preferExistingAst: false
|
|
2016
|
+
}) : null;
|
|
2017
|
+
if (attrDynamic?.exp && attrDynamicAst) {
|
|
2018
|
+
let value = getVueExpressionSource(attrDynamic.exp, "loc", "compiled");
|
|
2019
|
+
if (isMemberExpression(attrDynamicAst) || isOptionalMemberExpression(attrDynamicAst)) {
|
|
1587
2020
|
return staticAttributeValue(`${actualFileName}-${value.replaceAll(".", "")}-${role}`);
|
|
1588
2021
|
}
|
|
1589
|
-
if (
|
|
1590
|
-
value =
|
|
2022
|
+
if (isCallExpression(attrDynamicAst) || isOptionalCallExpression(attrDynamicAst)) {
|
|
2023
|
+
value = getVueExpressionSource(attrDynamic.exp, "compiled", "loc");
|
|
1591
2024
|
return templateAttributeValue(`${actualFileName}-\${${value}}-${role}`);
|
|
1592
2025
|
}
|
|
1593
2026
|
return staticAttributeValue(`${actualFileName}-${value}-${role}`);
|
|
@@ -1595,16 +2028,16 @@ function getDataTestIdValueFromValueAttribute(node, actualFileName, attributeKey
|
|
|
1595
2028
|
return null;
|
|
1596
2029
|
}
|
|
1597
2030
|
function generateToDirectiveDataTestId(componentName, node, toDirective, context, hierarchyMap2, nativeWrappers) {
|
|
1598
|
-
const
|
|
1599
|
-
if (
|
|
1600
|
-
return templateAttributeValue(`${componentName}-${
|
|
2031
|
+
const keyInfo = getKeyDirectiveInfo(node) || getContainedInVForDirectiveKeyInfo(context, node, hierarchyMap2);
|
|
2032
|
+
if (keyInfo) {
|
|
2033
|
+
return templateAttributeValue(`${componentName}-${keyInfo.selectorFragment}-${formatTagName(node, nativeWrappers)}`);
|
|
1601
2034
|
} else {
|
|
1602
2035
|
let name = toDirectiveObjectFieldNameValue(toDirective);
|
|
1603
2036
|
if (!name) {
|
|
1604
2037
|
if (toDirective.exp == null) {
|
|
1605
2038
|
return null;
|
|
1606
2039
|
}
|
|
1607
|
-
const source =
|
|
2040
|
+
const source = getVueExpressionSource(toDirective.exp, "compiled", "loc");
|
|
1608
2041
|
const toAst = toDirective.exp.ast;
|
|
1609
2042
|
const interpolated = toAst !== void 0 && toAst !== null && toAst !== false && isTemplateLiteral(toAst);
|
|
1610
2043
|
return templateAttributeValue(`${componentName}-\${${source}${interpolated ? ".replaceAll(' ', '')" : "?.name?.replaceAll(' ', '') ?? ''"}}${formatTagName(node, nativeWrappers)}`);
|
|
@@ -1628,44 +2061,46 @@ function toDirectiveObjectFieldNameValue(node) {
|
|
|
1628
2061
|
if (!node.exp || node.exp.type !== NodeTypes.COMPOUND_EXPRESSION && node.exp.type !== NodeTypes.SIMPLE_EXPRESSION) {
|
|
1629
2062
|
return null;
|
|
1630
2063
|
}
|
|
1631
|
-
const
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
const isStringLiteralNode = (n) => {
|
|
1638
|
-
return isNodeType(n, "StringLiteral") && typeof n.value === "string";
|
|
1639
|
-
};
|
|
1640
|
-
const isIdentifierNode = (n) => {
|
|
1641
|
-
return isNodeType(n, "Identifier") && typeof n.name === "string";
|
|
1642
|
-
};
|
|
1643
|
-
const isObjectPropertyNode = (n) => {
|
|
1644
|
-
if (!isNodeType(n, "ObjectProperty"))
|
|
1645
|
-
return false;
|
|
1646
|
-
const nn = n;
|
|
1647
|
-
return typeof nn.key === "object" && nn.key !== null && typeof nn.value === "object" && nn.value !== null;
|
|
1648
|
-
};
|
|
1649
|
-
const isObjectExpressionNode = (n) => {
|
|
1650
|
-
if (!isNodeType(n, "ObjectExpression"))
|
|
1651
|
-
return false;
|
|
1652
|
-
const nn = n;
|
|
1653
|
-
return Array.isArray(nn.properties);
|
|
1654
|
-
};
|
|
1655
|
-
if (!isObjectExpressionNode(expr))
|
|
1656
|
-
return null;
|
|
1657
|
-
const nameProp = expr.properties.find((p) => {
|
|
1658
|
-
if (!isObjectPropertyNode(p))
|
|
1659
|
-
return false;
|
|
1660
|
-
const key = p.key;
|
|
1661
|
-
return isIdentifierNode(key) && key.name === "name" || isStringLiteralNode(key) && key.value === "name";
|
|
1662
|
-
});
|
|
1663
|
-
if (!nameProp || !isObjectPropertyNode(nameProp) || !isStringLiteralNode(nameProp.value))
|
|
1664
|
-
return null;
|
|
1665
|
-
return toPascalCase(nameProp.value.value);
|
|
1666
|
-
} catch {
|
|
2064
|
+
const expr = tryGetDirectiveBabelAst(node, {
|
|
2065
|
+
preferredViews: ["loc", "compiled"],
|
|
2066
|
+
plugins: ["typescript"],
|
|
2067
|
+
preferExistingAst: false
|
|
2068
|
+
});
|
|
2069
|
+
if (!expr) {
|
|
1667
2070
|
return null;
|
|
1668
2071
|
}
|
|
2072
|
+
const isNodeType2 = (n, type) => {
|
|
2073
|
+
return n !== null && n.type === type;
|
|
2074
|
+
};
|
|
2075
|
+
const isStringLiteralNode2 = (n) => {
|
|
2076
|
+
return isNodeType2(n, "StringLiteral") && typeof n.value === "string";
|
|
2077
|
+
};
|
|
2078
|
+
const isIdentifierNode2 = (n) => {
|
|
2079
|
+
return isNodeType2(n, "Identifier") && typeof n.name === "string";
|
|
2080
|
+
};
|
|
2081
|
+
const isObjectPropertyNode2 = (n) => {
|
|
2082
|
+
if (!isNodeType2(n, "ObjectProperty"))
|
|
2083
|
+
return false;
|
|
2084
|
+
const nn = n;
|
|
2085
|
+
return typeof nn.key === "object" && nn.key !== null && typeof nn.value === "object" && nn.value !== null;
|
|
2086
|
+
};
|
|
2087
|
+
const isObjectExpressionNode2 = (n) => {
|
|
2088
|
+
if (!isNodeType2(n, "ObjectExpression"))
|
|
2089
|
+
return false;
|
|
2090
|
+
const nn = n;
|
|
2091
|
+
return Array.isArray(nn.properties);
|
|
2092
|
+
};
|
|
2093
|
+
if (!isObjectExpressionNode2(expr))
|
|
2094
|
+
return null;
|
|
2095
|
+
const nameProp = expr.properties.find((p) => {
|
|
2096
|
+
if (!isObjectPropertyNode2(p))
|
|
2097
|
+
return false;
|
|
2098
|
+
const key = p.key;
|
|
2099
|
+
return isIdentifierNode2(key) && key.name === "name" || isStringLiteralNode2(key) && key.value === "name";
|
|
2100
|
+
});
|
|
2101
|
+
if (!nameProp || !isObjectPropertyNode2(nameProp) || !isStringLiteralNode2(nameProp.value))
|
|
2102
|
+
return null;
|
|
2103
|
+
return toPascalCase(nameProp.value.value);
|
|
1669
2104
|
}
|
|
1670
2105
|
function getComposedClickHandlerContent(node, _context, innerText, clickDirective, _options = {}) {
|
|
1671
2106
|
const click = clickDirective ?? tryGetClickDirective(node);
|
|
@@ -1675,7 +2110,7 @@ function getComposedClickHandlerContent(node, _context, innerText, clickDirectiv
|
|
|
1675
2110
|
let handlerName = "";
|
|
1676
2111
|
if (click.exp) {
|
|
1677
2112
|
const exp = click.exp;
|
|
1678
|
-
const source = (exp
|
|
2113
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
1679
2114
|
if (source) {
|
|
1680
2115
|
const parsed = tryParseBabelAstFromHandlerSource(source);
|
|
1681
2116
|
if (parsed) {
|
|
@@ -1696,9 +2131,9 @@ function tryParseBabelAstFromHandlerSource(source) {
|
|
|
1696
2131
|
const trimmed = source.trim();
|
|
1697
2132
|
if (!trimmed)
|
|
1698
2133
|
return null;
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
2134
|
+
const expressionAst = tryParseBabelExpressionFromSource(trimmed, ["typescript", "jsx"]);
|
|
2135
|
+
if (expressionAst) {
|
|
2136
|
+
return expressionAst;
|
|
1702
2137
|
}
|
|
1703
2138
|
try {
|
|
1704
2139
|
return parse(trimmed, { sourceType: "module", plugins: ["typescript", "jsx"] });
|
|
@@ -2037,23 +2472,24 @@ function tryGetExistingElementDataTestId(node, attributeName = "data-testid") {
|
|
|
2037
2472
|
return null;
|
|
2038
2473
|
}
|
|
2039
2474
|
const simpleExp = exp;
|
|
2040
|
-
const ast = simpleExp
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
const
|
|
2047
|
-
const unwrappedTemplate = raw2.startsWith("`") && raw2.endsWith("`") && raw2.length >= 2 ? raw2.slice(1, -1) : cooked;
|
|
2475
|
+
const ast = tryGetVueExpressionAst(simpleExp, {
|
|
2476
|
+
preferredViews: ["content", "loc", "compiled"],
|
|
2477
|
+
plugins: ["typescript"]
|
|
2478
|
+
});
|
|
2479
|
+
const unwrappedTemplateLiteral = tryUnwrapTemplateLiteralExpressionSource(simpleExp);
|
|
2480
|
+
if (unwrappedTemplateLiteral) {
|
|
2481
|
+
const isStatic = unwrappedTemplateLiteral.expressionCount === 0;
|
|
2048
2482
|
if (isStatic) {
|
|
2049
|
-
return { value:
|
|
2483
|
+
return { value: unwrappedTemplateLiteral.template, isDynamic: false, isStaticLiteral: true };
|
|
2050
2484
|
}
|
|
2485
|
+
const templateValue = templateAttributeValue(unwrappedTemplateLiteral.template);
|
|
2051
2486
|
return {
|
|
2052
|
-
value:
|
|
2487
|
+
value: templateValue.template,
|
|
2053
2488
|
isDynamic: true,
|
|
2054
2489
|
isStaticLiteral: false,
|
|
2055
|
-
template:
|
|
2056
|
-
|
|
2490
|
+
template: templateValue.template,
|
|
2491
|
+
parsedTemplate: templateValue.parsedTemplate,
|
|
2492
|
+
templateExpressionCount: unwrappedTemplateLiteral.expressionCount
|
|
2057
2493
|
};
|
|
2058
2494
|
}
|
|
2059
2495
|
if (ast && typeof ast === "object" && "type" in ast && ast.type === "StringLiteral") {
|
|
@@ -2066,59 +2502,27 @@ function tryGetExistingElementDataTestId(node, attributeName = "data-testid") {
|
|
|
2066
2502
|
}
|
|
2067
2503
|
const preservableReference = tryGetPreservableDynamicReferenceExpression(ast);
|
|
2068
2504
|
if (preservableReference) {
|
|
2505
|
+
const templateValue = templateAttributeValue(`\${${preservableReference}}`);
|
|
2069
2506
|
return {
|
|
2070
2507
|
value: preservableReference,
|
|
2071
|
-
isDynamic: true,
|
|
2072
|
-
isStaticLiteral: false,
|
|
2073
|
-
template:
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
try {
|
|
2083
|
-
const ast2 = parseExpression(raw, { plugins: ["typescript"] });
|
|
2084
|
-
if (ast2 && typeof ast2 === "object" && "type" in ast2 && ast2.type === "TemplateLiteral") {
|
|
2085
|
-
const tl = ast2;
|
|
2086
|
-
const cooked = (tl.quasis ?? []).map((q) => q.value?.cooked ?? "").join("");
|
|
2087
|
-
const expressionCount = (tl.expressions ?? []).length;
|
|
2088
|
-
const isStatic = expressionCount === 0;
|
|
2089
|
-
const unwrappedTemplate = raw.startsWith("`") && raw.endsWith("`") && raw.length >= 2 ? raw.slice(1, -1) : cooked;
|
|
2090
|
-
if (isStatic) {
|
|
2091
|
-
return { value: unwrappedTemplate, isDynamic: false, isStaticLiteral: true };
|
|
2092
|
-
}
|
|
2093
|
-
return {
|
|
2094
|
-
value: unwrappedTemplate,
|
|
2095
|
-
isDynamic: true,
|
|
2096
|
-
isStaticLiteral: false,
|
|
2097
|
-
template: unwrappedTemplate,
|
|
2098
|
-
templateExpressionCount: expressionCount
|
|
2099
|
-
};
|
|
2100
|
-
}
|
|
2101
|
-
if (ast2 && typeof ast2 === "object" && "type" in ast2 && ast2.type === "StringLiteral") {
|
|
2102
|
-
const sl = ast2;
|
|
2103
|
-
return { value: sl.value ?? "", isDynamic: false, isStaticLiteral: true };
|
|
2104
|
-
}
|
|
2105
|
-
const preservableReference2 = tryGetPreservableDynamicReferenceExpression(ast2);
|
|
2106
|
-
if (preservableReference2) {
|
|
2107
|
-
return {
|
|
2108
|
-
value: preservableReference2,
|
|
2109
|
-
isDynamic: true,
|
|
2110
|
-
isStaticLiteral: false,
|
|
2111
|
-
template: `\${${preservableReference2}}`,
|
|
2112
|
-
templateExpressionCount: 1,
|
|
2113
|
-
rawExpression: preservableReference2
|
|
2114
|
-
};
|
|
2115
|
-
}
|
|
2116
|
-
} catch {
|
|
2508
|
+
isDynamic: true,
|
|
2509
|
+
isStaticLiteral: false,
|
|
2510
|
+
template: templateValue.template,
|
|
2511
|
+
parsedTemplate: templateValue.parsedTemplate,
|
|
2512
|
+
templateExpressionCount: 1,
|
|
2513
|
+
rawExpression: preservableReference
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
const raw = (simpleExp.content ?? "").trim();
|
|
2517
|
+
if (!raw) {
|
|
2518
|
+
return null;
|
|
2117
2519
|
}
|
|
2118
2520
|
return { value: raw, isDynamic: true, isStaticLiteral: false, rawExpression: raw };
|
|
2119
2521
|
}
|
|
2120
2522
|
function isTemplatePlaceholder(part) {
|
|
2121
|
-
|
|
2523
|
+
const parsedTemplate = tryParseTemplateFragment(part);
|
|
2524
|
+
const templateFragment = parsedTemplate ? getSingleExpressionTemplateFragment(parsedTemplate) : null;
|
|
2525
|
+
return !!templateFragment && templateFragment.prefix === "" && templateFragment.suffix === "";
|
|
2122
2526
|
}
|
|
2123
2527
|
function isAllCapsOrDigits(value) {
|
|
2124
2528
|
if (value.length <= 1) {
|
|
@@ -2171,33 +2575,14 @@ function safeMethodNameFromParts(parts) {
|
|
|
2171
2575
|
}
|
|
2172
2576
|
return name;
|
|
2173
2577
|
}
|
|
2174
|
-
function
|
|
2578
|
+
function toPomKeyPattern(templateValue) {
|
|
2579
|
+
const { templateLiteral } = templateValue.parsedTemplate;
|
|
2175
2580
|
let out = "";
|
|
2176
|
-
let i = 0;
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
out += template.slice(i);
|
|
2181
|
-
break;
|
|
2182
|
-
}
|
|
2183
|
-
out += template.slice(i, start);
|
|
2184
|
-
let depth = 1;
|
|
2185
|
-
let j = start + 2;
|
|
2186
|
-
while (j < template.length && depth > 0) {
|
|
2187
|
-
if (template[j] === "{") {
|
|
2188
|
-
depth++;
|
|
2189
|
-
} else if (template[j] === "}") {
|
|
2190
|
-
depth--;
|
|
2191
|
-
}
|
|
2192
|
-
j++;
|
|
2193
|
-
}
|
|
2194
|
-
const end = depth === 0 ? j - 1 : -1;
|
|
2195
|
-
if (end < 0) {
|
|
2196
|
-
out += template.slice(start);
|
|
2197
|
-
break;
|
|
2581
|
+
for (let i = 0; i < templateLiteral.quasis.length; i += 1) {
|
|
2582
|
+
out += templateLiteral.quasis[i]?.value.raw ?? "";
|
|
2583
|
+
if (templateLiteral.expressions[i]) {
|
|
2584
|
+
out += "${key}";
|
|
2198
2585
|
}
|
|
2199
|
-
out += "${key}";
|
|
2200
|
-
i = end + 1;
|
|
2201
2586
|
}
|
|
2202
2587
|
return out;
|
|
2203
2588
|
}
|
|
@@ -2205,8 +2590,8 @@ function applyResolvedDataTestId(args) {
|
|
|
2205
2590
|
const addHtmlAttribute = args.addHtmlAttribute ?? true;
|
|
2206
2591
|
const entryOverrides = args.entryOverrides ?? {};
|
|
2207
2592
|
const testIdAttribute = args.testIdAttribute ?? "data-testid";
|
|
2208
|
-
const existingIdBehavior = args.existingIdBehavior ?? "
|
|
2209
|
-
const nameCollisionBehavior = args.nameCollisionBehavior ?? "
|
|
2593
|
+
const existingIdBehavior = args.existingIdBehavior ?? "error";
|
|
2594
|
+
const nameCollisionBehavior = args.nameCollisionBehavior ?? "error";
|
|
2210
2595
|
const warn = args.warn;
|
|
2211
2596
|
const getBestKeyAccessCandidates = (expr) => {
|
|
2212
2597
|
if (!expr) {
|
|
@@ -2215,7 +2600,10 @@ function applyResolvedDataTestId(args) {
|
|
|
2215
2600
|
return splitNullishCoalescingExpression(expr);
|
|
2216
2601
|
};
|
|
2217
2602
|
let dataTestId = args.preferredGeneratedValue;
|
|
2603
|
+
let runtimeDataTestId = args.preferredRuntimeValue ?? args.preferredGeneratedValue;
|
|
2218
2604
|
let fromExisting = false;
|
|
2605
|
+
const bestKeyPreservePlaceholder = args.keyInfo?.runtimeFragment ?? null;
|
|
2606
|
+
const bestKeyVariable = args.keyInfo?.rawExpression ?? null;
|
|
2219
2607
|
const existing = tryGetExistingElementDataTestId(args.element, testIdAttribute);
|
|
2220
2608
|
if (existing) {
|
|
2221
2609
|
const loc = args.element.loc?.start;
|
|
@@ -2236,8 +2624,10 @@ Bulk cleanup: run ESLint with the @immense/vue-pom-generator/remove-existing-tes
|
|
|
2236
2624
|
if (existingIdBehavior === "preserve") {
|
|
2237
2625
|
if (existing.isDynamic) {
|
|
2238
2626
|
if (existing.template) {
|
|
2239
|
-
const
|
|
2240
|
-
|
|
2627
|
+
const existingTemplateValue = existing.parsedTemplate ? { kind: "template", template: existing.template, parsedTemplate: existing.parsedTemplate } : templateAttributeValue(existing.template);
|
|
2628
|
+
const existingTemplateFragment = getSingleExpressionTemplateFragment(existingTemplateValue.parsedTemplate);
|
|
2629
|
+
const requiredKeyTemplateValue = bestKeyPreservePlaceholder ? templateAttributeValue(bestKeyPreservePlaceholder) : null;
|
|
2630
|
+
if ((existing.templateExpressionCount ?? 0) !== 1 || !existingTemplateFragment) {
|
|
2241
2631
|
throw new Error(
|
|
2242
2632
|
`[vue-pom-generator] Existing ${attrLabel} is a template literal with multiple interpolations and cannot be preserved safely.
|
|
2243
2633
|
Component: ${args.componentName}
|
|
@@ -2247,20 +2637,21 @@ Existing ${attrLabel}: ${JSON.stringify(existing.value)}
|
|
|
2247
2637
|
Fix: reduce the template to a single key-based interpolation, or remove the explicit ${attrLabel} so it can be auto-generated.`
|
|
2248
2638
|
);
|
|
2249
2639
|
}
|
|
2250
|
-
const hasExact =
|
|
2251
|
-
const hasVarAccess = getBestKeyAccessCandidates(
|
|
2252
|
-
if (!hasExact && !hasVarAccess &&
|
|
2640
|
+
const hasExact = requiredKeyTemplateValue ? templateFragmentContainsSingleExpression(existingTemplateValue.parsedTemplate, requiredKeyTemplateValue.parsedTemplate) : false;
|
|
2641
|
+
const hasVarAccess = getBestKeyAccessCandidates(bestKeyVariable).some((candidate) => existingTemplateFragment.expressionSource === candidate);
|
|
2642
|
+
if (!hasExact && !hasVarAccess && bestKeyPreservePlaceholder) {
|
|
2253
2643
|
throw new Error(
|
|
2254
2644
|
`[vue-pom-generator] Existing ${attrLabel} appears to be missing the key placeholder needed to keep it unique.
|
|
2255
2645
|
Component: ${args.componentName}
|
|
2256
2646
|
File: ${file}:${locationHint}
|
|
2257
2647
|
Existing ${attrLabel}: ${JSON.stringify(existing.value)}
|
|
2258
|
-
Required placeholder: ${JSON.stringify(
|
|
2648
|
+
Required placeholder: ${JSON.stringify(bestKeyPreservePlaceholder)}${bestKeyVariable ? ` or an access on "${bestKeyVariable}"` : ""}
|
|
2259
2649
|
|
|
2260
|
-
Fix: either (1) include ${
|
|
2650
|
+
Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} template literal, or (2) remove the explicit ${attrLabel} so it can be auto-generated.`
|
|
2261
2651
|
);
|
|
2262
2652
|
}
|
|
2263
|
-
dataTestId =
|
|
2653
|
+
dataTestId = existingTemplateValue;
|
|
2654
|
+
runtimeDataTestId = existingTemplateValue;
|
|
2264
2655
|
fromExisting = true;
|
|
2265
2656
|
} else {
|
|
2266
2657
|
throw new Error(
|
|
@@ -2274,18 +2665,19 @@ If you really need a computed id, do not set existingIdBehavior="preserve".`
|
|
|
2274
2665
|
);
|
|
2275
2666
|
}
|
|
2276
2667
|
} else {
|
|
2277
|
-
if (
|
|
2668
|
+
if (bestKeyPreservePlaceholder && existing.isStaticLiteral) {
|
|
2278
2669
|
throw new Error(
|
|
2279
2670
|
`[vue-pom-generator] Existing ${attrLabel} appears to be missing the key placeholder needed to keep it unique.
|
|
2280
2671
|
Component: ${args.componentName}
|
|
2281
2672
|
File: ${file}:${locationHint}
|
|
2282
2673
|
Existing ${attrLabel}: ${JSON.stringify(existing.value)}
|
|
2283
|
-
Required placeholder: ${JSON.stringify(
|
|
2674
|
+
Required placeholder: ${JSON.stringify(bestKeyPreservePlaceholder)}${bestKeyVariable ? ` or an access on "${bestKeyVariable}"` : ""}
|
|
2284
2675
|
|
|
2285
|
-
Fix: either (1) include ${
|
|
2676
|
+
Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} template literal, or (2) remove the explicit ${attrLabel} so it can be auto-generated.`
|
|
2286
2677
|
);
|
|
2287
2678
|
}
|
|
2288
2679
|
dataTestId = staticAttributeValue(existing.value);
|
|
2680
|
+
runtimeDataTestId = staticAttributeValue(existing.value);
|
|
2289
2681
|
fromExisting = true;
|
|
2290
2682
|
}
|
|
2291
2683
|
}
|
|
@@ -2315,8 +2707,10 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2315
2707
|
};
|
|
2316
2708
|
const normalizedRole = normalizeNativeRole(args.nativeRole) ?? "button";
|
|
2317
2709
|
const targetPageObjectModelClass = entryOverrides.targetPageObjectModelClass;
|
|
2318
|
-
const formattedDataTestIdForPom = dataTestId.kind === "template" ?
|
|
2319
|
-
const
|
|
2710
|
+
const formattedDataTestIdForPom = dataTestId.kind === "template" ? toPomKeyPattern(dataTestId) : dataTestId.value;
|
|
2711
|
+
const selectorPatternKind = dataTestId.kind === "template" ? "parameterized" : "static";
|
|
2712
|
+
const selectorPattern = createPomStringPattern(formattedDataTestIdForPom, selectorPatternKind);
|
|
2713
|
+
const selectorIsParameterized = selectorPatternKind === "parameterized";
|
|
2320
2714
|
const deriveBaseMethodNameFromHint = (hint) => {
|
|
2321
2715
|
const hintRaw = (hint ?? "").trim();
|
|
2322
2716
|
const trimEdgeSeparators = (value) => {
|
|
@@ -2364,13 +2758,13 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2364
2758
|
const roleSuffix = upperFirst(normalizedRole || "Element");
|
|
2365
2759
|
const baseName = upperFirst(primaryMethodName);
|
|
2366
2760
|
const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
|
|
2367
|
-
return
|
|
2761
|
+
return selectorIsParameterized ? removeByKeySegment2(propertyName) : propertyName;
|
|
2368
2762
|
};
|
|
2369
2763
|
const getPrimaryGetterNameCandidates = (primaryMethodName) => {
|
|
2370
2764
|
const roleSuffix = upperFirst(normalizedRole || "Element");
|
|
2371
2765
|
const baseName = upperFirst(primaryMethodName);
|
|
2372
2766
|
const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
|
|
2373
|
-
if (!
|
|
2767
|
+
if (!selectorIsParameterized) {
|
|
2374
2768
|
return { primary: propertyName };
|
|
2375
2769
|
}
|
|
2376
2770
|
const stripped = removeByKeySegment2(propertyName);
|
|
@@ -2432,11 +2826,11 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2432
2826
|
return false;
|
|
2433
2827
|
}
|
|
2434
2828
|
const existingSelectors = [
|
|
2435
|
-
existingPom.
|
|
2436
|
-
...existingPom.
|
|
2829
|
+
existingPom.selector,
|
|
2830
|
+
...existingPom.alternateSelectors ?? []
|
|
2437
2831
|
];
|
|
2438
|
-
const sharesSelectorIdentity = existingSelectors.
|
|
2439
|
-
if (
|
|
2832
|
+
const sharesSelectorIdentity = existingSelectors.some((existingSelector) => pomStringPatternEquals(existingSelector, selectorPattern));
|
|
2833
|
+
if (selectorIsParameterized && !sharesSelectorIdentity) {
|
|
2440
2834
|
return false;
|
|
2441
2835
|
}
|
|
2442
2836
|
if (!mergeKey && !sharesSelectorIdentity) {
|
|
@@ -2451,12 +2845,15 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2451
2845
|
if ((existingEntry.targetPageObjectModelClass ?? null) !== (targetPageObjectModelClass ?? null)) {
|
|
2452
2846
|
return false;
|
|
2453
2847
|
}
|
|
2454
|
-
if (existingPom.
|
|
2455
|
-
existingPom.
|
|
2456
|
-
if (!existingPom.
|
|
2457
|
-
existingPom.
|
|
2848
|
+
if (!pomStringPatternEquals(existingPom.selector, selectorPattern)) {
|
|
2849
|
+
existingPom.alternateSelectors ??= [];
|
|
2850
|
+
if (!(existingPom.alternateSelectors ?? []).some((existingSelector) => pomStringPatternEquals(existingSelector, selectorPattern))) {
|
|
2851
|
+
existingPom.alternateSelectors.push(selectorPattern);
|
|
2458
2852
|
}
|
|
2459
2853
|
}
|
|
2854
|
+
if (selectorIsParameterized && !existingPom.parameters.some((param) => param.name === "key")) {
|
|
2855
|
+
existingPom.parameters = [createPomParameterSpec("key", keyTypeFromValues), ...existingPom.parameters];
|
|
2856
|
+
}
|
|
2460
2857
|
return true;
|
|
2461
2858
|
};
|
|
2462
2859
|
let methodName = "";
|
|
@@ -2469,7 +2866,7 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2469
2866
|
let suffix = 1;
|
|
2470
2867
|
while (true) {
|
|
2471
2868
|
const baseWithSuffix = suffix === 1 ? base : `${base}${suffix}`;
|
|
2472
|
-
const candidate =
|
|
2869
|
+
const candidate = selectorIsParameterized ? `${baseWithSuffix}ByKey` : baseWithSuffix;
|
|
2473
2870
|
const actionName = getPrimaryActionMethodName(candidate);
|
|
2474
2871
|
const getterCandidates = getPrimaryGetterNameCandidates(candidate);
|
|
2475
2872
|
let chosenGetterName = getterCandidates.primary;
|
|
@@ -2495,7 +2892,7 @@ Fix: either (1) include ${args.bestKeyPlaceholder} in your :${attrLabel} templat
|
|
|
2495
2892
|
const baseNameUpper = upperFirst(baseWithSuffix);
|
|
2496
2893
|
if (!hasRoleSuffix(baseNameUpper, roleSuffix)) {
|
|
2497
2894
|
const baseWithRoleSuffix = `${baseWithSuffix}${roleSuffix}`;
|
|
2498
|
-
const candidateWithRoleSuffix =
|
|
2895
|
+
const candidateWithRoleSuffix = selectorIsParameterized ? `${baseWithRoleSuffix}ByKey` : baseWithRoleSuffix;
|
|
2499
2896
|
const actionNameWithRoleSuffix = getPrimaryActionMethodName(candidateWithRoleSuffix);
|
|
2500
2897
|
const getterCandidatesWithRoleSuffix = getPrimaryGetterNameCandidates(candidateWithRoleSuffix);
|
|
2501
2898
|
let chosenGetterNameWithRoleSuffix = getterCandidatesWithRoleSuffix.primary;
|
|
@@ -2563,51 +2960,46 @@ Conflicts: getter=${last.getterName}, method=${last.actionName}
|
|
|
2563
2960
|
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".`
|
|
2564
2961
|
);
|
|
2565
2962
|
}
|
|
2566
|
-
|
|
2567
|
-
if (isKeyed) {
|
|
2568
|
-
params.key = keyTypeFromValues;
|
|
2569
|
-
}
|
|
2963
|
+
let parameters = selectorIsParameterized ? [createPomParameterSpec("key", keyTypeFromValues)] : [];
|
|
2570
2964
|
switch (normalizedRole) {
|
|
2571
2965
|
case "input":
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
if (!isKeyed) delete params.key;
|
|
2966
|
+
parameters = setPomParameter(parameters, "text", "string");
|
|
2967
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2575
2968
|
break;
|
|
2576
2969
|
case "select":
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
if (!isKeyed) delete params.key;
|
|
2970
|
+
parameters = setPomParameter(parameters, "value", "string");
|
|
2971
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2580
2972
|
break;
|
|
2581
2973
|
case "vselect":
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
if (!isKeyed) delete params.key;
|
|
2974
|
+
parameters = setPomParameter(parameters, "value", "string");
|
|
2975
|
+
parameters = setPomParameter(parameters, "timeOut", "number = 500");
|
|
2976
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2586
2977
|
break;
|
|
2587
2978
|
case "radio":
|
|
2588
|
-
|
|
2979
|
+
parameters = setPomParameter(parameters, "annotationText", 'string = ""');
|
|
2589
2980
|
break;
|
|
2590
2981
|
}
|
|
2591
|
-
|
|
2592
|
-
params.key = keyTypeFromValues;
|
|
2593
|
-
}
|
|
2982
|
+
const normalizedParameters = selectorIsParameterized ? setPomParameter(parameters, "key", keyTypeFromValues) : removePomParameter(parameters, "key");
|
|
2594
2983
|
if (addHtmlAttribute && !fromExisting) {
|
|
2595
2984
|
upsertAttribute(args.element, testIdAttribute, dataTestId);
|
|
2596
2985
|
}
|
|
2597
2986
|
const childComponentName = args.element.tag;
|
|
2598
2987
|
const dataTestIdEntry = {
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2988
|
+
selectorValue: entryOverrides.selectorValue ?? createPomStringPattern(
|
|
2989
|
+
getAttributeValueText(dataTestId),
|
|
2990
|
+
dataTestId.kind === "template" ? "parameterized" : "static"
|
|
2991
|
+
),
|
|
2992
|
+
templateLiteral: entryOverrides.templateLiteral,
|
|
2993
|
+
targetPageObjectModelClass: entryOverrides.targetPageObjectModelClass
|
|
2602
2994
|
};
|
|
2603
2995
|
dataTestIdEntry.pom = {
|
|
2604
2996
|
nativeRole: normalizedRole,
|
|
2605
2997
|
methodName,
|
|
2606
2998
|
getterNameOverride,
|
|
2607
|
-
|
|
2608
|
-
|
|
2999
|
+
selector: selectorPattern,
|
|
3000
|
+
alternateSelectors: void 0,
|
|
2609
3001
|
mergeKey: args.pomMergeKey,
|
|
2610
|
-
|
|
3002
|
+
parameters: normalizedParameters,
|
|
2611
3003
|
keyValuesOverride: args.keyValuesOverride ?? null
|
|
2612
3004
|
// emitPrimary defaults to true; special cases (including merge) may set it to false below.
|
|
2613
3005
|
};
|
|
@@ -2638,43 +3030,18 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2638
3030
|
}
|
|
2639
3031
|
};
|
|
2640
3032
|
const getSignatureForGeneratedMethod = () => {
|
|
2641
|
-
|
|
2642
|
-
const isNavigation2 = !!dataTestIdEntry.targetPageObjectModelClass;
|
|
2643
|
-
const needsKey2 = Object.prototype.hasOwnProperty.call(params, "key");
|
|
2644
|
-
const keyType = keyTypeFromValues;
|
|
2645
|
-
if (isNavigation2) {
|
|
2646
|
-
if (needsKey2) {
|
|
2647
|
-
return { params: `key: ${keyType}`, argNames: ["key"] };
|
|
2648
|
-
}
|
|
2649
|
-
return { params: "", argNames: [] };
|
|
2650
|
-
}
|
|
2651
|
-
switch (role) {
|
|
2652
|
-
case "input":
|
|
2653
|
-
return needsKey2 ? { params: `key: ${keyType}, text: string, annotationText: string = ""`, argNames: ["key", "text", "annotationText"] } : { params: 'text: string, annotationText: string = ""', argNames: ["text", "annotationText"] };
|
|
2654
|
-
case "select":
|
|
2655
|
-
return needsKey2 ? { params: `key: ${keyType}, value: string, annotationText: string = ""`, argNames: ["key", "value", "annotationText"] } : { params: 'value: string, annotationText: string = ""', argNames: ["value", "annotationText"] };
|
|
2656
|
-
case "vselect":
|
|
2657
|
-
return needsKey2 ? { params: `key: ${keyType}, value: string, timeOut = 500`, argNames: ["key", "value", "timeOut"] } : { params: "value: string, timeOut = 500", argNames: ["value", "timeOut"] };
|
|
2658
|
-
case "radio":
|
|
2659
|
-
return needsKey2 ? { params: `key: ${keyType}, annotationText: string = ""`, argNames: ["key", "annotationText"] } : { params: 'annotationText: string = ""', argNames: ["annotationText"] };
|
|
2660
|
-
default:
|
|
2661
|
-
if (needsKey2) {
|
|
2662
|
-
return { params: `key: ${keyType}`, argNames: ["key"] };
|
|
2663
|
-
}
|
|
2664
|
-
return { params: "", argNames: [] };
|
|
2665
|
-
}
|
|
3033
|
+
return createPomMethodSignature(normalizedParameters);
|
|
2666
3034
|
};
|
|
2667
3035
|
const registerPrimaryOnce = (pom) => {
|
|
2668
|
-
const
|
|
2669
|
-
const alternates = (pom.alternateFormattedDataTestIds ?? []).slice().sort();
|
|
3036
|
+
const alternates = (pom.alternateSelectors ?? []).slice().sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
2670
3037
|
const key = JSON.stringify({
|
|
2671
3038
|
kind: "primary",
|
|
2672
3039
|
role: pom.nativeRole,
|
|
2673
3040
|
methodName: pom.methodName,
|
|
2674
3041
|
getterNameOverride: pom.getterNameOverride ?? null,
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
3042
|
+
selector: pom.selector,
|
|
3043
|
+
alternateSelectors: alternates.length ? alternates : void 0,
|
|
3044
|
+
parameters: pom.parameters,
|
|
2678
3045
|
target: dataTestIdEntry.targetPageObjectModelClass ?? null,
|
|
2679
3046
|
emitPrimary: pom.emitPrimary ?? true
|
|
2680
3047
|
});
|
|
@@ -2688,8 +3055,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2688
3055
|
}
|
|
2689
3056
|
};
|
|
2690
3057
|
const addExtraClickMethod = (spec) => {
|
|
2691
|
-
const
|
|
2692
|
-
const key = JSON.stringify({ kind: spec.kind, selector: spec.selector, keyLiteral: spec.keyLiteral ?? null, params: stableParams });
|
|
3058
|
+
const key = JSON.stringify({ kind: spec.kind, selector: spec.selector, keyLiteral: spec.keyLiteral ?? null, parameters: spec.parameters });
|
|
2693
3059
|
const seen = args.generatedMethodContentByComponent.get(args.parentComponentName) ?? /* @__PURE__ */ new Set();
|
|
2694
3060
|
if (!args.generatedMethodContentByComponent.has(args.parentComponentName)) {
|
|
2695
3061
|
args.generatedMethodContentByComponent.set(args.parentComponentName, seen);
|
|
@@ -2712,7 +3078,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2712
3078
|
if (prev === null) {
|
|
2713
3079
|
return;
|
|
2714
3080
|
}
|
|
2715
|
-
if (signature === null || prev
|
|
3081
|
+
if (signature === null || !pomMethodSignatureEquals(prev, signature)) {
|
|
2716
3082
|
args.dependencies.generatedMethods.set(name, null);
|
|
2717
3083
|
}
|
|
2718
3084
|
};
|
|
@@ -2728,23 +3094,10 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2728
3094
|
return candidate;
|
|
2729
3095
|
};
|
|
2730
3096
|
const tryGetDirectiveExpressionAst = (dir) => {
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
}
|
|
2735
|
-
if (exp.type === NodeTypes.SIMPLE_EXPRESSION) {
|
|
2736
|
-
const simple = exp;
|
|
2737
|
-
const ast = simple.ast;
|
|
2738
|
-
if (ast && "type" in ast) {
|
|
2739
|
-
return ast;
|
|
2740
|
-
}
|
|
2741
|
-
}
|
|
2742
|
-
try {
|
|
2743
|
-
const raw = args.context ? stringifyExpression(exp) : exp.loc.source;
|
|
2744
|
-
return parseExpression(raw, { plugins: ["typescript"] });
|
|
2745
|
-
} catch {
|
|
2746
|
-
return null;
|
|
2747
|
-
}
|
|
3097
|
+
return tryGetDirectiveBabelAst(dir, {
|
|
3098
|
+
preferredViews: args.context ? ["compiled", "loc"] : ["loc", "compiled"],
|
|
3099
|
+
plugins: ["typescript"]
|
|
3100
|
+
});
|
|
2748
3101
|
};
|
|
2749
3102
|
const tryGetStaticStringFromBabel = (node) => {
|
|
2750
3103
|
if (!node) {
|
|
@@ -2841,17 +3194,17 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2841
3194
|
name: generatedName2,
|
|
2842
3195
|
selector: {
|
|
2843
3196
|
kind: "withinTestIdByLabel",
|
|
2844
|
-
|
|
2845
|
-
|
|
3197
|
+
rootTestId: createPomStringPattern(wrapperTestId, selectorPatternKind),
|
|
3198
|
+
label: createPomStringPattern(label, "static"),
|
|
2846
3199
|
exact: true
|
|
2847
3200
|
},
|
|
2848
|
-
|
|
3201
|
+
parameters: [createPomParameterSpec("annotationText", `string = ""`)]
|
|
2849
3202
|
});
|
|
2850
3203
|
if (added2) {
|
|
2851
|
-
registerGeneratedMethodSignature(generatedName2,
|
|
3204
|
+
registerGeneratedMethodSignature(generatedName2, createPomMethodSignature([createPomParameterSpec("annotationText", `string = ""`)]));
|
|
2852
3205
|
}
|
|
2853
3206
|
}
|
|
2854
|
-
return;
|
|
3207
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2855
3208
|
}
|
|
2856
3209
|
const generatedName = ensureUniqueGeneratedName(`select${upperFirst(methodName || "Radio")}`);
|
|
2857
3210
|
if (dataTestIdEntry.pom) {
|
|
@@ -2863,19 +3216,25 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2863
3216
|
name: generatedName,
|
|
2864
3217
|
selector: {
|
|
2865
3218
|
kind: "withinTestIdByLabel",
|
|
2866
|
-
|
|
2867
|
-
|
|
3219
|
+
rootTestId: createPomStringPattern(wrapperTestId, selectorPatternKind),
|
|
3220
|
+
label: createPomStringPattern("${value}", "parameterized"),
|
|
2868
3221
|
exact: true
|
|
2869
3222
|
},
|
|
2870
|
-
|
|
3223
|
+
parameters: [
|
|
3224
|
+
createPomParameterSpec("value", "string"),
|
|
3225
|
+
createPomParameterSpec("annotationText", `string = ""`)
|
|
3226
|
+
]
|
|
2871
3227
|
});
|
|
2872
3228
|
if (added) {
|
|
2873
|
-
registerGeneratedMethodSignature(generatedName,
|
|
3229
|
+
registerGeneratedMethodSignature(generatedName, createPomMethodSignature([
|
|
3230
|
+
createPomParameterSpec("value", "string"),
|
|
3231
|
+
createPomParameterSpec("annotationText", `string = ""`)
|
|
3232
|
+
]));
|
|
2874
3233
|
}
|
|
2875
|
-
return;
|
|
3234
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2876
3235
|
}
|
|
2877
3236
|
const staticKeyValues = args.keyValuesOverride ?? null;
|
|
2878
|
-
const needsKey =
|
|
3237
|
+
const needsKey = hasPomParameter(normalizedParameters, "key") && selectorIsParameterized;
|
|
2879
3238
|
const isNavigation = !!dataTestIdEntry.targetPageObjectModelClass;
|
|
2880
3239
|
if (staticKeyValues && staticKeyValues.length > 0 && needsKey && !isNavigation && normalizedRole !== "input" && normalizedRole !== "select" && normalizedRole !== "vselect" && normalizedRole !== "radio") {
|
|
2881
3240
|
if (dataTestIdEntry.pom) {
|
|
@@ -2894,19 +3253,19 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2894
3253
|
name: generatedName,
|
|
2895
3254
|
selector: {
|
|
2896
3255
|
kind: "testId",
|
|
2897
|
-
|
|
3256
|
+
testId: selectorPattern
|
|
2898
3257
|
},
|
|
2899
3258
|
keyLiteral: rawValue,
|
|
2900
|
-
|
|
3259
|
+
parameters: [createPomParameterSpec("wait", "boolean = true"), createPomParameterSpec("annotationText", 'string = ""')]
|
|
2901
3260
|
});
|
|
2902
3261
|
if (added) {
|
|
2903
|
-
registerGeneratedMethodSignature(generatedName,
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
3262
|
+
registerGeneratedMethodSignature(generatedName, createPomMethodSignature([
|
|
3263
|
+
createPomParameterSpec("wait", "boolean = true"),
|
|
3264
|
+
createPomParameterSpec("annotationText", 'string = ""')
|
|
3265
|
+
]));
|
|
2907
3266
|
}
|
|
2908
3267
|
}
|
|
2909
|
-
return;
|
|
3268
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2910
3269
|
}
|
|
2911
3270
|
if (dataTestIdEntry.pom) {
|
|
2912
3271
|
if (dataTestIdEntry.pom.emitPrimary !== false) {
|
|
@@ -2920,6 +3279,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
|
|
|
2920
3279
|
const generatedName = getGeneratedMethodName();
|
|
2921
3280
|
registerGeneratedMethodSignature(generatedName, signature);
|
|
2922
3281
|
}
|
|
3282
|
+
return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
|
|
2923
3283
|
}
|
|
2924
3284
|
function safeRealpath(value) {
|
|
2925
3285
|
try {
|
|
@@ -3907,113 +4267,6 @@ class VuePomGeneratorError extends Error {
|
|
|
3907
4267
|
this.name = "VuePomGeneratorError";
|
|
3908
4268
|
}
|
|
3909
4269
|
}
|
|
3910
|
-
function splitParameterList(parameters) {
|
|
3911
|
-
const parts = [];
|
|
3912
|
-
let current = "";
|
|
3913
|
-
let braceDepth = 0;
|
|
3914
|
-
let bracketDepth = 0;
|
|
3915
|
-
let parenDepth = 0;
|
|
3916
|
-
let angleDepth = 0;
|
|
3917
|
-
let inSingleQuote = false;
|
|
3918
|
-
let inDoubleQuote = false;
|
|
3919
|
-
let inTemplateString = false;
|
|
3920
|
-
for (let index = 0; index < parameters.length; index += 1) {
|
|
3921
|
-
const char = parameters[index];
|
|
3922
|
-
const previous = index > 0 ? parameters[index - 1] : "";
|
|
3923
|
-
if (char === "'" && !inDoubleQuote && !inTemplateString && previous !== "\\") {
|
|
3924
|
-
inSingleQuote = !inSingleQuote;
|
|
3925
|
-
current += char;
|
|
3926
|
-
continue;
|
|
3927
|
-
}
|
|
3928
|
-
if (char === '"' && !inSingleQuote && !inTemplateString && previous !== "\\") {
|
|
3929
|
-
inDoubleQuote = !inDoubleQuote;
|
|
3930
|
-
current += char;
|
|
3931
|
-
continue;
|
|
3932
|
-
}
|
|
3933
|
-
if (char === "`" && !inSingleQuote && !inDoubleQuote && previous !== "\\") {
|
|
3934
|
-
inTemplateString = !inTemplateString;
|
|
3935
|
-
current += char;
|
|
3936
|
-
continue;
|
|
3937
|
-
}
|
|
3938
|
-
if (inSingleQuote || inDoubleQuote || inTemplateString) {
|
|
3939
|
-
current += char;
|
|
3940
|
-
continue;
|
|
3941
|
-
}
|
|
3942
|
-
switch (char) {
|
|
3943
|
-
case "{":
|
|
3944
|
-
braceDepth += 1;
|
|
3945
|
-
break;
|
|
3946
|
-
case "}":
|
|
3947
|
-
braceDepth -= 1;
|
|
3948
|
-
break;
|
|
3949
|
-
case "[":
|
|
3950
|
-
bracketDepth += 1;
|
|
3951
|
-
break;
|
|
3952
|
-
case "]":
|
|
3953
|
-
bracketDepth -= 1;
|
|
3954
|
-
break;
|
|
3955
|
-
case "(":
|
|
3956
|
-
parenDepth += 1;
|
|
3957
|
-
break;
|
|
3958
|
-
case ")":
|
|
3959
|
-
parenDepth -= 1;
|
|
3960
|
-
break;
|
|
3961
|
-
case "<":
|
|
3962
|
-
angleDepth += 1;
|
|
3963
|
-
break;
|
|
3964
|
-
case ">":
|
|
3965
|
-
angleDepth -= 1;
|
|
3966
|
-
break;
|
|
3967
|
-
case ",":
|
|
3968
|
-
if (braceDepth === 0 && bracketDepth === 0 && parenDepth === 0 && angleDepth === 0) {
|
|
3969
|
-
const trimmed2 = current.trim();
|
|
3970
|
-
if (trimmed2) {
|
|
3971
|
-
parts.push(trimmed2);
|
|
3972
|
-
}
|
|
3973
|
-
current = "";
|
|
3974
|
-
continue;
|
|
3975
|
-
}
|
|
3976
|
-
break;
|
|
3977
|
-
}
|
|
3978
|
-
current += char;
|
|
3979
|
-
}
|
|
3980
|
-
const trimmed = current.trim();
|
|
3981
|
-
if (trimmed) {
|
|
3982
|
-
parts.push(trimmed);
|
|
3983
|
-
}
|
|
3984
|
-
return parts;
|
|
3985
|
-
}
|
|
3986
|
-
function parseParameterSignature(parameter) {
|
|
3987
|
-
const colonIndex = parameter.indexOf(":");
|
|
3988
|
-
if (colonIndex < 0) {
|
|
3989
|
-
return { name: parameter.trim() };
|
|
3990
|
-
}
|
|
3991
|
-
const rawName = parameter.slice(0, colonIndex).trim();
|
|
3992
|
-
const hasQuestionToken = rawName.endsWith("?");
|
|
3993
|
-
const name = hasQuestionToken ? rawName.slice(0, -1).trim() : rawName;
|
|
3994
|
-
const remainder = parameter.slice(colonIndex + 1).trim();
|
|
3995
|
-
const initializerIndex = remainder.lastIndexOf("=");
|
|
3996
|
-
if (initializerIndex < 0) {
|
|
3997
|
-
return {
|
|
3998
|
-
name,
|
|
3999
|
-
hasQuestionToken,
|
|
4000
|
-
type: remainder || void 0
|
|
4001
|
-
};
|
|
4002
|
-
}
|
|
4003
|
-
return {
|
|
4004
|
-
name,
|
|
4005
|
-
hasQuestionToken,
|
|
4006
|
-
type: remainder.slice(0, initializerIndex).trim() || void 0,
|
|
4007
|
-
initializer: remainder.slice(initializerIndex + 1).trim() || void 0
|
|
4008
|
-
};
|
|
4009
|
-
}
|
|
4010
|
-
function parseParameterSignatures(parameters) {
|
|
4011
|
-
const trimmed = parameters.trim();
|
|
4012
|
-
if (!trimmed) {
|
|
4013
|
-
return [];
|
|
4014
|
-
}
|
|
4015
|
-
return splitParameterList(trimmed).map(parseParameterSignature);
|
|
4016
|
-
}
|
|
4017
4270
|
function toPosixRelativePath(fromDir, toFile) {
|
|
4018
4271
|
let rel = path.relative(fromDir, toFile).replace(/\\/g, "/");
|
|
4019
4272
|
if (!rel.startsWith(".")) {
|
|
@@ -4033,6 +4286,21 @@ function resolveRouterEntry(projectRoot, routerEntry) {
|
|
|
4033
4286
|
const root = projectRoot ?? process.cwd();
|
|
4034
4287
|
return path.isAbsolute(routerEntry) ? routerEntry : path.resolve(root, routerEntry);
|
|
4035
4288
|
}
|
|
4289
|
+
function createMissingCustomPomDirectoryError(configuredDir, resolvedDir) {
|
|
4290
|
+
return new VuePomGeneratorError(
|
|
4291
|
+
`Custom POM directory "${configuredDir}" does not exist.
|
|
4292
|
+
Resolved path: ${resolvedDir}
|
|
4293
|
+
Create the directory, point generation.playwright.customPoms.dir at the correct location, or remove the customPoms configuration.`
|
|
4294
|
+
);
|
|
4295
|
+
}
|
|
4296
|
+
function createMissingCustomPomAttachmentClassError(missingClassNames, configuredDir) {
|
|
4297
|
+
const renderedClassNames = missingClassNames.map((name) => `"${name}"`).join(", ");
|
|
4298
|
+
return new VuePomGeneratorError(
|
|
4299
|
+
`Custom POM attachments reference missing helper classes: ${renderedClassNames}.
|
|
4300
|
+
Expected matching helper files/exports under "${configuredDir}".
|
|
4301
|
+
Add the missing helper classes or remove the corresponding generation.playwright.customPoms.attachments entries.`
|
|
4302
|
+
);
|
|
4303
|
+
}
|
|
4036
4304
|
function createCustomPomImportCollisionError(exportName, requested) {
|
|
4037
4305
|
return new VuePomGeneratorError(
|
|
4038
4306
|
`Custom POM import name collision detected for "${exportName}".
|
|
@@ -4171,35 +4439,28 @@ function generateGoToSelfMethod(componentName) {
|
|
|
4171
4439
|
})
|
|
4172
4440
|
];
|
|
4173
4441
|
}
|
|
4174
|
-
function
|
|
4175
|
-
|
|
4176
|
-
return "";
|
|
4177
|
-
const preferredOrder = ["key", "value", "text", "timeOut", "annotationText", "wait"];
|
|
4178
|
-
const entries = Object.entries(params);
|
|
4179
|
-
if (!entries.length)
|
|
4180
|
-
return "";
|
|
4181
|
-
const score = (name) => {
|
|
4182
|
-
const idx = preferredOrder.indexOf(name);
|
|
4183
|
-
return idx < 0 ? 999 : idx;
|
|
4184
|
-
};
|
|
4185
|
-
return entries.slice().sort((a, b) => score(a[0]) - score(b[0]) || a[0].localeCompare(b[0])).map(([name, typeExpr]) => `${name}: ${typeExpr}`).join(", ");
|
|
4442
|
+
function getSelectorPatterns(selector) {
|
|
4443
|
+
return selector.kind === "testId" ? [selector.testId] : [selector.rootTestId, selector.label];
|
|
4186
4444
|
}
|
|
4187
4445
|
function generateExtraClickMethodMembers(spec) {
|
|
4188
4446
|
if (spec.kind !== "click") {
|
|
4189
4447
|
return [];
|
|
4190
4448
|
}
|
|
4191
|
-
const
|
|
4192
|
-
const
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4449
|
+
const selectorPatterns = getSelectorPatterns(spec.selector);
|
|
4450
|
+
const signatureSpecs = orderPomPatternParameters(
|
|
4451
|
+
spec.parameters,
|
|
4452
|
+
selectorPatterns,
|
|
4453
|
+
{ omit: spec.keyLiteral !== void 0 ? ["key"] : [] }
|
|
4454
|
+
);
|
|
4455
|
+
const parameters = toTypeScriptPomParameterStructures(signatureSpecs);
|
|
4456
|
+
const hasAnnotationText = signatureSpecs.some((param) => param.name === "annotationText");
|
|
4457
|
+
const hasWait = signatureSpecs.some((param) => param.name === "wait");
|
|
4196
4458
|
const annotationArg = hasAnnotationText ? "annotationText" : '""';
|
|
4197
4459
|
const waitArg = hasWait ? "wait" : "true";
|
|
4198
4460
|
if (spec.selector.kind === "testId") {
|
|
4199
|
-
const
|
|
4200
|
-
const testIdExpr = needsTemplate ? `\`${spec.selector.formattedDataTestId}\`` : JSON.stringify(spec.selector.formattedDataTestId);
|
|
4461
|
+
const testIdBinding = bindTypeScriptPomPattern(spec.selector.testId, "testId");
|
|
4201
4462
|
const clickArgs = [];
|
|
4202
|
-
clickArgs.push(
|
|
4463
|
+
clickArgs.push(testIdBinding.expression);
|
|
4203
4464
|
if (hasAnnotationText || hasWait) {
|
|
4204
4465
|
clickArgs.push(annotationArg);
|
|
4205
4466
|
}
|
|
@@ -4215,20 +4476,16 @@ function generateExtraClickMethodMembers(spec) {
|
|
|
4215
4476
|
if (spec.keyLiteral !== void 0) {
|
|
4216
4477
|
writer.writeLine(`const key = ${JSON.stringify(spec.keyLiteral)};`);
|
|
4217
4478
|
}
|
|
4218
|
-
|
|
4219
|
-
writer.writeLine(
|
|
4479
|
+
for (const statement of testIdBinding.setupStatements) {
|
|
4480
|
+
writer.writeLine(statement);
|
|
4220
4481
|
}
|
|
4221
4482
|
writer.writeLine(`await this.clickByTestId(${clickArgs.join(", ")});`);
|
|
4222
4483
|
}
|
|
4223
4484
|
})
|
|
4224
4485
|
];
|
|
4225
4486
|
}
|
|
4226
|
-
const
|
|
4227
|
-
const
|
|
4228
|
-
const rootExpr = rootNeedsTemplate ? `\`${spec.selector.rootFormattedDataTestId}\`` : JSON.stringify(spec.selector.rootFormattedDataTestId);
|
|
4229
|
-
const labelExpr = labelNeedsTemplate ? `\`${spec.selector.formattedLabel}\`` : JSON.stringify(spec.selector.formattedLabel);
|
|
4230
|
-
const rootArg = rootNeedsTemplate ? "rootTestId" : rootExpr;
|
|
4231
|
-
const labelArg = labelNeedsTemplate ? "label" : labelExpr;
|
|
4487
|
+
const rootBinding = bindTypeScriptPomPattern(spec.selector.rootTestId, "rootTestId");
|
|
4488
|
+
const labelBinding = bindTypeScriptPomPattern(spec.selector.label, "label");
|
|
4232
4489
|
return [
|
|
4233
4490
|
createClassMethod({
|
|
4234
4491
|
name: spec.name,
|
|
@@ -4238,13 +4495,13 @@ function generateExtraClickMethodMembers(spec) {
|
|
|
4238
4495
|
if (spec.keyLiteral !== void 0) {
|
|
4239
4496
|
writer.writeLine(`const key = ${JSON.stringify(spec.keyLiteral)};`);
|
|
4240
4497
|
}
|
|
4241
|
-
|
|
4242
|
-
writer.writeLine(
|
|
4498
|
+
for (const statement of rootBinding.setupStatements) {
|
|
4499
|
+
writer.writeLine(statement);
|
|
4243
4500
|
}
|
|
4244
|
-
|
|
4245
|
-
writer.writeLine(
|
|
4501
|
+
for (const statement of labelBinding.setupStatements) {
|
|
4502
|
+
writer.writeLine(statement);
|
|
4246
4503
|
}
|
|
4247
|
-
writer.writeLine(`await this.clickWithinTestIdByLabel(${
|
|
4504
|
+
writer.writeLine(`await this.clickWithinTestIdByLabel(${rootBinding.expression}, ${labelBinding.expression}, ${annotationArg}, ${waitArg});`);
|
|
4248
4505
|
}
|
|
4249
4506
|
})
|
|
4250
4507
|
];
|
|
@@ -4257,10 +4514,10 @@ function generateMethodMembersFromPom(primary, targetPageObjectModelClass) {
|
|
|
4257
4514
|
targetPageObjectModelClass,
|
|
4258
4515
|
primary.methodName,
|
|
4259
4516
|
primary.nativeRole,
|
|
4260
|
-
primary.
|
|
4261
|
-
primary.
|
|
4517
|
+
primary.selector,
|
|
4518
|
+
primary.alternateSelectors,
|
|
4262
4519
|
primary.getterNameOverride,
|
|
4263
|
-
primary.
|
|
4520
|
+
primary.parameters
|
|
4264
4521
|
);
|
|
4265
4522
|
}
|
|
4266
4523
|
function generateMethodsContentForDependencies(dependencies) {
|
|
@@ -4268,15 +4525,14 @@ function generateMethodsContentForDependencies(dependencies) {
|
|
|
4268
4525
|
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));
|
|
4269
4526
|
const seenPrimaryKeys = /* @__PURE__ */ new Set();
|
|
4270
4527
|
const primarySpecs = primarySpecsAll.filter(({ pom, target }) => {
|
|
4271
|
-
const
|
|
4272
|
-
const alternates = (pom.alternateFormattedDataTestIds ?? []).slice().sort();
|
|
4528
|
+
const alternates = (pom.alternateSelectors ?? []).slice().sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
|
4273
4529
|
const key = JSON.stringify({
|
|
4274
4530
|
role: pom.nativeRole,
|
|
4275
4531
|
methodName: pom.methodName,
|
|
4276
4532
|
getterNameOverride: pom.getterNameOverride ?? null,
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4533
|
+
selector: pom.selector,
|
|
4534
|
+
alternateSelectors: alternates.length ? alternates : void 0,
|
|
4535
|
+
parameters: pom.parameters,
|
|
4280
4536
|
target: target ?? null,
|
|
4281
4537
|
emitPrimary: pom.emitPrimary ?? true
|
|
4282
4538
|
});
|
|
@@ -4303,6 +4559,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
|
|
|
4303
4559
|
customPomAttachments = [],
|
|
4304
4560
|
projectRoot,
|
|
4305
4561
|
customPomDir,
|
|
4562
|
+
requireCustomPomDir,
|
|
4306
4563
|
customPomImportAliases,
|
|
4307
4564
|
customPomImportNameCollisionBehavior = "error",
|
|
4308
4565
|
testIdAttribute,
|
|
@@ -4335,6 +4592,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
|
|
|
4335
4592
|
customPomAttachments,
|
|
4336
4593
|
projectRoot,
|
|
4337
4594
|
customPomDir,
|
|
4595
|
+
requireCustomPomDir,
|
|
4338
4596
|
customPomImportAliases,
|
|
4339
4597
|
customPomImportNameCollisionBehavior,
|
|
4340
4598
|
testIdAttribute,
|
|
@@ -4344,6 +4602,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
|
|
|
4344
4602
|
customPomAttachments,
|
|
4345
4603
|
projectRoot,
|
|
4346
4604
|
customPomDir,
|
|
4605
|
+
requireCustomPomDir,
|
|
4347
4606
|
customPomImportAliases,
|
|
4348
4607
|
customPomImportNameCollisionBehavior,
|
|
4349
4608
|
testIdAttribute,
|
|
@@ -4399,9 +4658,15 @@ async function generateSplitTypeScriptFiles(componentHierarchyMap, vueFilesPathM
|
|
|
4399
4658
|
}
|
|
4400
4659
|
const customPomImportResolution = resolveCustomPomImportResolution(generatedClassNames, projectRoot, {
|
|
4401
4660
|
customPomDir: options.customPomDir,
|
|
4661
|
+
requireCustomPomDir: options.requireCustomPomDir,
|
|
4402
4662
|
customPomImportAliases: options.customPomImportAliases,
|
|
4403
4663
|
customPomImportNameCollisionBehavior: options.customPomImportNameCollisionBehavior
|
|
4404
4664
|
});
|
|
4665
|
+
assertCustomPomAttachmentsResolved(
|
|
4666
|
+
options.customPomAttachments ?? [],
|
|
4667
|
+
customPomImportResolution.classIdentifierMap,
|
|
4668
|
+
options.customPomDir ?? "tests/playwright/pom/custom"
|
|
4669
|
+
);
|
|
4405
4670
|
const runtimeBasePagePath = path.join(base, "_pom-runtime", "class-generation", "base-page.ts");
|
|
4406
4671
|
const files = [];
|
|
4407
4672
|
for (const [name, deps] of entries) {
|
|
@@ -4560,21 +4825,8 @@ function buildGeneratedGitAttributesFiles(generatedFilePaths) {
|
|
|
4560
4825
|
return { filePath, content };
|
|
4561
4826
|
});
|
|
4562
4827
|
}
|
|
4563
|
-
function
|
|
4564
|
-
const
|
|
4565
|
-
if (!needsInterpolation) {
|
|
4566
|
-
return JSON.stringify(formattedDataTestId);
|
|
4567
|
-
}
|
|
4568
|
-
const inner = formattedDataTestId.replace(/\$\{/g, "{");
|
|
4569
|
-
const quoted = JSON.stringify(inner);
|
|
4570
|
-
return `$${quoted}`;
|
|
4571
|
-
}
|
|
4572
|
-
function toCSharpParam(paramTypeExpr) {
|
|
4573
|
-
const trimmed = (paramTypeExpr ?? "").trim();
|
|
4574
|
-
const eqIdx = trimmed.indexOf("=");
|
|
4575
|
-
const left = eqIdx >= 0 ? trimmed.slice(0, eqIdx).trim() : trimmed;
|
|
4576
|
-
const right = eqIdx >= 0 ? trimmed.slice(eqIdx + 1).trim() : void 0;
|
|
4577
|
-
const typePart = left.includes("|") ? "string" : left;
|
|
4828
|
+
function toCSharpParam(param) {
|
|
4829
|
+
const typePart = param.type?.includes("|") ? "string" : param.type ?? "string";
|
|
4578
4830
|
let type = "string";
|
|
4579
4831
|
if (/(?:^|\s)boolean(?:\s|$)/.test(typePart))
|
|
4580
4832
|
type = "bool";
|
|
@@ -4582,19 +4834,17 @@ function toCSharpParam(paramTypeExpr) {
|
|
|
4582
4834
|
type = "string";
|
|
4583
4835
|
else if (/(?:^|\s)number(?:\s|$)/.test(typePart))
|
|
4584
4836
|
type = "int";
|
|
4585
|
-
else if (/\d+/.test(typePart) && typePart === "")
|
|
4586
|
-
type = "int";
|
|
4587
4837
|
else if (/\btimeOut\b/i.test(typePart))
|
|
4588
4838
|
type = "int";
|
|
4589
4839
|
let defaultExpr;
|
|
4590
|
-
if (
|
|
4840
|
+
if (param.initializer !== void 0) {
|
|
4591
4841
|
if (type === "bool") {
|
|
4592
|
-
defaultExpr =
|
|
4842
|
+
defaultExpr = param.initializer.includes("true") ? "true" : param.initializer.includes("false") ? "false" : void 0;
|
|
4593
4843
|
} else if (type === "int") {
|
|
4594
|
-
const m =
|
|
4844
|
+
const m = param.initializer.match(/\d+/);
|
|
4595
4845
|
defaultExpr = m ? m[0] : void 0;
|
|
4596
4846
|
} else {
|
|
4597
|
-
if (
|
|
4847
|
+
if (param.initializer === '""' || param.initializer === "''") {
|
|
4598
4848
|
defaultExpr = '""';
|
|
4599
4849
|
}
|
|
4600
4850
|
}
|
|
@@ -4602,17 +4852,15 @@ function toCSharpParam(paramTypeExpr) {
|
|
|
4602
4852
|
return { type, defaultExpr };
|
|
4603
4853
|
}
|
|
4604
4854
|
function formatCSharpParams(params) {
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
const entries = Object.entries(params);
|
|
4608
|
-
if (!entries.length)
|
|
4855
|
+
const normalizedParams = normalizePomParameters(params);
|
|
4856
|
+
if (!normalizedParams.length)
|
|
4609
4857
|
return { signature: "", argNames: [] };
|
|
4610
4858
|
const signatureParts = [];
|
|
4611
4859
|
const argNames = [];
|
|
4612
|
-
for (const
|
|
4613
|
-
const { type, defaultExpr } = toCSharpParam(
|
|
4614
|
-
argNames.push(name);
|
|
4615
|
-
signatureParts.push(defaultExpr !== void 0 ? `${type} ${name} = ${defaultExpr}` : `${type} ${name}`);
|
|
4860
|
+
for (const param of normalizedParams) {
|
|
4861
|
+
const { type, defaultExpr } = toCSharpParam(param);
|
|
4862
|
+
argNames.push(param.name);
|
|
4863
|
+
signatureParts.push(defaultExpr !== void 0 ? `${type} ${param.name} = ${defaultExpr}` : `${type} ${param.name}`);
|
|
4616
4864
|
}
|
|
4617
4865
|
return { signature: signatureParts.join(", "), argNames };
|
|
4618
4866
|
}
|
|
@@ -4703,23 +4951,13 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4703
4951
|
const baseMethodName = upperFirst(pom.methodName);
|
|
4704
4952
|
const baseGetterName = upperFirst(pom.getterNameOverride ?? pom.methodName);
|
|
4705
4953
|
const locatorName = baseGetterName.endsWith(roleSuffix) ? baseGetterName : `${baseGetterName}${roleSuffix}`;
|
|
4706
|
-
const
|
|
4707
|
-
const
|
|
4708
|
-
const
|
|
4709
|
-
const augmentedParams = { ...pom.params };
|
|
4710
|
-
for (const v of templateVars) {
|
|
4711
|
-
if (!Object.prototype.hasOwnProperty.call(augmentedParams, v)) {
|
|
4712
|
-
augmentedParams[v] = "string";
|
|
4713
|
-
}
|
|
4714
|
-
}
|
|
4715
|
-
const orderedParams = Object.fromEntries([
|
|
4716
|
-
...templateVars.map((v) => [v, augmentedParams[v]]),
|
|
4717
|
-
...Object.entries(augmentedParams).filter(([k]) => !templateVars.includes(k))
|
|
4718
|
-
]);
|
|
4954
|
+
const selectorIsParameterized = isParameterizedPomPattern(pom.selector.patternKind);
|
|
4955
|
+
const testIdExpr = toCSharpPomPatternExpression(pom.selector);
|
|
4956
|
+
const orderedParams = orderPomPatternParameters(pom.parameters, [pom.selector]);
|
|
4719
4957
|
const { signature, argNames } = formatCSharpParams(orderedParams);
|
|
4720
4958
|
const args = argNames.join(", ");
|
|
4721
|
-
const allTestIds =
|
|
4722
|
-
if (
|
|
4959
|
+
const allTestIds = uniquePomStringPatterns(pom.selector, pom.alternateSelectors);
|
|
4960
|
+
if (selectorIsParameterized) {
|
|
4723
4961
|
chunks.push(` public ILocator ${locatorName}(${signature}) => LocatorByTestId(${testIdExpr});`);
|
|
4724
4962
|
} else {
|
|
4725
4963
|
chunks.push(` public ILocator ${locatorName} => LocatorByTestId(${testIdExpr});`);
|
|
@@ -4730,12 +4968,12 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4730
4968
|
if (target) {
|
|
4731
4969
|
chunks.push(` public async Task<${target}> ${actionName}(${sig})`);
|
|
4732
4970
|
chunks.push(" {");
|
|
4733
|
-
if (
|
|
4734
|
-
chunks.push(` await ${locatorName}${
|
|
4971
|
+
if (selectorIsParameterized || allTestIds.length <= 1) {
|
|
4972
|
+
chunks.push(` await ${locatorName}${selectorIsParameterized ? `(${args})` : ""}.ClickAsync();`);
|
|
4735
4973
|
chunks.push(` return new ${target}(Page);`);
|
|
4736
4974
|
} else {
|
|
4737
4975
|
chunks.push(" Exception? lastError = null;");
|
|
4738
|
-
chunks.push(` foreach (var testId in new[] { ${allTestIds.map(
|
|
4976
|
+
chunks.push(` foreach (var testId in new[] { ${allTestIds.map((testId) => toCSharpPomPatternExpression(testId)).join(", ")} })`);
|
|
4739
4977
|
chunks.push(" {");
|
|
4740
4978
|
chunks.push(" try");
|
|
4741
4979
|
chunks.push(" {");
|
|
@@ -4759,7 +4997,7 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4759
4997
|
}
|
|
4760
4998
|
chunks.push(` public async Task ${actionName}(${sig})`);
|
|
4761
4999
|
chunks.push(" {");
|
|
4762
|
-
const callSuffix =
|
|
5000
|
+
const callSuffix = selectorIsParameterized ? `(${args})` : "";
|
|
4763
5001
|
const emitActionCall = (locatorAccess) => {
|
|
4764
5002
|
if (pom.nativeRole === "input") {
|
|
4765
5003
|
chunks.push(` var editableLocator = await ResolveEditableLocatorAsync(${locatorAccess});`);
|
|
@@ -4772,9 +5010,9 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4772
5010
|
chunks.push(` await ${locatorAccess}.ClickAsync();`);
|
|
4773
5011
|
}
|
|
4774
5012
|
};
|
|
4775
|
-
if (!
|
|
5013
|
+
if (!selectorIsParameterized && allTestIds.length > 1) {
|
|
4776
5014
|
chunks.push(" Exception? lastError = null;");
|
|
4777
|
-
chunks.push(` foreach (var testId in new[] { ${allTestIds.map(
|
|
5015
|
+
chunks.push(` foreach (var testId in new[] { ${allTestIds.map((testId) => toCSharpPomPatternExpression(testId)).join(", ")} })`);
|
|
4778
5016
|
chunks.push(" {");
|
|
4779
5017
|
chunks.push(" try");
|
|
4780
5018
|
chunks.push(" {");
|
|
@@ -4820,7 +5058,12 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4820
5058
|
for (const extra of extras) {
|
|
4821
5059
|
if (extra.kind !== "click")
|
|
4822
5060
|
continue;
|
|
4823
|
-
const
|
|
5061
|
+
const extraParams = orderPomPatternParameters(
|
|
5062
|
+
extra.parameters,
|
|
5063
|
+
getSelectorPatterns(extra.selector),
|
|
5064
|
+
{ omit: extra.keyLiteral !== void 0 ? ["key"] : [] }
|
|
5065
|
+
);
|
|
5066
|
+
const { signature } = formatCSharpParams(extraParams);
|
|
4824
5067
|
const extraName = upperFirst(extra.name);
|
|
4825
5068
|
chunks.push(` public async Task ${extraName}Async(${signature})`);
|
|
4826
5069
|
chunks.push(" {");
|
|
@@ -4828,29 +5071,22 @@ function generateAggregatedCSharpFiles(componentHierarchyMap, outDir, options =
|
|
|
4828
5071
|
chunks.push(` var key = ${JSON.stringify(extra.keyLiteral)};`);
|
|
4829
5072
|
}
|
|
4830
5073
|
if (extra.selector.kind === "testId") {
|
|
4831
|
-
const
|
|
4832
|
-
const
|
|
4833
|
-
|
|
4834
|
-
chunks.push(` var testId = ${testIdExpr};`);
|
|
4835
|
-
chunks.push(" await LocatorByTestId(testId).ClickAsync();");
|
|
4836
|
-
} else {
|
|
4837
|
-
chunks.push(` await LocatorByTestId(${testIdExpr}).ClickAsync();`);
|
|
5074
|
+
const testIdBinding = bindCSharpPomPattern(extra.selector.testId, "testId");
|
|
5075
|
+
for (const statement of testIdBinding.setupStatements) {
|
|
5076
|
+
chunks.push(` ${statement}`);
|
|
4838
5077
|
}
|
|
5078
|
+
chunks.push(` await LocatorByTestId(${testIdBinding.expression}).ClickAsync();`);
|
|
4839
5079
|
} else {
|
|
4840
|
-
const
|
|
4841
|
-
const
|
|
4842
|
-
const rootExpr = toCSharpTestIdExpression(extra.selector.rootFormattedDataTestId);
|
|
4843
|
-
const labelExpr = toCSharpTestIdExpression(extra.selector.formattedLabel);
|
|
5080
|
+
const rootBinding = bindCSharpPomPattern(extra.selector.rootTestId, "rootTestId");
|
|
5081
|
+
const labelBinding = bindCSharpPomPattern(extra.selector.label, "label");
|
|
4844
5082
|
const exactArg = extra.selector.exact === false ? "false" : "true";
|
|
4845
|
-
|
|
4846
|
-
chunks.push(`
|
|
5083
|
+
for (const statement of rootBinding.setupStatements) {
|
|
5084
|
+
chunks.push(` ${statement}`);
|
|
4847
5085
|
}
|
|
4848
|
-
|
|
4849
|
-
chunks.push(`
|
|
5086
|
+
for (const statement of labelBinding.setupStatements) {
|
|
5087
|
+
chunks.push(` ${statement}`);
|
|
4850
5088
|
}
|
|
4851
|
-
|
|
4852
|
-
const labelArg = labelNeedsTemplate ? "label" : labelExpr;
|
|
4853
|
-
chunks.push(` await ClickWithinTestIdByLabelAsync(${rootArg}, ${labelArg}, ${exactArg});`);
|
|
5089
|
+
chunks.push(` await ClickWithinTestIdByLabelAsync(${rootBinding.expression}, ${labelBinding.expression}, ${exactArg});`);
|
|
4854
5090
|
}
|
|
4855
5091
|
chunks.push(" }");
|
|
4856
5092
|
chunks.push("");
|
|
@@ -5294,7 +5530,7 @@ function getViewPassthroughMethods(viewName, viewDependencies, childrenComponent
|
|
|
5294
5530
|
if (existingOnView.has(name) || blockedMethodNames.has(name))
|
|
5295
5531
|
continue;
|
|
5296
5532
|
const list = methodToChildren.get(name) ?? [];
|
|
5297
|
-
list.push({ childProp,
|
|
5533
|
+
list.push({ childProp, signature: sig });
|
|
5298
5534
|
methodToChildren.set(name, list);
|
|
5299
5535
|
}
|
|
5300
5536
|
}
|
|
@@ -5304,12 +5540,12 @@ function getViewPassthroughMethods(viewName, viewDependencies, childrenComponent
|
|
|
5304
5540
|
return [];
|
|
5305
5541
|
}
|
|
5306
5542
|
return passthroughs.map(([methodName, candidates]) => {
|
|
5307
|
-
const { childProp,
|
|
5308
|
-
const callArgs =
|
|
5543
|
+
const { childProp, signature } = candidates[0];
|
|
5544
|
+
const callArgs = getPomParameterArgumentNames(signature.parameters).join(", ");
|
|
5309
5545
|
return createClassMethod({
|
|
5310
5546
|
name: methodName,
|
|
5311
5547
|
isAsync: true,
|
|
5312
|
-
parameters:
|
|
5548
|
+
parameters: toTypeScriptPomParameterStructures(signature.parameters),
|
|
5313
5549
|
statements: [
|
|
5314
5550
|
`return await this.${childProp}.${methodName}(${callArgs});`
|
|
5315
5551
|
]
|
|
@@ -5333,8 +5569,7 @@ function getAttachmentPassthroughMethods(ownerName, ownerDependencies, attachmen
|
|
|
5333
5569
|
const list = methodToAttachments.get(methodName) ?? [];
|
|
5334
5570
|
list.push({
|
|
5335
5571
|
propertyName: attachment.propertyName,
|
|
5336
|
-
|
|
5337
|
-
argNames: signature.argNames
|
|
5572
|
+
signature
|
|
5338
5573
|
});
|
|
5339
5574
|
methodToAttachments.set(methodName, list);
|
|
5340
5575
|
}
|
|
@@ -5345,12 +5580,12 @@ function getAttachmentPassthroughMethods(ownerName, ownerDependencies, attachmen
|
|
|
5345
5580
|
return [];
|
|
5346
5581
|
}
|
|
5347
5582
|
return passthroughs.map(([methodName, candidates]) => {
|
|
5348
|
-
const { propertyName,
|
|
5349
|
-
const callArgs =
|
|
5583
|
+
const { propertyName, signature } = candidates[0];
|
|
5584
|
+
const callArgs = getPomParameterArgumentNames(signature.parameters).join(", ");
|
|
5350
5585
|
const invocation = callArgs ? `this.${propertyName}.${methodName}(${callArgs})` : `this.${propertyName}.${methodName}()`;
|
|
5351
5586
|
return createClassMethod({
|
|
5352
5587
|
name: methodName,
|
|
5353
|
-
parameters:
|
|
5588
|
+
parameters: toTypeScriptPomParameterStructures(signature.parameters),
|
|
5354
5589
|
statements: [
|
|
5355
5590
|
`return ${invocation};`
|
|
5356
5591
|
]
|
|
@@ -5364,15 +5599,44 @@ function sliceNodeSource(source, node) {
|
|
|
5364
5599
|
const snippet = source.slice(node.start, node.end).trim();
|
|
5365
5600
|
return snippet.length ? snippet : null;
|
|
5366
5601
|
}
|
|
5367
|
-
function
|
|
5602
|
+
function getTypeAnnotationSource(source, node) {
|
|
5603
|
+
const rawTypeAnnotation = node.typeAnnotation;
|
|
5604
|
+
if (!rawTypeAnnotation || typeof rawTypeAnnotation !== "object" || !("type" in rawTypeAnnotation) || rawTypeAnnotation.type !== "TSTypeAnnotation" || !("typeAnnotation" in rawTypeAnnotation)) {
|
|
5605
|
+
return void 0;
|
|
5606
|
+
}
|
|
5607
|
+
const typeAnnotation = rawTypeAnnotation.typeAnnotation;
|
|
5608
|
+
return typeAnnotation && typeof typeAnnotation === "object" ? sliceNodeSource(source, typeAnnotation) ?? void 0 : void 0;
|
|
5609
|
+
}
|
|
5610
|
+
function getCustomPomParameterSpec(source, param) {
|
|
5368
5611
|
if (param.type === "Identifier") {
|
|
5369
|
-
return param.name
|
|
5612
|
+
return createPomParameterSpec(param.name, getTypeAnnotationSource(source, param), {
|
|
5613
|
+
hasQuestionToken: !!param.optional
|
|
5614
|
+
});
|
|
5370
5615
|
}
|
|
5371
5616
|
if (param.type === "AssignmentPattern") {
|
|
5372
|
-
|
|
5617
|
+
if (param.left.type !== "Identifier") {
|
|
5618
|
+
return null;
|
|
5619
|
+
}
|
|
5620
|
+
const initializer = sliceNodeSource(source, param.right);
|
|
5621
|
+
if (!initializer) {
|
|
5622
|
+
return null;
|
|
5623
|
+
}
|
|
5624
|
+
return createPomParameterSpec(param.left.name, getTypeAnnotationSource(source, param.left), {
|
|
5625
|
+
initializer,
|
|
5626
|
+
hasQuestionToken: !!param.left.optional
|
|
5627
|
+
});
|
|
5373
5628
|
}
|
|
5374
5629
|
if (param.type === "RestElement") {
|
|
5375
|
-
|
|
5630
|
+
if (param.argument.type !== "Identifier") {
|
|
5631
|
+
return null;
|
|
5632
|
+
}
|
|
5633
|
+
const typeExpression = getTypeAnnotationSource(
|
|
5634
|
+
source,
|
|
5635
|
+
param
|
|
5636
|
+
) ?? getTypeAnnotationSource(source, param.argument);
|
|
5637
|
+
return createPomParameterSpec(param.argument.name, typeExpression, {
|
|
5638
|
+
isRestParameter: true
|
|
5639
|
+
});
|
|
5376
5640
|
}
|
|
5377
5641
|
return null;
|
|
5378
5642
|
}
|
|
@@ -5405,29 +5669,23 @@ function extractCustomPomMethodSignatures(source, exportName) {
|
|
|
5405
5669
|
if (member.key.type !== "Identifier") {
|
|
5406
5670
|
continue;
|
|
5407
5671
|
}
|
|
5408
|
-
const
|
|
5409
|
-
const argNames = [];
|
|
5672
|
+
const parameters = [];
|
|
5410
5673
|
let supported = true;
|
|
5411
5674
|
member.params.forEach((param) => {
|
|
5412
5675
|
if (!supported) {
|
|
5413
5676
|
return;
|
|
5414
5677
|
}
|
|
5415
|
-
const
|
|
5416
|
-
|
|
5417
|
-
if (!paramSource || !argName) {
|
|
5678
|
+
const parameter = getCustomPomParameterSpec(source, param);
|
|
5679
|
+
if (!parameter) {
|
|
5418
5680
|
supported = false;
|
|
5419
5681
|
return;
|
|
5420
5682
|
}
|
|
5421
|
-
|
|
5422
|
-
argNames.push(argName);
|
|
5683
|
+
parameters.push(parameter);
|
|
5423
5684
|
});
|
|
5424
5685
|
if (!supported) {
|
|
5425
5686
|
continue;
|
|
5426
5687
|
}
|
|
5427
|
-
signatures.set(member.key.name,
|
|
5428
|
-
params: params.join(", "),
|
|
5429
|
-
argNames
|
|
5430
|
-
});
|
|
5688
|
+
signatures.set(member.key.name, createPomMethodSignature(parameters));
|
|
5431
5689
|
}
|
|
5432
5690
|
}
|
|
5433
5691
|
return signatures;
|
|
@@ -5610,6 +5868,9 @@ function resolveCustomPomImportResolution(generatedClassNames, projectRoot, opti
|
|
|
5610
5868
|
const customDirRelOrAbs = options.customPomDir ?? "tests/playwright/pom/custom";
|
|
5611
5869
|
const customDirAbs = path.isAbsolute(customDirRelOrAbs) ? customDirRelOrAbs : path.resolve(projectRoot, customDirRelOrAbs);
|
|
5612
5870
|
if (!fs.existsSync(customDirAbs)) {
|
|
5871
|
+
if (options.requireCustomPomDir) {
|
|
5872
|
+
throw createMissingCustomPomDirectoryError(customDirRelOrAbs, customDirAbs);
|
|
5873
|
+
}
|
|
5613
5874
|
return {
|
|
5614
5875
|
classIdentifierMap,
|
|
5615
5876
|
methodSignaturesByClass,
|
|
@@ -5652,6 +5913,14 @@ function resolveCustomPomImportResolution(generatedClassNames, projectRoot, opti
|
|
|
5652
5913
|
importSpecifiersByClass
|
|
5653
5914
|
};
|
|
5654
5915
|
}
|
|
5916
|
+
function assertCustomPomAttachmentsResolved(attachments, classIdentifierMap, configuredDir) {
|
|
5917
|
+
const missingClassNames = Array.from(new Set(
|
|
5918
|
+
attachments.map((attachment) => attachment.className).filter((className) => !Object.prototype.hasOwnProperty.call(classIdentifierMap, className))
|
|
5919
|
+
)).sort((left, right) => left.localeCompare(right));
|
|
5920
|
+
if (missingClassNames.length > 0) {
|
|
5921
|
+
throw createMissingCustomPomAttachmentClassError(missingClassNames, configuredDir);
|
|
5922
|
+
}
|
|
5923
|
+
}
|
|
5655
5924
|
function getComposedStubBody(targetClassName, availableClassNames, depsByClassName, vueFilesPathMap, projectRoot) {
|
|
5656
5925
|
const filePath = resolveVueSourcePath(targetClassName, vueFilesPathMap, projectRoot);
|
|
5657
5926
|
if (!filePath)
|
|
@@ -5680,7 +5949,7 @@ function getComposedStubBody(targetClassName, availableClassNames, depsByClassNa
|
|
|
5680
5949
|
if (!sig)
|
|
5681
5950
|
continue;
|
|
5682
5951
|
const list = methodToChildren.get(name) ?? [];
|
|
5683
|
-
list.push({ child,
|
|
5952
|
+
list.push({ child, signature: sig });
|
|
5684
5953
|
methodToChildren.set(name, list);
|
|
5685
5954
|
}
|
|
5686
5955
|
}
|
|
@@ -5688,12 +5957,12 @@ function getComposedStubBody(targetClassName, availableClassNames, depsByClassNa
|
|
|
5688
5957
|
for (const [methodName, candidatesForMethod] of methodToChildren.entries()) {
|
|
5689
5958
|
if (candidatesForMethod.length !== 1 || methodName === "constructor")
|
|
5690
5959
|
continue;
|
|
5691
|
-
const { child,
|
|
5692
|
-
const callArgs =
|
|
5960
|
+
const { child, signature } = candidatesForMethod[0];
|
|
5961
|
+
const callArgs = getPomParameterArgumentNames(signature.parameters).join(", ");
|
|
5693
5962
|
passthroughMembers.push(createClassMethod({
|
|
5694
5963
|
name: methodName,
|
|
5695
5964
|
isAsync: true,
|
|
5696
|
-
parameters:
|
|
5965
|
+
parameters: toTypeScriptPomParameterStructures(signature.parameters),
|
|
5697
5966
|
statements: [
|
|
5698
5967
|
`return await this.${child}.${methodName}(${callArgs});`
|
|
5699
5968
|
]
|
|
@@ -5742,9 +6011,15 @@ async function generateAggregatedFiles(componentHierarchyMap, vueFilesPathMap, b
|
|
|
5742
6011
|
imports.push(`export * from "${runtimeClassGenRel}/base-page";`);
|
|
5743
6012
|
const customPomImportResolution = resolveCustomPomImportResolution(generatedClassNames, projectRoot, {
|
|
5744
6013
|
customPomDir: options.customPomDir,
|
|
6014
|
+
requireCustomPomDir: options.requireCustomPomDir,
|
|
5745
6015
|
customPomImportAliases: options.customPomImportAliases,
|
|
5746
6016
|
customPomImportNameCollisionBehavior: options.customPomImportNameCollisionBehavior
|
|
5747
6017
|
});
|
|
6018
|
+
assertCustomPomAttachmentsResolved(
|
|
6019
|
+
options.customPomAttachments ?? [],
|
|
6020
|
+
customPomImportResolution.classIdentifierMap,
|
|
6021
|
+
options.customPomDir ?? "tests/playwright/pom/custom"
|
|
6022
|
+
);
|
|
5748
6023
|
const customPomClassIdentifierMap = customPomImportResolution.classIdentifierMap;
|
|
5749
6024
|
const customPomMethodSignaturesByClass = customPomImportResolution.methodSignaturesByClass;
|
|
5750
6025
|
const customPomAvailableClassIdentifiers = customPomImportResolution.availableClassIdentifiers;
|
|
@@ -5894,8 +6169,8 @@ function getWidgetInstancesForView(componentName, dataTestIdSet, availableClassI
|
|
|
5894
6169
|
return candidate;
|
|
5895
6170
|
};
|
|
5896
6171
|
for (const dt of dataTestIdSet) {
|
|
5897
|
-
const raw = dt.
|
|
5898
|
-
if (
|
|
6172
|
+
const raw = dt.selectorValue.formatted;
|
|
6173
|
+
if (isParameterizedPomPattern(dt.selectorValue.patternKind)) {
|
|
5899
6174
|
continue;
|
|
5900
6175
|
}
|
|
5901
6176
|
const toggleSuffix = "-toggle";
|
|
@@ -5985,7 +6260,6 @@ function getConstructor(childrenComponent, componentHierarchyMap, attachmentsFor
|
|
|
5985
6260
|
});
|
|
5986
6261
|
}
|
|
5987
6262
|
const TESTID_CLICK_EVENT_NAME = "__testid_event__";
|
|
5988
|
-
const TESTID_CLICK_EVENT_STRICT_FLAG = "__testid_click_event_strict__";
|
|
5989
6263
|
const CLICK_EVENT_NAME = TESTID_CLICK_EVENT_NAME;
|
|
5990
6264
|
const inferredNativeWrapperConfigByLookup = /* @__PURE__ */ new Map();
|
|
5991
6265
|
const inferredSfcPathByLookup = /* @__PURE__ */ new Map();
|
|
@@ -6247,7 +6521,7 @@ function getConditionalDirectiveInfo(element) {
|
|
|
6247
6521
|
if (directive.name === "else") {
|
|
6248
6522
|
const exp2 = directive.exp;
|
|
6249
6523
|
if (exp2 && (exp2.type === NodeTypes.SIMPLE_EXPRESSION || exp2.type === NodeTypes.COMPOUND_EXPRESSION)) {
|
|
6250
|
-
const source2 = (exp2
|
|
6524
|
+
const source2 = getVueExpressionSource(exp2, "content", "compiled");
|
|
6251
6525
|
return { kind: "else-if", source: source2 };
|
|
6252
6526
|
}
|
|
6253
6527
|
return { kind: "else", source: "" };
|
|
@@ -6256,13 +6530,13 @@ function getConditionalDirectiveInfo(element) {
|
|
|
6256
6530
|
const exp2 = directive.exp;
|
|
6257
6531
|
if (!exp2 || exp2.type !== NodeTypes.SIMPLE_EXPRESSION && exp2.type !== NodeTypes.COMPOUND_EXPRESSION)
|
|
6258
6532
|
return null;
|
|
6259
|
-
const source2 = (exp2
|
|
6533
|
+
const source2 = getVueExpressionSource(exp2, "content", "compiled");
|
|
6260
6534
|
return { kind: "else-if", source: source2 };
|
|
6261
6535
|
}
|
|
6262
6536
|
const exp = directive.exp;
|
|
6263
6537
|
if (!exp || exp.type !== NodeTypes.SIMPLE_EXPRESSION && exp.type !== NodeTypes.COMPOUND_EXPRESSION)
|
|
6264
6538
|
return null;
|
|
6265
|
-
const source = (exp
|
|
6539
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
6266
6540
|
return { kind: directive.name, source };
|
|
6267
6541
|
}
|
|
6268
6542
|
function tryExtractStableHintFromConditionalExpressionSource(source) {
|
|
@@ -6288,26 +6562,26 @@ function tryExtractStableHintFromConditionalExpressionSource(source) {
|
|
|
6288
6562
|
};
|
|
6289
6563
|
try {
|
|
6290
6564
|
const expr = parseExpression(src, { plugins: ["typescript"] });
|
|
6291
|
-
const
|
|
6565
|
+
const isNodeType2 = (n, type) => {
|
|
6292
6566
|
return n !== null && n.type === type;
|
|
6293
6567
|
};
|
|
6294
|
-
const
|
|
6295
|
-
return
|
|
6568
|
+
const isStringLiteralNode2 = (n) => {
|
|
6569
|
+
return isNodeType2(n, "StringLiteral") && typeof n.value === "string";
|
|
6296
6570
|
};
|
|
6297
|
-
const
|
|
6298
|
-
return
|
|
6571
|
+
const isIdentifierNode2 = (n) => {
|
|
6572
|
+
return isNodeType2(n, "Identifier") && typeof n.name === "string";
|
|
6299
6573
|
};
|
|
6300
6574
|
const results = [];
|
|
6301
6575
|
const walk = (n) => {
|
|
6302
6576
|
if (!n)
|
|
6303
6577
|
return;
|
|
6304
|
-
if (
|
|
6578
|
+
if (isStringLiteralNode2(n)) {
|
|
6305
6579
|
const v = (n.value ?? "").trim();
|
|
6306
6580
|
if (isIdentifierish(v)) {
|
|
6307
6581
|
results.push(v);
|
|
6308
6582
|
}
|
|
6309
6583
|
}
|
|
6310
|
-
if (
|
|
6584
|
+
if (isIdentifierNode2(n)) {
|
|
6311
6585
|
const v = (n.name ?? "").trim();
|
|
6312
6586
|
if (isIdentifierish(v)) {
|
|
6313
6587
|
results.push(v);
|
|
@@ -6457,34 +6731,20 @@ ${buildSearchRootsKey(normalizedSearchRoots)}`;
|
|
|
6457
6731
|
inferredNativeWrapperConfigByLookup.set(cacheKey, { role: "" });
|
|
6458
6732
|
return null;
|
|
6459
6733
|
}
|
|
6460
|
-
function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
6734
|
+
function tryWrapClickDirectiveForTestEvents(element, testIdAttribute, resolvedRuntimeTestId) {
|
|
6461
6735
|
const jsStringLiteral = (value) => {
|
|
6462
6736
|
return JSON.stringify(value);
|
|
6463
6737
|
};
|
|
6464
6738
|
const getTestIdExpressionForNode = () => {
|
|
6465
|
-
|
|
6466
|
-
if (!existing) {
|
|
6467
|
-
return "undefined";
|
|
6468
|
-
}
|
|
6469
|
-
if (existing.type === NodeTypes.ATTRIBUTE) {
|
|
6470
|
-
const v = existing.value?.content;
|
|
6471
|
-
if (!v) {
|
|
6472
|
-
return "undefined";
|
|
6473
|
-
}
|
|
6474
|
-
return jsStringLiteral(v);
|
|
6475
|
-
}
|
|
6476
|
-
const directive = existing;
|
|
6477
|
-
const exp2 = directive.exp;
|
|
6478
|
-
if (!exp2 || exp2.type !== NodeTypes.SIMPLE_EXPRESSION) {
|
|
6739
|
+
if (!resolvedRuntimeTestId) {
|
|
6479
6740
|
return "undefined";
|
|
6480
6741
|
}
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
return "undefined";
|
|
6742
|
+
if (resolvedRuntimeTestId.kind === "static") {
|
|
6743
|
+
return jsStringLiteral(resolvedRuntimeTestId.value);
|
|
6484
6744
|
}
|
|
6485
|
-
return `(${
|
|
6745
|
+
return `(${renderTemplateLiteralExpression(resolvedRuntimeTestId)})`;
|
|
6486
6746
|
};
|
|
6487
|
-
const
|
|
6747
|
+
const testIdExpression = getTestIdExpressionForNode();
|
|
6488
6748
|
const clickDirective = tryGetClickDirective(element);
|
|
6489
6749
|
if (!clickDirective)
|
|
6490
6750
|
return;
|
|
@@ -6495,10 +6755,10 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6495
6755
|
const exp = clickDirective.exp;
|
|
6496
6756
|
if (!exp)
|
|
6497
6757
|
return;
|
|
6498
|
-
const existingSource = (exp
|
|
6758
|
+
const existingSource = getVueExpressionSource(exp, "loc", "content");
|
|
6499
6759
|
if (existingSource.includes(CLICK_EVENT_NAME))
|
|
6500
6760
|
return;
|
|
6501
|
-
const originalExpression = (exp
|
|
6761
|
+
const originalExpression = getVueExpressionSource(exp, "content", "loc");
|
|
6502
6762
|
if (!originalExpression)
|
|
6503
6763
|
return;
|
|
6504
6764
|
const isStatementBody = (() => {
|
|
@@ -6515,7 +6775,7 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6515
6775
|
const statementWrappedHandler = `($event) => {
|
|
6516
6776
|
const __win = ($event && $event.view) ? $event.view : undefined;
|
|
6517
6777
|
const __target = ($event && $event.currentTarget) ? $event.currentTarget : undefined;
|
|
6518
|
-
const __testIdFromNode = ${
|
|
6778
|
+
const __testIdFromNode = ${testIdExpression};
|
|
6519
6779
|
const __testIdFromTarget = (__target && typeof __target.getAttribute === 'function') ? __target.getAttribute(${jsStringLiteral(testIdAttribute)}) : undefined;
|
|
6520
6780
|
const __testId = (__testIdFromNode ?? __testIdFromTarget);
|
|
6521
6781
|
const __emit = (phase, err) => {
|
|
@@ -6526,16 +6786,12 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6526
6786
|
__w.dispatchEvent(new __CustomEvent('${CLICK_EVENT_NAME}', { detail: { testId: __testId, phase, err: err ? String(err) : undefined } }));
|
|
6527
6787
|
}
|
|
6528
6788
|
} catch (e) {
|
|
6529
|
-
// Instrumentation
|
|
6530
|
-
// In strict mode we rethrow so tests fail fast and the underlying problem is visible.
|
|
6531
|
-
// Outside strict mode we log and continue so we don't break real user clicks.
|
|
6789
|
+
// Instrumentation failures should never be silent. Log the root cause and fail fast.
|
|
6532
6790
|
const __w = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
6533
6791
|
if (__w && __w.console && typeof __w.console.error === 'function') {
|
|
6534
6792
|
__w.console.error('[testid-click-event] failed to emit ${CLICK_EVENT_NAME}', e);
|
|
6535
6793
|
}
|
|
6536
|
-
|
|
6537
|
-
throw e;
|
|
6538
|
-
}
|
|
6794
|
+
throw e;
|
|
6539
6795
|
}
|
|
6540
6796
|
};
|
|
6541
6797
|
const __w2 = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
@@ -6566,7 +6822,7 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6566
6822
|
const expressionWrappedHandler = `($event) => {
|
|
6567
6823
|
const __win = ($event && $event.view) ? $event.view : undefined;
|
|
6568
6824
|
const __target = ($event && $event.currentTarget) ? $event.currentTarget : undefined;
|
|
6569
|
-
const __testIdFromNode = ${
|
|
6825
|
+
const __testIdFromNode = ${testIdExpression};
|
|
6570
6826
|
const __testIdFromTarget = (__target && typeof __target.getAttribute === 'function') ? __target.getAttribute(${jsStringLiteral(testIdAttribute)}) : undefined;
|
|
6571
6827
|
const __testId = (__testIdFromNode ?? __testIdFromTarget);
|
|
6572
6828
|
const __emit = (phase, err) => {
|
|
@@ -6577,16 +6833,12 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6577
6833
|
__w.dispatchEvent(new __CustomEvent('${CLICK_EVENT_NAME}', { detail: { testId: __testId, phase, err: err ? String(err) : undefined } }));
|
|
6578
6834
|
}
|
|
6579
6835
|
} catch (e) {
|
|
6580
|
-
// Instrumentation
|
|
6581
|
-
// In strict mode we rethrow so tests fail fast and the underlying problem is visible.
|
|
6582
|
-
// Outside strict mode we log and continue so we don't break real user clicks.
|
|
6836
|
+
// Instrumentation failures should never be silent. Log the root cause and fail fast.
|
|
6583
6837
|
const __w = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
6584
6838
|
if (__w && __w.console && typeof __w.console.error === 'function') {
|
|
6585
6839
|
__w.console.error('[testid-click-event] failed to emit ${CLICK_EVENT_NAME}', e);
|
|
6586
6840
|
}
|
|
6587
|
-
|
|
6588
|
-
throw e;
|
|
6589
|
-
}
|
|
6841
|
+
throw e;
|
|
6590
6842
|
}
|
|
6591
6843
|
};
|
|
6592
6844
|
const __w2 = __win || (__target && __target.ownerDocument && __target.ownerDocument.defaultView);
|
|
@@ -6624,10 +6876,9 @@ function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
|
|
|
6624
6876
|
let previousFileName = "";
|
|
6625
6877
|
const hierarchyMap = /* @__PURE__ */ new Map();
|
|
6626
6878
|
function createTestIdTransform(componentName, componentHierarchyMap, nativeWrappers = {}, excludedComponents = [], viewsDirAbs, options = {}) {
|
|
6627
|
-
const existingIdBehavior = options.existingIdBehavior ?? "
|
|
6879
|
+
const existingIdBehavior = options.existingIdBehavior ?? "error";
|
|
6628
6880
|
const testIdAttribute = (options.testIdAttribute || "data-testid").trim() || "data-testid";
|
|
6629
|
-
const nameCollisionBehavior = options.nameCollisionBehavior ?? "
|
|
6630
|
-
const missingSemanticNameBehavior = options.missingSemanticNameBehavior ?? "error";
|
|
6881
|
+
const nameCollisionBehavior = options.nameCollisionBehavior ?? "error";
|
|
6631
6882
|
const warn = options.warn;
|
|
6632
6883
|
const vueFilesPathMap = options.vueFilesPathMap;
|
|
6633
6884
|
const wrapperSearchRoots = options.wrapperSearchRoots ?? [];
|
|
@@ -6703,7 +6954,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6703
6954
|
conditionalHintByIfBranch.set(branch, hint);
|
|
6704
6955
|
continue;
|
|
6705
6956
|
}
|
|
6706
|
-
const condSource = (cond
|
|
6957
|
+
const condSource = getVueExpressionSource(cond, "content", "compiled");
|
|
6707
6958
|
const stable = tryExtractStableHintFromConditionalExpressionSource(condSource);
|
|
6708
6959
|
if (stable) {
|
|
6709
6960
|
conditionalHintByIfBranch.set(branch, stable);
|
|
@@ -6765,17 +7016,18 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6765
7016
|
nativeWrappers[element.tag] = { role: "grid" };
|
|
6766
7017
|
}
|
|
6767
7018
|
}
|
|
6768
|
-
const
|
|
7019
|
+
const getBestAvailableKeyInfo = () => {
|
|
6769
7020
|
const parentNode = context.parent && typeof context.parent === "object" ? context.parent : null;
|
|
6770
7021
|
const isDirectVForChild = parentNode?.type === NodeTypes.FOR;
|
|
6771
|
-
const
|
|
6772
|
-
if (
|
|
6773
|
-
|
|
7022
|
+
const vForKeyInfo = (isDirectVForChild ? getKeyDirectiveInfo(element) : null) || getContainedInVForDirectiveKeyInfo(context, element, hierarchyMap);
|
|
7023
|
+
if (vForKeyInfo) {
|
|
7024
|
+
return vForKeyInfo;
|
|
7025
|
+
}
|
|
7026
|
+
return getContainedInSlotDataKeyInfo(element, hierarchyMap);
|
|
6774
7027
|
};
|
|
6775
|
-
const
|
|
6776
|
-
const
|
|
6777
|
-
const
|
|
6778
|
-
const bestKeyVariable = isSlotKey ? bestKeyInferred : null;
|
|
7028
|
+
const bestKeyInfo = getBestAvailableKeyInfo();
|
|
7029
|
+
const bestKeyPlaceholder = bestKeyInfo?.selectorFragment ?? null;
|
|
7030
|
+
const bestRuntimeKeyPlaceholder = bestKeyInfo?.runtimeFragment ?? null;
|
|
6779
7031
|
const keyValuesOverride = tryGetContainedInStaticVForSourceLiteralValues(context);
|
|
6780
7032
|
const parentKey = context?.parent ? context.parent : null;
|
|
6781
7033
|
const conditional = getConditionalDirectiveInfo(element);
|
|
@@ -6818,7 +7070,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6818
7070
|
if (!cond) {
|
|
6819
7071
|
conditionalHint = "else";
|
|
6820
7072
|
} else {
|
|
6821
|
-
const condSource = (cond
|
|
7073
|
+
const condSource = getVueExpressionSource(cond, "content", "compiled");
|
|
6822
7074
|
conditionalHint = tryExtractStableHintFromConditionalExpressionSource(condSource) ?? "if";
|
|
6823
7075
|
}
|
|
6824
7076
|
}
|
|
@@ -6828,7 +7080,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6828
7080
|
});
|
|
6829
7081
|
if (showDirective?.exp && (showDirective.exp.type === NodeTypes.SIMPLE_EXPRESSION || showDirective.exp.type === NodeTypes.COMPOUND_EXPRESSION)) {
|
|
6830
7082
|
const exp = showDirective.exp;
|
|
6831
|
-
const source = (exp
|
|
7083
|
+
const source = getVueExpressionSource(exp, "content", "compiled");
|
|
6832
7084
|
const showHint = tryExtractStableHintFromConditionalExpressionSource(source);
|
|
6833
7085
|
if (showHint) {
|
|
6834
7086
|
conditionalHint = conditionalHint ? `${conditionalHint} ${showHint}` : showHint;
|
|
@@ -6862,13 +7114,17 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6862
7114
|
const tagSuffix = getTagSuffix();
|
|
6863
7115
|
return bestKeyPlaceholder ? templateAttributeValue(`${componentName}-${bestKeyPlaceholder}${clickSuffix}${tagSuffix}`) : staticAttributeValue(`${componentName}${clickSuffix}${tagSuffix}`);
|
|
6864
7116
|
};
|
|
7117
|
+
const getClickRuntimeDataTestId = (clickSuffix) => {
|
|
7118
|
+
const tagSuffix = getTagSuffix();
|
|
7119
|
+
return bestRuntimeKeyPlaceholder ? templateAttributeValue(`${componentName}-${bestRuntimeKeyPlaceholder}${clickSuffix}${tagSuffix}`) : staticAttributeValue(`${componentName}${clickSuffix}${tagSuffix}`);
|
|
7120
|
+
};
|
|
6865
7121
|
const getSubmitDataTestId = (identifier) => {
|
|
6866
7122
|
const tagSuffix = getTagSuffix();
|
|
6867
7123
|
return `${componentName}-${identifier}${tagSuffix}`;
|
|
6868
7124
|
};
|
|
6869
7125
|
const applyResolvedDataTestIdForElement = (args) => {
|
|
6870
7126
|
const nativeRole = args.nativeRoleOverride ?? getNativeRoleFromTagSuffix();
|
|
6871
|
-
applyResolvedDataTestId({
|
|
7127
|
+
return applyResolvedDataTestId({
|
|
6872
7128
|
element,
|
|
6873
7129
|
componentName,
|
|
6874
7130
|
parentComponentName,
|
|
@@ -6878,8 +7134,8 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6878
7134
|
generatedMethodContentByComponent,
|
|
6879
7135
|
nativeRole,
|
|
6880
7136
|
preferredGeneratedValue: args.preferredGeneratedValue,
|
|
6881
|
-
|
|
6882
|
-
|
|
7137
|
+
preferredRuntimeValue: args.preferredRuntimeValue,
|
|
7138
|
+
keyInfo: bestKeyInfo,
|
|
6883
7139
|
keyValuesOverride,
|
|
6884
7140
|
entryOverrides: args.entryOverrides,
|
|
6885
7141
|
semanticNameHint: args.semanticNameHint,
|
|
@@ -6897,7 +7153,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6897
7153
|
return p.type === NodeTypes.DIRECTIVE && p.name === "bind" && p.arg?.type === NodeTypes.SIMPLE_EXPRESSION && p.arg.content === "handler" && !!p.exp;
|
|
6898
7154
|
}) ?? null;
|
|
6899
7155
|
const handlerInfo = handlerDirective ? nodeHandlerAttributeInfo(element) : null;
|
|
6900
|
-
if (
|
|
7156
|
+
if (nativeWrappers[element.tag]?.role === "button" && handlerDirective && !handlerInfo) {
|
|
6901
7157
|
const loc = element.loc?.start;
|
|
6902
7158
|
const locationHint = loc ? `${loc.line}:${loc.column}` : "unknown";
|
|
6903
7159
|
const handlerSource = (handlerDirective.exp?.loc?.source ?? "").trim() || "<unknown>";
|
|
@@ -6906,7 +7162,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
6906
7162
|
Element: <${element.tag}>
|
|
6907
7163
|
Handler: ${handlerSource}
|
|
6908
7164
|
|
|
6909
|
-
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
|
|
7165
|
+
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.`
|
|
6910
7166
|
);
|
|
6911
7167
|
}
|
|
6912
7168
|
if (nativeWrappersValue) {
|
|
@@ -7033,20 +7289,20 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
|
|
|
7033
7289
|
contextFilename: context.filename
|
|
7034
7290
|
});
|
|
7035
7291
|
const clickHint = trimLeadingSeparators(clickSuffix) || void 0;
|
|
7036
|
-
const idOrName =
|
|
7292
|
+
const idOrName = getStaticIdOrNameHint(element) || void 0;
|
|
7037
7293
|
const semanticHintCandidates = [clickHint, idOrName, innerText, conditionalHint].map((value) => (value ?? "").trim()).filter(Boolean).filter((value, index, values) => values.indexOf(value) === index);
|
|
7038
7294
|
const [semanticNameHint2, ...semanticNameHintAlternates] = semanticHintCandidates;
|
|
7039
7295
|
const pomMergeKey = clickHint ? `click:hint:${clickHint}` : void 0;
|
|
7040
7296
|
const testId = getClickDataTestId(clickSuffix);
|
|
7041
|
-
|
|
7297
|
+
const runtimeTestId = getClickRuntimeDataTestId(clickSuffix);
|
|
7298
|
+
const resolvedDataTestId = applyResolvedDataTestIdForElement({
|
|
7042
7299
|
preferredGeneratedValue: testId,
|
|
7300
|
+
preferredRuntimeValue: runtimeTestId,
|
|
7043
7301
|
semanticNameHint: semanticNameHint2,
|
|
7044
7302
|
semanticNameHintAlternates,
|
|
7045
7303
|
pomMergeKey
|
|
7046
7304
|
});
|
|
7047
|
-
|
|
7048
|
-
tryWrapClickDirectiveForTestEvents(element, testIdAttribute);
|
|
7049
|
-
}
|
|
7305
|
+
tryWrapClickDirectiveForTestEvents(element, testIdAttribute, resolvedDataTestId.runtimeValue);
|
|
7050
7306
|
return;
|
|
7051
7307
|
}
|
|
7052
7308
|
const existingElementDataTestId = tryGetExistingElementDataTestId(element, testIdAttribute);
|
|
@@ -7056,7 +7312,7 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
|
|
|
7056
7312
|
if (!isRecognizedInteractiveRole) {
|
|
7057
7313
|
return;
|
|
7058
7314
|
}
|
|
7059
|
-
const identifierHint =
|
|
7315
|
+
const identifierHint = getStaticIdOrNameHint(element) || nodeHandlerAttributeValue(element) || innerText || existingElementDataTestId.value || conditionalHint || void 0;
|
|
7060
7316
|
const preferredGeneratedValue = existingElementDataTestId.isDynamic ? templateAttributeValue(existingElementDataTestId.template) : staticAttributeValue(existingElementDataTestId.value);
|
|
7061
7317
|
applyResolvedDataTestIdForElement({
|
|
7062
7318
|
preferredGeneratedValue,
|
|
@@ -7066,7 +7322,7 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
|
|
|
7066
7322
|
}
|
|
7067
7323
|
const isSubmit = element.props.find((p) => p.type === NodeTypes.ATTRIBUTE && p.name === "type")?.value?.content === "submit";
|
|
7068
7324
|
if (isSubmit) {
|
|
7069
|
-
const identifier =
|
|
7325
|
+
const identifier = getStaticIdOrNameHint(element) || innerText;
|
|
7070
7326
|
if (!identifier) {
|
|
7071
7327
|
const loc = element.loc?.start;
|
|
7072
7328
|
const locationHint = loc ? `${loc.line}:${loc.column}` : "unknown";
|
|
@@ -7118,29 +7374,32 @@ function createBuildProcessorPlugin(options) {
|
|
|
7118
7374
|
getSourceDirs,
|
|
7119
7375
|
basePageClassPath,
|
|
7120
7376
|
normalizedBasePagePath,
|
|
7377
|
+
generation,
|
|
7378
|
+
projectRootRef,
|
|
7379
|
+
nativeWrappers,
|
|
7380
|
+
excludedComponents,
|
|
7381
|
+
getWrapperSearchRoots,
|
|
7382
|
+
getResolvedRouterEntry,
|
|
7383
|
+
loggerRef
|
|
7384
|
+
} = options;
|
|
7385
|
+
const {
|
|
7121
7386
|
outDir,
|
|
7122
7387
|
emitLanguages,
|
|
7123
7388
|
typescriptOutputStructure,
|
|
7124
7389
|
csharp,
|
|
7125
7390
|
generateFixtures,
|
|
7126
7391
|
customPomAttachments,
|
|
7127
|
-
projectRootRef,
|
|
7128
7392
|
customPomDir,
|
|
7393
|
+
requireCustomPomDir,
|
|
7129
7394
|
customPomImportAliases,
|
|
7130
7395
|
customPomImportNameCollisionBehavior,
|
|
7131
7396
|
testIdAttribute,
|
|
7132
7397
|
nameCollisionBehavior,
|
|
7133
|
-
missingSemanticNameBehavior = "error",
|
|
7134
7398
|
existingIdBehavior,
|
|
7135
|
-
nativeWrappers,
|
|
7136
|
-
excludedComponents,
|
|
7137
|
-
getWrapperSearchRoots,
|
|
7138
7399
|
routerAwarePoms,
|
|
7139
|
-
getResolvedRouterEntry,
|
|
7140
7400
|
routerType,
|
|
7141
|
-
routerModuleShims
|
|
7142
|
-
|
|
7143
|
-
} = options;
|
|
7401
|
+
routerModuleShims
|
|
7402
|
+
} = generation;
|
|
7144
7403
|
let lastGeneratedMetrics = {
|
|
7145
7404
|
entryCount: 0,
|
|
7146
7405
|
interactiveComponentCount: 0,
|
|
@@ -7239,10 +7498,9 @@ function createBuildProcessorPlugin(options) {
|
|
|
7239
7498
|
excludedComponents,
|
|
7240
7499
|
getViewsDirAbs(),
|
|
7241
7500
|
{
|
|
7242
|
-
existingIdBehavior: existingIdBehavior ?? "
|
|
7501
|
+
existingIdBehavior: existingIdBehavior ?? "error",
|
|
7243
7502
|
testIdAttribute,
|
|
7244
7503
|
nameCollisionBehavior,
|
|
7245
|
-
missingSemanticNameBehavior,
|
|
7246
7504
|
warn: (message) => loggerRef.current.warn(message),
|
|
7247
7505
|
vueFilesPathMap,
|
|
7248
7506
|
wrapperSearchRoots: getWrapperSearchRoots()
|
|
@@ -7342,6 +7600,7 @@ function createBuildProcessorPlugin(options) {
|
|
|
7342
7600
|
customPomAttachments,
|
|
7343
7601
|
projectRoot: projectRootRef.current,
|
|
7344
7602
|
customPomDir,
|
|
7603
|
+
requireCustomPomDir,
|
|
7345
7604
|
customPomImportAliases,
|
|
7346
7605
|
customPomImportNameCollisionBehavior,
|
|
7347
7606
|
testIdAttribute,
|
|
@@ -7373,6 +7632,11 @@ function createDevProcessorPlugin(options) {
|
|
|
7373
7632
|
projectRootRef,
|
|
7374
7633
|
normalizedBasePagePath,
|
|
7375
7634
|
basePageClassPath,
|
|
7635
|
+
generation,
|
|
7636
|
+
getResolvedRouterEntry,
|
|
7637
|
+
loggerRef
|
|
7638
|
+
} = options;
|
|
7639
|
+
const {
|
|
7376
7640
|
outDir,
|
|
7377
7641
|
emitLanguages,
|
|
7378
7642
|
typescriptOutputStructure,
|
|
@@ -7380,18 +7644,16 @@ function createDevProcessorPlugin(options) {
|
|
|
7380
7644
|
generateFixtures,
|
|
7381
7645
|
customPomAttachments,
|
|
7382
7646
|
customPomDir,
|
|
7647
|
+
requireCustomPomDir,
|
|
7383
7648
|
customPomImportAliases,
|
|
7384
7649
|
customPomImportNameCollisionBehavior,
|
|
7385
|
-
nameCollisionBehavior
|
|
7386
|
-
missingSemanticNameBehavior = "error",
|
|
7650
|
+
nameCollisionBehavior,
|
|
7387
7651
|
existingIdBehavior,
|
|
7388
7652
|
testIdAttribute,
|
|
7389
7653
|
routerAwarePoms,
|
|
7390
|
-
getResolvedRouterEntry,
|
|
7391
7654
|
routerType,
|
|
7392
|
-
routerModuleShims
|
|
7393
|
-
|
|
7394
|
-
} = options;
|
|
7655
|
+
routerModuleShims
|
|
7656
|
+
} = generation;
|
|
7395
7657
|
let scheduleVueFileRegen = null;
|
|
7396
7658
|
const getProjectRootCandidates = () => Array.from(/* @__PURE__ */ new Set([
|
|
7397
7659
|
path.resolve(projectRootRef.current),
|
|
@@ -7578,9 +7840,8 @@ function createDevProcessorPlugin(options) {
|
|
|
7578
7840
|
excludedComponents,
|
|
7579
7841
|
getViewsDirAbs(),
|
|
7580
7842
|
{
|
|
7581
|
-
existingIdBehavior: existingIdBehavior ?? "
|
|
7843
|
+
existingIdBehavior: existingIdBehavior ?? "error",
|
|
7582
7844
|
nameCollisionBehavior,
|
|
7583
|
-
missingSemanticNameBehavior,
|
|
7584
7845
|
testIdAttribute,
|
|
7585
7846
|
warn: (message) => loggerRef.current.warn(message),
|
|
7586
7847
|
vueFilesPathMap: provisionalVuePathMap,
|
|
@@ -7631,6 +7892,7 @@ function createDevProcessorPlugin(options) {
|
|
|
7631
7892
|
customPomAttachments,
|
|
7632
7893
|
projectRoot: projectRootRef.current,
|
|
7633
7894
|
customPomDir,
|
|
7895
|
+
requireCustomPomDir,
|
|
7634
7896
|
customPomImportAliases,
|
|
7635
7897
|
customPomImportNameCollisionBehavior,
|
|
7636
7898
|
pageDirs: getPageDirs(),
|
|
@@ -7866,27 +8128,30 @@ function createSupportPlugins(options) {
|
|
|
7866
8128
|
getViewsDir,
|
|
7867
8129
|
getSourceDirs,
|
|
7868
8130
|
getWrapperSearchRoots,
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
8131
|
+
generation,
|
|
8132
|
+
projectRootRef,
|
|
8133
|
+
basePageClassPath: basePageClassPathOverride,
|
|
8134
|
+
loggerRef
|
|
8135
|
+
} = options;
|
|
8136
|
+
const {
|
|
7872
8137
|
outDir,
|
|
7873
8138
|
emitLanguages,
|
|
7874
8139
|
typescriptOutputStructure,
|
|
7875
8140
|
csharp,
|
|
7876
|
-
routerAwarePoms,
|
|
7877
|
-
routerEntry,
|
|
7878
|
-
routerType,
|
|
7879
|
-
routerModuleShims,
|
|
7880
8141
|
generateFixtures,
|
|
7881
8142
|
customPomAttachments,
|
|
7882
|
-
projectRootRef,
|
|
7883
|
-
basePageClassPath: basePageClassPathOverride,
|
|
7884
8143
|
customPomDir,
|
|
8144
|
+
requireCustomPomDir,
|
|
7885
8145
|
customPomImportAliases,
|
|
7886
8146
|
customPomImportNameCollisionBehavior,
|
|
8147
|
+
nameCollisionBehavior,
|
|
8148
|
+
existingIdBehavior,
|
|
7887
8149
|
testIdAttribute,
|
|
7888
|
-
|
|
7889
|
-
|
|
8150
|
+
routerAwarePoms,
|
|
8151
|
+
routerEntry,
|
|
8152
|
+
routerType,
|
|
8153
|
+
routerModuleShims
|
|
8154
|
+
} = generation;
|
|
7890
8155
|
const resolveRouterEntry2 = () => {
|
|
7891
8156
|
if (!routerAwarePoms)
|
|
7892
8157
|
return void 0;
|
|
@@ -7915,27 +8180,12 @@ function createSupportPlugins(options) {
|
|
|
7915
8180
|
getSourceDirs,
|
|
7916
8181
|
basePageClassPath,
|
|
7917
8182
|
normalizedBasePagePath,
|
|
7918
|
-
|
|
7919
|
-
emitLanguages,
|
|
7920
|
-
typescriptOutputStructure,
|
|
7921
|
-
csharp,
|
|
7922
|
-
generateFixtures,
|
|
7923
|
-
customPomAttachments,
|
|
8183
|
+
generation,
|
|
7924
8184
|
projectRootRef,
|
|
7925
|
-
customPomDir,
|
|
7926
|
-
customPomImportAliases,
|
|
7927
|
-
customPomImportNameCollisionBehavior,
|
|
7928
|
-
testIdAttribute,
|
|
7929
|
-
nameCollisionBehavior,
|
|
7930
|
-
missingSemanticNameBehavior,
|
|
7931
|
-
existingIdBehavior,
|
|
7932
8185
|
nativeWrappers,
|
|
7933
8186
|
excludedComponents,
|
|
7934
8187
|
getWrapperSearchRoots,
|
|
7935
|
-
routerAwarePoms,
|
|
7936
|
-
routerType,
|
|
7937
8188
|
getResolvedRouterEntry: resolveRouterEntry2,
|
|
7938
|
-
routerModuleShims,
|
|
7939
8189
|
loggerRef
|
|
7940
8190
|
});
|
|
7941
8191
|
const devProcessor = createDevProcessorPlugin({
|
|
@@ -7950,23 +8200,8 @@ function createSupportPlugins(options) {
|
|
|
7950
8200
|
projectRootRef,
|
|
7951
8201
|
normalizedBasePagePath,
|
|
7952
8202
|
basePageClassPath,
|
|
7953
|
-
|
|
7954
|
-
emitLanguages,
|
|
7955
|
-
typescriptOutputStructure,
|
|
7956
|
-
csharp,
|
|
7957
|
-
generateFixtures,
|
|
7958
|
-
customPomAttachments,
|
|
7959
|
-
customPomDir,
|
|
7960
|
-
customPomImportAliases,
|
|
7961
|
-
customPomImportNameCollisionBehavior,
|
|
7962
|
-
nameCollisionBehavior,
|
|
7963
|
-
missingSemanticNameBehavior,
|
|
7964
|
-
existingIdBehavior,
|
|
7965
|
-
testIdAttribute,
|
|
7966
|
-
routerAwarePoms,
|
|
7967
|
-
routerType,
|
|
8203
|
+
generation,
|
|
7968
8204
|
getResolvedRouterEntry: resolveRouterEntry2,
|
|
7969
|
-
routerModuleShims,
|
|
7970
8205
|
loggerRef
|
|
7971
8206
|
});
|
|
7972
8207
|
const virtualModules = createTestIdsVirtualModulesPlugin(componentTestIds);
|
|
@@ -8303,8 +8538,7 @@ function createVuePluginWithTestIds(options) {
|
|
|
8303
8538
|
});
|
|
8304
8539
|
const api = viteVuePlugin?.api;
|
|
8305
8540
|
if (!api) {
|
|
8306
|
-
|
|
8307
|
-
return;
|
|
8541
|
+
throw new Error("[vue-pom-generator] Nuxt bridge could not find vite:vue plugin to patch.");
|
|
8308
8542
|
}
|
|
8309
8543
|
const currentOptions = api.options ?? {};
|
|
8310
8544
|
const currentTemplate = currentOptions.template ?? {};
|
|
@@ -8370,33 +8604,6 @@ function assertOneOf(value, allowed, name) {
|
|
|
8370
8604
|
}
|
|
8371
8605
|
throw new TypeError(`${name} must be one of: ${allowed.join(", ")}.`);
|
|
8372
8606
|
}
|
|
8373
|
-
function assertErrorBehavior(value, name) {
|
|
8374
|
-
if (!value) {
|
|
8375
|
-
return;
|
|
8376
|
-
}
|
|
8377
|
-
if (value === "ignore" || value === "error") {
|
|
8378
|
-
return;
|
|
8379
|
-
}
|
|
8380
|
-
if (typeof value !== "object" || Array.isArray(value)) {
|
|
8381
|
-
throw new TypeError(`${name} must be "ignore", "error", or an object.`);
|
|
8382
|
-
}
|
|
8383
|
-
const supportedKeys = /* @__PURE__ */ new Set(["missingSemanticNameBehavior"]);
|
|
8384
|
-
for (const key of Object.keys(value)) {
|
|
8385
|
-
if (!supportedKeys.has(key)) {
|
|
8386
|
-
throw new TypeError(`${name} contains unsupported key "${key}".`);
|
|
8387
|
-
}
|
|
8388
|
-
}
|
|
8389
|
-
assertOneOf(value.missingSemanticNameBehavior, ["ignore", "error"], `${name}.missingSemanticNameBehavior`);
|
|
8390
|
-
}
|
|
8391
|
-
function resolveMissingSemanticNameBehavior(value) {
|
|
8392
|
-
if (!value) {
|
|
8393
|
-
return "error";
|
|
8394
|
-
}
|
|
8395
|
-
if (value === "ignore" || value === "error") {
|
|
8396
|
-
return value;
|
|
8397
|
-
}
|
|
8398
|
-
return value.missingSemanticNameBehavior ?? "error";
|
|
8399
|
-
}
|
|
8400
8607
|
function readPackageJson(projectRoot) {
|
|
8401
8608
|
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
8402
8609
|
if (!fs.existsSync(packageJsonPath)) {
|
|
@@ -8505,7 +8712,7 @@ function applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompile
|
|
|
8505
8712
|
'[vue-pom-generator] vuePluginOwnership="external" requires the resolved Vite Vue plugin, but none was found. Add vue() to your Vite plugins before spreading createVuePomGeneratorPlugins(...).'
|
|
8506
8713
|
);
|
|
8507
8714
|
}
|
|
8508
|
-
throw new Error("[vue-pom-generator] Nuxt
|
|
8715
|
+
throw new Error("[vue-pom-generator] Nuxt bridge could not find vite:vue plugin to patch.");
|
|
8509
8716
|
}
|
|
8510
8717
|
const currentOptions = viteVuePlugin.api.options ?? {};
|
|
8511
8718
|
const currentTemplate = currentOptions.template ?? {};
|
|
@@ -8562,18 +8769,23 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8562
8769
|
const vueGenerationOptions = generationOptions;
|
|
8563
8770
|
const verbosity = options.logging?.verbosity ?? "warn";
|
|
8564
8771
|
const vueOptions = options.vueOptions;
|
|
8565
|
-
const
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8772
|
+
const resolvedInjectionOptionsRef = {
|
|
8773
|
+
current: resolveInjectionSupportOptions({
|
|
8774
|
+
isNuxt,
|
|
8775
|
+
viewsDir: injection.viewsDir,
|
|
8776
|
+
componentDirs: injection.componentDirs,
|
|
8777
|
+
layoutDirs: injection.layoutDirs,
|
|
8778
|
+
wrapperSearchRoots: injection.wrapperSearchRoots,
|
|
8779
|
+
nativeWrappers: injection.nativeWrappers,
|
|
8780
|
+
excludedComponents: injection.excludeComponents,
|
|
8781
|
+
existingIdBehavior: injection.existingIdBehavior,
|
|
8782
|
+
testIdAttribute: injection.attribute
|
|
8783
|
+
})
|
|
8784
|
+
};
|
|
8785
|
+
const resolvedInjectionOptions = resolvedInjectionOptionsRef.current;
|
|
8786
|
+
const nativeWrappers = resolvedInjectionOptions.nativeWrappers;
|
|
8787
|
+
const excludedComponents = resolvedInjectionOptions.excludedComponents;
|
|
8788
|
+
const testIdAttribute = resolvedInjectionOptions.testIdAttribute;
|
|
8577
8789
|
const routerEntry = !isNuxt ? vueGenerationOptions?.router?.entry : void 0;
|
|
8578
8790
|
const routerType = isNuxt ? "nuxt" : vueGenerationOptions?.router?.type ?? "vue-router";
|
|
8579
8791
|
const routerModuleShims = !isNuxt ? vueGenerationOptions?.router?.moduleShims : void 0;
|
|
@@ -8582,27 +8794,41 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8582
8794
|
}
|
|
8583
8795
|
const vuePluginOwnership = isNuxt ? "external" : options.vuePluginOwnership ?? "internal";
|
|
8584
8796
|
const usesExternalVuePlugin = vuePluginOwnership === "external";
|
|
8585
|
-
const csharp = generationOptions?.csharp;
|
|
8586
|
-
const errorBehavior = options.errorBehavior;
|
|
8587
|
-
const missingSemanticNameBehavior = resolveMissingSemanticNameBehavior(errorBehavior);
|
|
8588
|
-
const typescriptOutputStructure = generationOptions?.playwright?.outputStructure ?? "aggregated";
|
|
8589
8797
|
const generateFixtures = generationOptions?.playwright?.fixtures;
|
|
8590
8798
|
const customPoms = generationOptions?.playwright?.customPoms;
|
|
8591
8799
|
const resolvedCustomPomAttachments = customPoms?.attachments ?? [];
|
|
8592
|
-
const resolvedCustomPomDir = customPoms?.dir ?? "tests/playwright/pom/custom";
|
|
8593
8800
|
const resolvedCustomPomImportAliases = customPoms?.importAliases;
|
|
8594
|
-
const
|
|
8801
|
+
const requireCustomPomDir = customPoms?.dir !== void 0 || resolvedCustomPomAttachments.length > 0 || Object.keys(resolvedCustomPomImportAliases ?? {}).length > 0;
|
|
8802
|
+
const resolvedGenerationOptions = resolveGenerationSupportOptions({
|
|
8803
|
+
outDir: generationOptions?.outDir,
|
|
8804
|
+
emitLanguages: generationOptions?.emit,
|
|
8805
|
+
typescriptOutputStructure: generationOptions?.playwright?.outputStructure,
|
|
8806
|
+
csharp: generationOptions?.csharp,
|
|
8807
|
+
generateFixtures,
|
|
8808
|
+
customPomAttachments: resolvedCustomPomAttachments,
|
|
8809
|
+
customPomDir: customPoms?.dir,
|
|
8810
|
+
requireCustomPomDir,
|
|
8811
|
+
customPomImportAliases: resolvedCustomPomImportAliases,
|
|
8812
|
+
customPomImportNameCollisionBehavior: customPoms?.importNameCollisionBehavior,
|
|
8813
|
+
nameCollisionBehavior: generationOptions?.nameCollisionBehavior,
|
|
8814
|
+
existingIdBehavior: resolvedInjectionOptions.existingIdBehavior,
|
|
8815
|
+
testIdAttribute,
|
|
8816
|
+
routerAwarePoms: typeof routerEntry === "string" && routerEntry.trim().length > 0 || routerType === "nuxt",
|
|
8817
|
+
routerEntry,
|
|
8818
|
+
routerType,
|
|
8819
|
+
routerModuleShims
|
|
8820
|
+
});
|
|
8595
8821
|
const basePageClassPathOverride = generationOptions?.basePageClassPath;
|
|
8596
|
-
const getPageDirs = () =>
|
|
8822
|
+
const getPageDirs = () => resolvedInjectionOptionsRef.current.pageDirs;
|
|
8597
8823
|
const getViewsDir = () => getPageDirs()[0] ?? "src/views";
|
|
8598
|
-
const getComponentDirs = () =>
|
|
8599
|
-
const getLayoutDirs = () =>
|
|
8824
|
+
const getComponentDirs = () => resolvedInjectionOptionsRef.current.componentDirs;
|
|
8825
|
+
const getLayoutDirs = () => resolvedInjectionOptionsRef.current.layoutDirs;
|
|
8600
8826
|
const getSourceDirs = () => Array.from(/* @__PURE__ */ new Set([
|
|
8601
8827
|
...getPageDirs(),
|
|
8602
8828
|
...getComponentDirs(),
|
|
8603
8829
|
...getLayoutDirs()
|
|
8604
8830
|
]));
|
|
8605
|
-
const getWrapperSearchRoots = () =>
|
|
8831
|
+
const getWrapperSearchRoots = () => resolvedInjectionOptionsRef.current.wrapperSearchRoots;
|
|
8606
8832
|
const sharedStateKey = JSON.stringify({
|
|
8607
8833
|
cwd: process.cwd(),
|
|
8608
8834
|
mode: isNuxt ? "nuxt" : "vue",
|
|
@@ -8610,9 +8836,9 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8610
8836
|
componentDirs: isNuxt ? null : getComponentDirs(),
|
|
8611
8837
|
layoutDirs: isNuxt ? null : getLayoutDirs(),
|
|
8612
8838
|
wrapperSearchRoots: isNuxt ? null : getWrapperSearchRoots(),
|
|
8613
|
-
outDir,
|
|
8839
|
+
outDir: resolvedGenerationOptions.outDir,
|
|
8614
8840
|
testIdAttribute,
|
|
8615
|
-
routerType,
|
|
8841
|
+
routerType: resolvedGenerationOptions.routerType,
|
|
8616
8842
|
vuePluginOwnership
|
|
8617
8843
|
});
|
|
8618
8844
|
const sharedState = getSharedGeneratorState(sharedStateKey);
|
|
@@ -8633,23 +8859,22 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8633
8859
|
if (isNuxt) {
|
|
8634
8860
|
const nuxtDiscovery = await loadNuxtProjectDiscovery(process.cwd());
|
|
8635
8861
|
projectRootRef.current = nuxtDiscovery.rootDir;
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8862
|
+
resolvedInjectionOptionsRef.current = applyNuxtDiscoveryToInjectionOptions(
|
|
8863
|
+
resolvedInjectionOptionsRef.current,
|
|
8864
|
+
nuxtDiscovery
|
|
8865
|
+
);
|
|
8640
8866
|
}
|
|
8641
8867
|
assertNonEmptyString(testIdAttribute, "[vue-pom-generator] injection.attribute");
|
|
8642
8868
|
assertNonEmptyString(getViewsDir(), "[vue-pom-generator] injection.viewsDir");
|
|
8643
8869
|
assertNonEmptyStringArray(getComponentDirs(), "[vue-pom-generator] injection.componentDirs");
|
|
8644
8870
|
assertNonEmptyStringArray(getLayoutDirs(), "[vue-pom-generator] injection.layoutDirs");
|
|
8645
8871
|
assertNonEmptyStringArray(getWrapperSearchRoots(), "[vue-pom-generator] injection.wrapperSearchRoots");
|
|
8646
|
-
assertErrorBehavior(errorBehavior, "[vue-pom-generator] errorBehavior");
|
|
8647
8872
|
if (generationEnabled) {
|
|
8648
|
-
assertNonEmptyString(outDir, "[vue-pom-generator] generation.outDir");
|
|
8649
|
-
assertOneOf(typescriptOutputStructure, ["aggregated", "split"], "[vue-pom-generator] generation.playwright.outputStructure");
|
|
8650
|
-
assertRouterModuleShims(routerModuleShims, "[vue-pom-generator] generation.router.moduleShims");
|
|
8651
|
-
if (!isNuxt && vueGenerationOptions?.router && routerType === "vue-router") {
|
|
8652
|
-
assertNonEmptyString(routerEntry, "[vue-pom-generator] generation.router.entry");
|
|
8873
|
+
assertNonEmptyString(resolvedGenerationOptions.outDir, "[vue-pom-generator] generation.outDir");
|
|
8874
|
+
assertOneOf(resolvedGenerationOptions.typescriptOutputStructure, ["aggregated", "split"], "[vue-pom-generator] generation.playwright.outputStructure");
|
|
8875
|
+
assertRouterModuleShims(resolvedGenerationOptions.routerModuleShims, "[vue-pom-generator] generation.router.moduleShims");
|
|
8876
|
+
if (!isNuxt && vueGenerationOptions?.router && resolvedGenerationOptions.routerType === "vue-router") {
|
|
8877
|
+
assertNonEmptyString(resolvedGenerationOptions.routerEntry, "[vue-pom-generator] generation.router.entry");
|
|
8653
8878
|
}
|
|
8654
8879
|
}
|
|
8655
8880
|
if (usesExternalVuePlugin) {
|
|
@@ -8671,8 +8896,8 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8671
8896
|
const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
|
|
8672
8897
|
const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
|
|
8673
8898
|
vueOptions,
|
|
8674
|
-
existingIdBehavior,
|
|
8675
|
-
nameCollisionBehavior,
|
|
8899
|
+
existingIdBehavior: resolvedGenerationOptions.existingIdBehavior,
|
|
8900
|
+
nameCollisionBehavior: resolvedGenerationOptions.nameCollisionBehavior,
|
|
8676
8901
|
nativeWrappers,
|
|
8677
8902
|
elementMetadata,
|
|
8678
8903
|
semanticNameMap,
|
|
@@ -8687,7 +8912,6 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8687
8912
|
getProjectRoot: () => projectRootRef.current
|
|
8688
8913
|
});
|
|
8689
8914
|
templateCompilerOptionsForResolvedPlugin = templateCompilerOptions;
|
|
8690
|
-
const routerAwarePoms = typeof routerEntry === "string" && routerEntry.trim().length > 0 || routerType === "nuxt";
|
|
8691
8915
|
const supportPlugins = createSupportPlugins({
|
|
8692
8916
|
componentTestIds,
|
|
8693
8917
|
componentHierarchyMap,
|
|
@@ -8700,26 +8924,10 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
8700
8924
|
getViewsDir,
|
|
8701
8925
|
getSourceDirs,
|
|
8702
8926
|
getWrapperSearchRoots: getWrapperSearchRootsAbs,
|
|
8703
|
-
|
|
8704
|
-
missingSemanticNameBehavior,
|
|
8705
|
-
existingIdBehavior,
|
|
8706
|
-
outDir,
|
|
8707
|
-
emitLanguages,
|
|
8708
|
-
typescriptOutputStructure,
|
|
8709
|
-
csharp,
|
|
8710
|
-
routerAwarePoms,
|
|
8711
|
-
routerEntry,
|
|
8712
|
-
generateFixtures,
|
|
8927
|
+
generation: resolvedGenerationOptions,
|
|
8713
8928
|
projectRootRef,
|
|
8714
8929
|
basePageClassPath: basePageClassPathOverride,
|
|
8715
|
-
|
|
8716
|
-
customPomDir: resolvedCustomPomDir,
|
|
8717
|
-
customPomImportAliases: resolvedCustomPomImportAliases,
|
|
8718
|
-
customPomImportNameCollisionBehavior: resolvedCustomPomImportCollisionBehavior,
|
|
8719
|
-
testIdAttribute,
|
|
8720
|
-
loggerRef,
|
|
8721
|
-
routerType,
|
|
8722
|
-
routerModuleShims
|
|
8930
|
+
loggerRef
|
|
8723
8931
|
});
|
|
8724
8932
|
if (isNuxt) {
|
|
8725
8933
|
loggerRef.current.info("Nuxt environment detected. Skipping internal @vitejs/plugin-vue to avoid conflicts.");
|