@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/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