@immense/vue-pom-generator 1.0.38 → 1.0.39
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 +953 -165
- package/RELEASE_NOTES.md +19 -30
- package/class-generation/index.ts +232 -29
- package/dist/class-generation/index.d.ts +8 -10
- package/dist/class-generation/index.d.ts.map +1 -1
- package/dist/index.cjs +161 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +161 -10
- package/dist/index.mjs.map +1 -1
- package/dist/plugin/support/build-plugin.d.ts +1 -0
- package/dist/plugin/support/build-plugin.d.ts.map +1 -1
- package/dist/plugin/support/dev-plugin.d.ts +1 -0
- package/dist/plugin/support/dev-plugin.d.ts.map +1 -1
- package/dist/plugin/support-plugins.d.ts +1 -0
- package/dist/plugin/support-plugins.d.ts.map +1 -1
- package/dist/plugin/types.d.ts +3 -1
- package/dist/plugin/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -26,10 +26,10 @@ const path = require("node:path");
|
|
|
26
26
|
const process = require("node:process");
|
|
27
27
|
const node_url = require("node:url");
|
|
28
28
|
const fs = require("node:fs");
|
|
29
|
+
const parser = require("@babel/parser");
|
|
29
30
|
const jsdom = require("jsdom");
|
|
30
31
|
const compilerCore = require("@vue/compiler-core");
|
|
31
32
|
const types = require("@babel/types");
|
|
32
|
-
const parser = require("@babel/parser");
|
|
33
33
|
const node_perf_hooks = require("node:perf_hooks");
|
|
34
34
|
const compilerDom = require("@vue/compiler-dom");
|
|
35
35
|
const compilerSfc = require("@vue/compiler-sfc");
|
|
@@ -4058,6 +4058,7 @@ function generateViewObjectModelContent(componentName, dependencies, componentHi
|
|
|
4058
4058
|
};
|
|
4059
4059
|
const customPomClassIdentifierMap = options.customPomClassIdentifierMap ?? {};
|
|
4060
4060
|
const customPomAvailableClassIdentifiers = options.customPomAvailableClassIdentifiers ?? /* @__PURE__ */ new Set();
|
|
4061
|
+
const customPomMethodSignaturesByClass = options.customPomMethodSignaturesByClass ?? /* @__PURE__ */ new Map();
|
|
4061
4062
|
const attachmentsForThisClass = customPomAttachments.filter((a) => {
|
|
4062
4063
|
if (!Object.prototype.hasOwnProperty.call(customPomClassIdentifierMap, a.className))
|
|
4063
4064
|
return false;
|
|
@@ -4068,7 +4069,9 @@ function generateViewObjectModelContent(componentName, dependencies, componentHi
|
|
|
4068
4069
|
return a.attachWhenUsesComponents.some((c) => hasChildComponent(c));
|
|
4069
4070
|
}).map((a) => ({
|
|
4070
4071
|
className: customPomClassIdentifierMap[a.className],
|
|
4071
|
-
propertyName: a.propertyName
|
|
4072
|
+
propertyName: a.propertyName,
|
|
4073
|
+
flatten: a.flatten ?? false,
|
|
4074
|
+
methodSignatures: a.flatten ? customPomMethodSignaturesByClass.get(a.className) ?? /* @__PURE__ */ new Map() : /* @__PURE__ */ new Map()
|
|
4072
4075
|
}));
|
|
4073
4076
|
let content = "";
|
|
4074
4077
|
const sourceRel = toPosixRelativePath(outputDir, filePath);
|
|
@@ -4109,6 +4112,15 @@ export class ${className} extends BasePage {
|
|
|
4109
4112
|
`;
|
|
4110
4113
|
const widgetInstances = isView ? getWidgetInstancesForView(componentName, dependencies.dataTestIdSet, customPomAvailableClassIdentifiers) : [];
|
|
4111
4114
|
const componentRefsForInstances = isView ? usedComponentSet?.size ? usedComponentSet : childrenComponentSet : childrenComponentSet;
|
|
4115
|
+
const childInstancePropertyNames = Array.from(componentRefsForInstances).filter((child) => componentHierarchyMap.has(child) && componentHierarchyMap.get(child)?.dataTestIdSet.size).map((child) => child.split(".vue")[0]);
|
|
4116
|
+
const blockedViewPassthroughMethodNames = new Set(
|
|
4117
|
+
attachmentsForThisClass.filter((a) => a.flatten).flatMap((a) => Array.from(a.methodSignatures.keys()))
|
|
4118
|
+
);
|
|
4119
|
+
const reservedAttachmentPassthroughNames = /* @__PURE__ */ new Set([
|
|
4120
|
+
...attachmentsForThisClass.map((a) => a.propertyName),
|
|
4121
|
+
...widgetInstances.map((w) => w.propertyName),
|
|
4122
|
+
...childInstancePropertyNames
|
|
4123
|
+
]);
|
|
4112
4124
|
if (isView && (componentRefsForInstances.size > 0 || attachmentsForThisClass.length > 0 || widgetInstances.length > 0)) {
|
|
4113
4125
|
content += getComponentInstances(componentRefsForInstances, componentHierarchyMap, attachmentsForThisClass, widgetInstances);
|
|
4114
4126
|
content += getConstructor(componentRefsForInstances, componentHierarchyMap, attachmentsForThisClass, widgetInstances, { testIdAttribute });
|
|
@@ -4117,8 +4129,9 @@ export class ${className} extends BasePage {
|
|
|
4117
4129
|
content += getComponentInstances(/* @__PURE__ */ new Set(), componentHierarchyMap, attachmentsForThisClass);
|
|
4118
4130
|
content += getConstructor(/* @__PURE__ */ new Set(), componentHierarchyMap, attachmentsForThisClass, [], { testIdAttribute });
|
|
4119
4131
|
}
|
|
4132
|
+
content += getAttachmentPassthroughMethods(componentName, dependencies, attachmentsForThisClass, reservedAttachmentPassthroughNames);
|
|
4120
4133
|
if (isView && componentRefsForInstances.size === 1) {
|
|
4121
|
-
content += getViewPassthroughMethods(componentName, dependencies, componentRefsForInstances, componentHierarchyMap);
|
|
4134
|
+
content += getViewPassthroughMethods(componentName, dependencies, componentRefsForInstances, componentHierarchyMap, blockedViewPassthroughMethodNames);
|
|
4122
4135
|
}
|
|
4123
4136
|
if (isView && options.vueRouterFluentChaining) {
|
|
4124
4137
|
const routeMeta = options.routeMetaByComponent?.[componentName] ?? null;
|
|
@@ -4129,7 +4142,7 @@ export class ${className} extends BasePage {
|
|
|
4129
4142
|
content += "}\n";
|
|
4130
4143
|
return content;
|
|
4131
4144
|
}
|
|
4132
|
-
function getViewPassthroughMethods(viewName, viewDependencies, childrenComponentSet, componentHierarchyMap) {
|
|
4145
|
+
function getViewPassthroughMethods(viewName, viewDependencies, childrenComponentSet, componentHierarchyMap, blockedMethodNames = /* @__PURE__ */ new Set()) {
|
|
4133
4146
|
const existingOnView = viewDependencies.generatedMethods ?? /* @__PURE__ */ new Map();
|
|
4134
4147
|
const methodToChildren = /* @__PURE__ */ new Map();
|
|
4135
4148
|
for (const child of childrenComponentSet) {
|
|
@@ -4143,7 +4156,7 @@ function getViewPassthroughMethods(viewName, viewDependencies, childrenComponent
|
|
|
4143
4156
|
for (const [name, sig] of methods.entries()) {
|
|
4144
4157
|
if (!sig)
|
|
4145
4158
|
continue;
|
|
4146
|
-
if (existingOnView.has(name))
|
|
4159
|
+
if (existingOnView.has(name) || blockedMethodNames.has(name))
|
|
4147
4160
|
continue;
|
|
4148
4161
|
const list = methodToChildren.get(name) ?? [];
|
|
4149
4162
|
list.push({ childProp, params: sig.params, argNames: sig.argNames });
|
|
@@ -4174,6 +4187,130 @@ function getViewPassthroughMethods(viewName, viewDependencies, childrenComponent
|
|
|
4174
4187
|
""
|
|
4175
4188
|
].join("\n");
|
|
4176
4189
|
}
|
|
4190
|
+
function getAttachmentPassthroughMethods(ownerName, ownerDependencies, attachmentsForThisClass, reservedMemberNames) {
|
|
4191
|
+
if (!attachmentsForThisClass.some((a) => a.flatten && a.methodSignatures.size > 0)) {
|
|
4192
|
+
return "";
|
|
4193
|
+
}
|
|
4194
|
+
const existingOnClass = ownerDependencies.generatedMethods ?? /* @__PURE__ */ new Map();
|
|
4195
|
+
const methodToAttachments = /* @__PURE__ */ new Map();
|
|
4196
|
+
for (const attachment of attachmentsForThisClass) {
|
|
4197
|
+
if (!attachment.flatten) {
|
|
4198
|
+
continue;
|
|
4199
|
+
}
|
|
4200
|
+
for (const [methodName, signature] of attachment.methodSignatures.entries()) {
|
|
4201
|
+
if (methodName === "constructor" || existingOnClass.has(methodName) || reservedMemberNames.has(methodName)) {
|
|
4202
|
+
continue;
|
|
4203
|
+
}
|
|
4204
|
+
const list = methodToAttachments.get(methodName) ?? [];
|
|
4205
|
+
list.push({
|
|
4206
|
+
propertyName: attachment.propertyName,
|
|
4207
|
+
params: signature.params,
|
|
4208
|
+
argNames: signature.argNames
|
|
4209
|
+
});
|
|
4210
|
+
methodToAttachments.set(methodName, list);
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
const sorted = Array.from(methodToAttachments.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
4214
|
+
const lines = [];
|
|
4215
|
+
for (const [methodName, candidates] of sorted) {
|
|
4216
|
+
if (candidates.length !== 1) {
|
|
4217
|
+
continue;
|
|
4218
|
+
}
|
|
4219
|
+
const { propertyName, params, argNames } = candidates[0];
|
|
4220
|
+
const callArgs = argNames.join(", ");
|
|
4221
|
+
const invocation = callArgs ? `this.${propertyName}.${methodName}(${callArgs})` : `this.${propertyName}.${methodName}()`;
|
|
4222
|
+
lines.push(
|
|
4223
|
+
"",
|
|
4224
|
+
` ${methodName}(${params}) {`,
|
|
4225
|
+
` return ${invocation};`,
|
|
4226
|
+
" }"
|
|
4227
|
+
);
|
|
4228
|
+
}
|
|
4229
|
+
if (!lines.length) {
|
|
4230
|
+
return "";
|
|
4231
|
+
}
|
|
4232
|
+
return [
|
|
4233
|
+
"",
|
|
4234
|
+
` // Passthrough methods composed from custom helper attachments of ${ownerName}.`,
|
|
4235
|
+
...lines,
|
|
4236
|
+
""
|
|
4237
|
+
].join("\n");
|
|
4238
|
+
}
|
|
4239
|
+
function sliceNodeSource(source, node) {
|
|
4240
|
+
if (node.start == null || node.end == null) {
|
|
4241
|
+
return null;
|
|
4242
|
+
}
|
|
4243
|
+
const snippet = source.slice(node.start, node.end).trim();
|
|
4244
|
+
return snippet.length ? snippet : null;
|
|
4245
|
+
}
|
|
4246
|
+
function getCustomPomCallArgumentName(param) {
|
|
4247
|
+
if (param.type === "Identifier") {
|
|
4248
|
+
return param.name;
|
|
4249
|
+
}
|
|
4250
|
+
if (param.type === "AssignmentPattern") {
|
|
4251
|
+
return param.left.type === "Identifier" ? param.left.name : null;
|
|
4252
|
+
}
|
|
4253
|
+
if (param.type === "RestElement") {
|
|
4254
|
+
return param.argument.type === "Identifier" ? `...${param.argument.name}` : null;
|
|
4255
|
+
}
|
|
4256
|
+
return null;
|
|
4257
|
+
}
|
|
4258
|
+
function extractCustomPomMethodSignatures(source, exportName) {
|
|
4259
|
+
const signatures = /* @__PURE__ */ new Map();
|
|
4260
|
+
let ast;
|
|
4261
|
+
try {
|
|
4262
|
+
ast = parser.parse(source, {
|
|
4263
|
+
sourceType: "module",
|
|
4264
|
+
plugins: ["typescript", "jsx"]
|
|
4265
|
+
});
|
|
4266
|
+
} catch {
|
|
4267
|
+
return signatures;
|
|
4268
|
+
}
|
|
4269
|
+
for (const statement of ast.program.body) {
|
|
4270
|
+
if (statement.type !== "ExportNamedDeclaration" || !statement.declaration || statement.declaration.type !== "ClassDeclaration") {
|
|
4271
|
+
continue;
|
|
4272
|
+
}
|
|
4273
|
+
const declaration = statement.declaration;
|
|
4274
|
+
if (declaration.id?.name !== exportName) {
|
|
4275
|
+
continue;
|
|
4276
|
+
}
|
|
4277
|
+
for (const member of declaration.body.body) {
|
|
4278
|
+
if (member.type !== "ClassMethod" || member.kind !== "method" || member.static || member.computed) {
|
|
4279
|
+
continue;
|
|
4280
|
+
}
|
|
4281
|
+
if (member.accessibility === "private" || member.accessibility === "protected") {
|
|
4282
|
+
continue;
|
|
4283
|
+
}
|
|
4284
|
+
if (member.key.type !== "Identifier") {
|
|
4285
|
+
continue;
|
|
4286
|
+
}
|
|
4287
|
+
const params = [];
|
|
4288
|
+
const argNames = [];
|
|
4289
|
+
let supported = true;
|
|
4290
|
+
member.params.forEach((param) => {
|
|
4291
|
+
if (!supported) {
|
|
4292
|
+
return;
|
|
4293
|
+
}
|
|
4294
|
+
const paramSource = sliceNodeSource(source, param);
|
|
4295
|
+
const argName = getCustomPomCallArgumentName(param);
|
|
4296
|
+
if (!paramSource || !argName) {
|
|
4297
|
+
supported = false;
|
|
4298
|
+
return;
|
|
4299
|
+
}
|
|
4300
|
+
params.push(paramSource);
|
|
4301
|
+
argNames.push(argName);
|
|
4302
|
+
});
|
|
4303
|
+
if (!supported) {
|
|
4304
|
+
continue;
|
|
4305
|
+
}
|
|
4306
|
+
signatures.set(member.key.name, {
|
|
4307
|
+
params: params.join(", "),
|
|
4308
|
+
argNames
|
|
4309
|
+
});
|
|
4310
|
+
}
|
|
4311
|
+
}
|
|
4312
|
+
return signatures;
|
|
4313
|
+
}
|
|
4177
4314
|
function ensureDir(dir) {
|
|
4178
4315
|
const normalized = dir.replace(/\\/g, "/");
|
|
4179
4316
|
if (!fs.existsSync(normalized)) {
|
|
@@ -4217,6 +4354,7 @@ async function generateAggregatedFiles(componentHierarchyMap, vueFilesPathMap, b
|
|
|
4217
4354
|
]);
|
|
4218
4355
|
const usedImportIdentifiers = /* @__PURE__ */ new Set();
|
|
4219
4356
|
const customPomClassIdentifierMap2 = {};
|
|
4357
|
+
const customPomMethodSignaturesByClass2 = /* @__PURE__ */ new Map();
|
|
4220
4358
|
const ensureUniqueIdentifier = (base2) => {
|
|
4221
4359
|
let candidate = base2;
|
|
4222
4360
|
let i = 2;
|
|
@@ -4230,7 +4368,10 @@ async function generateAggregatedFiles(componentHierarchyMap, vueFilesPathMap, b
|
|
|
4230
4368
|
const customDirRelOrAbs = options.customPomDir ?? "tests/playwright/pom/custom";
|
|
4231
4369
|
const customDirAbs = path.isAbsolute(customDirRelOrAbs) ? customDirRelOrAbs : path.resolve(projectRoot, customDirRelOrAbs);
|
|
4232
4370
|
if (!fs.existsSync(customDirAbs)) {
|
|
4233
|
-
return
|
|
4371
|
+
return {
|
|
4372
|
+
classIdentifierMap: customPomClassIdentifierMap2,
|
|
4373
|
+
methodSignaturesByClass: customPomMethodSignaturesByClass2
|
|
4374
|
+
};
|
|
4234
4375
|
}
|
|
4235
4376
|
const files = fs.readdirSync(customDirAbs).filter((f) => f.endsWith(".ts")).sort((a, b) => a.localeCompare(b));
|
|
4236
4377
|
for (const file of files) {
|
|
@@ -4252,8 +4393,12 @@ Fix by setting generation.playwright.customPoms.importAliases["${exportName}"] t
|
|
|
4252
4393
|
} else {
|
|
4253
4394
|
localIdentifier = ensureUniqueIdentifier(requested);
|
|
4254
4395
|
}
|
|
4255
|
-
customPomClassIdentifierMap2[exportName] = localIdentifier;
|
|
4256
4396
|
const customFileAbs = path.join(customDirAbs, file);
|
|
4397
|
+
customPomClassIdentifierMap2[exportName] = localIdentifier;
|
|
4398
|
+
const customPomMethodSignatures = extractCustomPomMethodSignatures(fs.readFileSync(customFileAbs, "utf8"), exportName);
|
|
4399
|
+
if (customPomMethodSignatures.size > 0) {
|
|
4400
|
+
customPomMethodSignaturesByClass2.set(exportName, customPomMethodSignatures);
|
|
4401
|
+
}
|
|
4257
4402
|
const fromOutputDir = outputDir;
|
|
4258
4403
|
const importPath = stripExtension(toPosixRelativePath(fromOutputDir, customFileAbs));
|
|
4259
4404
|
if (localIdentifier !== exportName) {
|
|
@@ -4262,10 +4407,15 @@ Fix by setting generation.playwright.customPoms.importAliases["${exportName}"] t
|
|
|
4262
4407
|
imports.push(`import { ${exportName} } from "${importPath}";`);
|
|
4263
4408
|
}
|
|
4264
4409
|
}
|
|
4265
|
-
return
|
|
4410
|
+
return {
|
|
4411
|
+
classIdentifierMap: customPomClassIdentifierMap2,
|
|
4412
|
+
methodSignaturesByClass: customPomMethodSignaturesByClass2
|
|
4413
|
+
};
|
|
4266
4414
|
};
|
|
4267
|
-
const
|
|
4268
|
-
const
|
|
4415
|
+
const customPomImportResolution = addCustomPomImports();
|
|
4416
|
+
const customPomClassIdentifierMap = customPomImportResolution?.classIdentifierMap ?? {};
|
|
4417
|
+
const customPomMethodSignaturesByClass = customPomImportResolution?.methodSignaturesByClass ?? /* @__PURE__ */ new Map();
|
|
4418
|
+
const customPomAvailableClassIdentifiers = new Set(Object.values(customPomClassIdentifierMap));
|
|
4269
4419
|
const referencedTargets = /* @__PURE__ */ new Set();
|
|
4270
4420
|
for (const [, deps] of items) {
|
|
4271
4421
|
for (const dt of deps.dataTestIdSet) {
|
|
@@ -4419,6 +4569,7 @@ Fix by setting generation.playwright.customPoms.importAliases["${exportName}"] t
|
|
|
4419
4569
|
customPomAttachments: options.customPomAttachments ?? [],
|
|
4420
4570
|
customPomClassIdentifierMap,
|
|
4421
4571
|
customPomAvailableClassIdentifiers,
|
|
4572
|
+
customPomMethodSignaturesByClass,
|
|
4422
4573
|
testIdAttribute: options.testIdAttribute,
|
|
4423
4574
|
vueRouterFluentChaining: options.vueRouterFluentChaining,
|
|
4424
4575
|
routeMetaByComponent: options.routeMetaByComponent
|