@immense/vue-pom-generator 1.0.3
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/AGENTS.md +37 -0
- package/README.md +171 -0
- package/class-generation/BasePage.ts +569 -0
- package/class-generation/Pointer.ts +124 -0
- package/class-generation/index.ts +1691 -0
- package/dist/index.cjs +5163 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +5124 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +76 -0
- package/sequence-diagram.md +200 -0
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@immense/vue-pom-generator",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Injects data-testid attributes for all interactive elements and generates page object models for every page.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/immense/vue-pom-generator.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/immense/vue-pom-generator/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/immense/vue-pom-generator#readme",
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public",
|
|
16
|
+
"registry": "https://registry.npmjs.org"
|
|
17
|
+
},
|
|
18
|
+
"main": "./dist/index.cjs",
|
|
19
|
+
"module": "./dist/index.mjs",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.mjs",
|
|
25
|
+
"require": "./dist/index.cjs"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"class-generation",
|
|
31
|
+
"*.md"
|
|
32
|
+
],
|
|
33
|
+
"sideEffects": false,
|
|
34
|
+
"scripts": {
|
|
35
|
+
"clean": "rm -rf dist",
|
|
36
|
+
"build": "vite build",
|
|
37
|
+
"knip": "knip",
|
|
38
|
+
"lint": "eslint .",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"test:coverage": "vitest run --coverage",
|
|
41
|
+
"smoke:pack": "node ./scripts/packed-smoke-check.mjs",
|
|
42
|
+
"typecheck": "node -e \"process.argv = ['node','tsc',...process.argv.slice(1)]; require('typescript/bin/tsc')\" -- -p tsconfig.json --noEmit",
|
|
43
|
+
"prepublishOnly": "npm run build"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@vitejs/plugin-vue": ">=5",
|
|
47
|
+
"vite": ">=7",
|
|
48
|
+
"vitest": "^4",
|
|
49
|
+
"vue": ">=3"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@babel/parser": "^7.28.5",
|
|
53
|
+
"@babel/types": "^7.28.5",
|
|
54
|
+
"@vue/compiler-core": "3.5.22",
|
|
55
|
+
"@vue/compiler-dom": "3.5.22",
|
|
56
|
+
"@vue/compiler-sfc": "3.5.22",
|
|
57
|
+
"@vue/shared": "3.5.22",
|
|
58
|
+
"jsdom": "^27.4.0",
|
|
59
|
+
"vite-plugin-virtual": "^0.5.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@antfu/eslint-config": "5.3.0",
|
|
63
|
+
"@github/copilot-sdk": "^0.1.8",
|
|
64
|
+
"@types/jsdom": "^27.0.0",
|
|
65
|
+
"@types/node": "^24.1.0",
|
|
66
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
67
|
+
"@playwright/test": "^1.57.0",
|
|
68
|
+
"eslint": "^9.27.0",
|
|
69
|
+
"knip": "^5.82.1",
|
|
70
|
+
"typescript": "^5.9.2",
|
|
71
|
+
"vue-router": "^4.2.5",
|
|
72
|
+
"vitest": "^4.0.16",
|
|
73
|
+
"vite": "^7.3.1"
|
|
74
|
+
},
|
|
75
|
+
"license": "MIT"
|
|
76
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Vue Test ID Transform Sequence Diagram
|
|
2
|
+
|
|
3
|
+
This diagram illustrates the flow of the Vue compiler transform that automatically injects `data-testid` attributes into interactive elements during template compilation.
|
|
4
|
+
|
|
5
|
+
```mermaid
|
|
6
|
+
sequenceDiagram
|
|
7
|
+
participant Vite as Vite Plugin
|
|
8
|
+
participant Compiler as Vue Compiler
|
|
9
|
+
participant Transform as createTestIdTransform
|
|
10
|
+
participant Validator as nodeShouldBeIgnored
|
|
11
|
+
participant Generators as Test ID Generators
|
|
12
|
+
participant Element as ElementNode
|
|
13
|
+
|
|
14
|
+
Note over Vite,Compiler: Template Compilation Phase
|
|
15
|
+
Vite->>Compiler: Compile Vue template
|
|
16
|
+
Compiler->>Transform: Call NodeTransform for each node
|
|
17
|
+
|
|
18
|
+
Transform->>Element: Check node.type === ELEMENT?
|
|
19
|
+
alt Not an element node
|
|
20
|
+
Transform-->>Compiler: Skip (return)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Note over Transform,Validator: Element Validation Phase
|
|
24
|
+
Transform->>Transform: Get componentName from context
|
|
25
|
+
Transform->>Transform: addContainedComponent(fileName, tag, componentNames, componentChildrenMap)
|
|
26
|
+
Transform->>Transform: isTemplateWithData(node)?
|
|
27
|
+
alt Has template with slot scope
|
|
28
|
+
Transform->>Transform: Add to templateContainedComponentsSet
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Transform->>Validator: nodeShouldBeIgnored(element, tag, fileName, context)
|
|
32
|
+
|
|
33
|
+
Note over Validator: Check Multiple Conditions
|
|
34
|
+
Validator->>Validator: nodeHasClickDirective(node)?
|
|
35
|
+
Validator->>Validator: nodeHasToDirective(node)?
|
|
36
|
+
Validator->>Validator: nodeHasSubmitTypeOrHandlerAttribute(node)?
|
|
37
|
+
Validator->>Validator: nodeHasForDirective(node)?
|
|
38
|
+
Validator->>Validator: getInputValue(node, tag, fileName)?
|
|
39
|
+
Validator->>Validator: getRadioGroupDataTestId(node, tag, fileName, parent)?
|
|
40
|
+
Validator->>Validator: nodeHandlerAttributeValue(node)?
|
|
41
|
+
Validator->>Validator: getTabItemDataTestId(node, tag, fileName)?
|
|
42
|
+
|
|
43
|
+
alt None of the conditions match
|
|
44
|
+
Validator-->>Transform: return null (skip element)
|
|
45
|
+
Transform-->>Compiler: return (no test ID needed)
|
|
46
|
+
else At least one condition matches
|
|
47
|
+
Validator-->>Transform: return { hasClickDirective, toDirective, ... }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
Note over Transform,Generators: Test ID Generation Phase
|
|
51
|
+
|
|
52
|
+
alt tabItemDataTestId exists
|
|
53
|
+
Transform->>Transform: desiredTestId = tabItemDataTestId
|
|
54
|
+
else element.tag === "option"
|
|
55
|
+
Transform->>Generators: isOptionTagWithvalue(node, componentName)
|
|
56
|
+
Generators-->>Transform: return testId or empty
|
|
57
|
+
alt No value returned
|
|
58
|
+
Transform-->>Compiler: return (skip)
|
|
59
|
+
end
|
|
60
|
+
else handlerAttributeValue exists
|
|
61
|
+
Transform->>Transform: Check if in data template or v-for
|
|
62
|
+
alt In template/v-for with key
|
|
63
|
+
Transform->>Generators: insertBeforeLastUnderscore(handlerAttributeValue, "key")
|
|
64
|
+
Generators-->>Transform: return testId with key interpolation
|
|
65
|
+
else Simple handler
|
|
66
|
+
Transform->>Transform: desiredTestId = handlerAttributeValue
|
|
67
|
+
end
|
|
68
|
+
else vModelModelValue exists
|
|
69
|
+
Transform->>Generators: generateInputTestId(componentName, vModelModelValue, tag)
|
|
70
|
+
Generators-->>Transform: return testId
|
|
71
|
+
alt vModelModelValue.type === "checkbox"
|
|
72
|
+
Transform->>Transform: Add to idSet and componentTestIds
|
|
73
|
+
Transform-->>Compiler: return (handled by component)
|
|
74
|
+
end
|
|
75
|
+
else radioGroupDataTestId exists
|
|
76
|
+
Transform->>Transform: desiredTestId = radioGroupDataTestId
|
|
77
|
+
else toDirective exists && no object name value
|
|
78
|
+
Transform->>Transform: desiredTestId = fileName + placeholder + formatTagName
|
|
79
|
+
else Default case (click/submit/for directives)
|
|
80
|
+
Transform->>Transform: Determine keyAttributeValue
|
|
81
|
+
alt hasClickDirective
|
|
82
|
+
alt hasForDirective
|
|
83
|
+
Transform->>Generators: getSelfClosingForDirectiveKeyAttrValue(node)
|
|
84
|
+
Generators-->>Transform: return key value or null
|
|
85
|
+
else Not in for directive
|
|
86
|
+
Transform->>Generators: getContainedInVForDirectiveKeyValue(context)
|
|
87
|
+
Generators-->>Transform: return key value or null
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
alt No key found
|
|
92
|
+
Transform->>Generators: getKeyDirectiveValue(node)
|
|
93
|
+
Generators-->>Transform: return key placeholder or null
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
alt keyAttributeValue contains placeholder
|
|
97
|
+
Transform->>Transform: Generate simple testId with placeholder
|
|
98
|
+
else Generate complex testId
|
|
99
|
+
Transform->>Generators: generateTestId(node, context, toDirective, typeSubmit, key, componentName)
|
|
100
|
+
Note over Generators: generateTestId Process
|
|
101
|
+
Generators->>Generators: getIdOrName(node) - extract id/name
|
|
102
|
+
Generators->>Generators: getInnerText(node) - extract text content
|
|
103
|
+
Generators->>Generators: Compose testId based on directives
|
|
104
|
+
alt toDirective exists
|
|
105
|
+
Generators->>Generators: toDirectiveObjectFieldNameValue(toDirective)
|
|
106
|
+
Generators->>Generators: testId = componentName + toValue + innerText
|
|
107
|
+
else forDirective with key
|
|
108
|
+
Generators->>Generators: testId = componentName + key
|
|
109
|
+
Generators->>Generators: getComposedClickHandlerContent(node, context, innerText)
|
|
110
|
+
Generators->>Generators: Append click handler to testId
|
|
111
|
+
else Has identifier (id/name)
|
|
112
|
+
Generators->>Generators: testId = componentName + identifier
|
|
113
|
+
else typeSubmit/handler
|
|
114
|
+
Generators->>Generators: testId = componentName + identifier
|
|
115
|
+
else Default
|
|
116
|
+
Generators->>Generators: getComposedClickHandlerContent(node, context, innerText)
|
|
117
|
+
Generators->>Generators: testId = componentName + clickHandler
|
|
118
|
+
end
|
|
119
|
+
Generators->>Generators: formatTagName(node) - append tag suffix
|
|
120
|
+
Generators-->>Transform: return formatted testId
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
Note over Transform: Test ID Ready
|
|
125
|
+
Transform->>Transform: desiredTestId is now set
|
|
126
|
+
Transform->>Transform: addComponentTestIds(componentName, componentTestIds, desiredTestId)
|
|
127
|
+
Transform->>Element: addDataTestIdAttribute(element, desiredTestId, isDynamic)
|
|
128
|
+
|
|
129
|
+
Note over Element: Attribute Added
|
|
130
|
+
alt isDynamic
|
|
131
|
+
Element->>Element: Add :data-testid="`${testId}`" directive
|
|
132
|
+
else Static
|
|
133
|
+
Element->>Element: Add data-testid="testId" attribute
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
Transform-->>Compiler: return (transformation complete)
|
|
137
|
+
Compiler-->>Vite: Continue compilation
|
|
138
|
+
|
|
139
|
+
Note over Vite: Final Output
|
|
140
|
+
Vite->>Vite: Generate compiled template with data-testid attributes
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Key Components
|
|
144
|
+
|
|
145
|
+
### 1. **createTestIdTransform**
|
|
146
|
+
Main entry point that creates the NodeTransform function. Receives component context and tracking maps.
|
|
147
|
+
|
|
148
|
+
### 2. **nodeShouldBeIgnored**
|
|
149
|
+
Validates whether an element needs a test ID by checking:
|
|
150
|
+
- Click directives (`@click`)
|
|
151
|
+
- Router links (`:to`)
|
|
152
|
+
- Submit buttons (`type="submit"`)
|
|
153
|
+
- v-for loops
|
|
154
|
+
- Input elements with v-model
|
|
155
|
+
- Radio groups and selects
|
|
156
|
+
- Handler attributes (`:handler`)
|
|
157
|
+
- Tab items
|
|
158
|
+
|
|
159
|
+
### 3. **Test ID Generators**
|
|
160
|
+
Multiple specialized generators handle different element types:
|
|
161
|
+
- `generateTestId()` - Main generator for click/submit elements
|
|
162
|
+
- `generateInputTestId()` - For input/textarea/checkbox
|
|
163
|
+
- `getRadioGroupDataTestId()` - For radio groups and selects
|
|
164
|
+
- `getTabItemDataTestId()` - For tab items
|
|
165
|
+
- `isOptionTagWithvalue()` - For option elements
|
|
166
|
+
|
|
167
|
+
### 4. **Helper Functions**
|
|
168
|
+
- `getIdOrName()` - Extracts id/name attributes
|
|
169
|
+
- `getInnerText()` - Extracts text content from children
|
|
170
|
+
- `formatTagName()` - Formats tag suffix (e.g., "_btn")
|
|
171
|
+
- `getComposedClickHandlerContent()` - Analyzes @click handlers
|
|
172
|
+
- `toDirectiveObjectFieldNameValue()` - Extracts route names
|
|
173
|
+
- Key detection functions for v-for context
|
|
174
|
+
|
|
175
|
+
## Flow Summary
|
|
176
|
+
|
|
177
|
+
1. **Validation**: Check if element needs a test ID
|
|
178
|
+
2. **Context Analysis**: Determine element type and context (v-for, template scope, etc.)
|
|
179
|
+
3. **ID Generation**: Generate appropriate test ID based on element type and directives
|
|
180
|
+
4. **Attribute Injection**: Add static or dynamic data-testid attribute to element
|
|
181
|
+
5. **Tracking**: Update component test ID maps for build-time analysis
|
|
182
|
+
|
|
183
|
+
## Test ID Format Examples
|
|
184
|
+
|
|
185
|
+
- **Button with @click**: `ComponentName_HandlerName_btn`
|
|
186
|
+
- **Router link**: `ComponentName_RouteName_a`
|
|
187
|
+
- **Input with v-model**: `ComponentName_ModelName_input`
|
|
188
|
+
- **Element in v-for**: `ComponentName_${key}_tag`
|
|
189
|
+
- **Submit button**: `ComponentName_ButtonId_btn`
|
|
190
|
+
- **Radio group**: `ComponentName_ModelName_radio`
|
|
191
|
+
- **Tab item**: `ComponentName_${tabValue}_tabItem`
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Notes
|
|
195
|
+
|
|
196
|
+
- The transform runs during Vue template compilation, before the component is rendered
|
|
197
|
+
- Test IDs can be static (string literals) or dynamic (template literals with interpolation)
|
|
198
|
+
- Elements in v-for loops get dynamic test IDs with key interpolation when possible
|
|
199
|
+
- If a key cannot be determined in v-for, the sentinel `NEEDS_KEY` is used
|
|
200
|
+
- The transform tracks all generated test IDs in `componentTestIds` map for validation
|