create-appraisejs 0.3.1-alpha.1 → 0.4.0-alpha.0

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.
Files changed (133) hide show
  1. package/package.json +1 -1
  2. package/templates/blank/.env.example +2 -2
  3. package/templates/blank/.prettierrc +13 -13
  4. package/templates/blank/cucumber.mjs +12 -1
  5. package/templates/blank/e2e/helpers/test-data.ts +10 -0
  6. package/templates/blank/next-env.d.ts +6 -6
  7. package/templates/blank/package-lock.json +2 -2
  8. package/templates/blank/package.json +1 -1
  9. package/templates/blank/packages/cucumber-runtime/package.json +2 -1
  10. package/templates/blank/packages/cucumber-runtime/src/paths.ts +22 -5
  11. package/templates/blank/packages/locator-picker-companion/package.json +2 -1
  12. package/templates/blank/prisma/dev.db +0 -0
  13. package/templates/blank/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -16
  14. package/templates/blank/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -27
  15. package/templates/blank/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -17
  16. package/templates/blank/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -12
  17. package/templates/blank/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -28
  18. package/templates/blank/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -12
  19. package/templates/blank/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -10
  20. package/templates/blank/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -10
  21. package/templates/blank/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -108
  22. package/templates/blank/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -55
  23. package/templates/blank/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -27
  24. package/templates/blank/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -59
  25. package/templates/blank/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -34
  26. package/templates/blank/prisma/migrations/20260313093000_add_report_step_screenshot_path/migration.sql +1 -1
  27. package/templates/blank/scripts/setup-env.ts +0 -0
  28. package/templates/blank/scripts/sync-test-cases.ts +60 -10
  29. package/templates/blank/src/components/diagram/flow-diagram-node-search.tsx +9 -2
  30. package/templates/blank/src/components/diagram/flow-diagram-toolbar.tsx +37 -3
  31. package/templates/blank/src/components/diagram/flow-diagram.test.tsx +225 -0
  32. package/templates/blank/src/components/diagram/use-flow-diagram-search.ts +2 -0
  33. package/templates/blank/src/components/diagram/use-flow-diagram.ts +93 -0
  34. package/templates/blank/src/lib/appraise-test-case-metadata.test.ts +78 -0
  35. package/templates/blank/src/lib/appraise-test-case-metadata.ts +220 -0
  36. package/templates/blank/src/lib/automation/automation-path-roots.test.ts +14 -0
  37. package/templates/blank/src/lib/automation/automation-path-roots.ts +10 -2
  38. package/templates/blank/src/lib/database-sync.ts +166 -15
  39. package/templates/blank/src/lib/executor/local-executor-adapter.ts +6 -2
  40. package/templates/blank/src/lib/feature-file-generator.ts +54 -10
  41. package/templates/blank/src/lib/gherkin-parser.test.ts +52 -0
  42. package/templates/blank/src/lib/gherkin-parser.ts +39 -1
  43. package/templates/blank/src/lib/sync/projected-feature-utils.ts +5 -1
  44. package/templates/blank/src/lib/sync/sync-pending-counts.test.ts +115 -0
  45. package/templates/blank/src/lib/sync/sync-pending-counts.ts +108 -13
  46. package/templates/blank/src/services/test-run/test-run-service.test.ts +10 -0
  47. package/templates/blank/src/services/test-run/test-run-service.ts +41 -1
  48. package/templates/starter/.env.example +2 -2
  49. package/templates/starter/.prettierrc +13 -13
  50. package/templates/starter/cucumber.mjs +12 -1
  51. package/templates/starter/e2e/helpers/test-data.ts +10 -0
  52. package/templates/starter/next-env.d.ts +6 -6
  53. package/templates/starter/package-lock.json +2 -2
  54. package/templates/starter/package.json +1 -1
  55. package/templates/starter/packages/cucumber-runtime/package.json +2 -1
  56. package/templates/starter/packages/cucumber-runtime/src/paths.ts +22 -5
  57. package/templates/starter/packages/locator-picker-companion/package.json +2 -1
  58. package/templates/starter/prisma/dev.db +0 -0
  59. package/templates/starter/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -16
  60. package/templates/starter/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -27
  61. package/templates/starter/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -17
  62. package/templates/starter/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -12
  63. package/templates/starter/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -28
  64. package/templates/starter/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -12
  65. package/templates/starter/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -10
  66. package/templates/starter/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -10
  67. package/templates/starter/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -108
  68. package/templates/starter/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -55
  69. package/templates/starter/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -27
  70. package/templates/starter/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -59
  71. package/templates/starter/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -34
  72. package/templates/starter/prisma/migrations/20260313093000_add_report_step_screenshot_path/migration.sql +1 -1
  73. package/templates/starter/scripts/setup-env.ts +0 -0
  74. package/templates/starter/scripts/sync-test-cases.ts +60 -10
  75. package/templates/starter/src/components/diagram/flow-diagram-node-search.tsx +9 -2
  76. package/templates/starter/src/components/diagram/flow-diagram-toolbar.tsx +37 -3
  77. package/templates/starter/src/components/diagram/flow-diagram.test.tsx +225 -0
  78. package/templates/starter/src/components/diagram/use-flow-diagram-search.ts +2 -0
  79. package/templates/starter/src/components/diagram/use-flow-diagram.ts +93 -0
  80. package/templates/starter/src/lib/appraise-test-case-metadata.test.ts +78 -0
  81. package/templates/starter/src/lib/appraise-test-case-metadata.ts +220 -0
  82. package/templates/starter/src/lib/automation/automation-path-roots.test.ts +14 -0
  83. package/templates/starter/src/lib/automation/automation-path-roots.ts +10 -2
  84. package/templates/starter/src/lib/database-sync.ts +166 -15
  85. package/templates/starter/src/lib/executor/local-executor-adapter.ts +6 -2
  86. package/templates/starter/src/lib/feature-file-generator.ts +54 -10
  87. package/templates/starter/src/lib/gherkin-parser.test.ts +52 -0
  88. package/templates/starter/src/lib/gherkin-parser.ts +39 -1
  89. package/templates/starter/src/lib/sync/projected-feature-utils.ts +5 -1
  90. package/templates/starter/src/lib/sync/sync-pending-counts.test.ts +115 -0
  91. package/templates/starter/src/lib/sync/sync-pending-counts.ts +108 -13
  92. package/templates/starter/src/services/test-run/test-run-service.test.ts +10 -0
  93. package/templates/starter/src/services/test-run/test-run-service.ts +41 -1
  94. package/dist/cli.e2e.test.d.ts +0 -2
  95. package/dist/cli.e2e.test.d.ts.map +0 -1
  96. package/dist/cli.e2e.test.js +0 -75
  97. package/dist/cli.e2e.test.js.map +0 -1
  98. package/dist/config.test.d.ts +0 -2
  99. package/dist/config.test.d.ts.map +0 -1
  100. package/dist/config.test.js +0 -65
  101. package/dist/config.test.js.map +0 -1
  102. package/dist/copy-template.test.d.ts +0 -2
  103. package/dist/copy-template.test.d.ts.map +0 -1
  104. package/dist/copy-template.test.js +0 -71
  105. package/dist/copy-template.test.js.map +0 -1
  106. package/dist/download-repo.test.d.ts +0 -2
  107. package/dist/download-repo.test.d.ts.map +0 -1
  108. package/dist/download-repo.test.js +0 -16
  109. package/dist/download-repo.test.js.map +0 -1
  110. package/dist/install.test.d.ts +0 -2
  111. package/dist/install.test.d.ts.map +0 -1
  112. package/dist/install.test.js +0 -120
  113. package/dist/install.test.js.map +0 -1
  114. package/dist/prompts.test.d.ts +0 -2
  115. package/dist/prompts.test.d.ts.map +0 -1
  116. package/dist/prompts.test.js +0 -58
  117. package/dist/prompts.test.js.map +0 -1
  118. package/templates/default/next-env.d.ts +0 -6
  119. package/templates/default/packages/locator-picker-companion/dist/cli.d.ts +0 -1
  120. package/templates/default/packages/locator-picker-companion/dist/cli.js +0 -336
  121. package/templates/default/packages/locator-picker-companion/dist/index.d.ts +0 -3
  122. package/templates/default/packages/locator-picker-companion/dist/index.js +0 -3
  123. package/templates/default/packages/locator-picker-companion/dist/injected-picker-script.d.ts +0 -1
  124. package/templates/default/packages/locator-picker-companion/dist/injected-picker-script.js +0 -660
  125. package/templates/default/packages/locator-picker-companion/dist/launcher.d.ts +0 -14
  126. package/templates/default/packages/locator-picker-companion/dist/launcher.js +0 -58
  127. package/templates/default/packages/locator-picker-companion/dist/selector-generator.d.ts +0 -6
  128. package/templates/default/packages/locator-picker-companion/dist/selector-generator.js +0 -261
  129. package/templates/default/packages/locator-picker-companion/dist/session-file.d.ts +0 -30
  130. package/templates/default/packages/locator-picker-companion/dist/session-file.js +0 -162
  131. package/templates/default/packages/locator-picker-companion/dist/types.d.ts +0 -31
  132. package/templates/default/packages/locator-picker-companion/dist/types.js +0 -1
  133. package/templates/default/prisma/dev.db +0 -0
@@ -1,58 +0,0 @@
1
- import { access, readdir, stat } from 'fs/promises'
2
- import { execa } from 'execa'
3
- import path from 'path'
4
- async function pathExists(targetPath) {
5
- try {
6
- await access(targetPath)
7
- return true
8
- } catch (_a) {
9
- return false
10
- }
11
- }
12
- async function getLatestModifiedTime(targetPath) {
13
- const stats = await stat(targetPath)
14
- if (!stats.isDirectory()) {
15
- return stats.mtimeMs
16
- }
17
- const entries = await readdir(targetPath, { withFileTypes: true })
18
- const times = await Promise.all(entries.map(entry => getLatestModifiedTime(path.join(targetPath, entry.name))))
19
- return Math.max(stats.mtimeMs, ...times)
20
- }
21
- export function getLocatorPickerCompanionPaths(repoRoot = process.cwd()) {
22
- const packageRoot = path.join(repoRoot, 'packages', 'locator-picker-companion')
23
- return {
24
- packageRoot,
25
- sourceRoot: path.join(packageRoot, 'src'),
26
- distCliPath: path.join(packageRoot, 'dist', 'cli.js'),
27
- tsconfigPath: path.join(packageRoot, 'tsconfig.json'),
28
- tscCliPath: path.join(repoRoot, 'node_modules', 'typescript', 'bin', 'tsc'),
29
- }
30
- }
31
- async function ensureLocatorPickerCompanionBuilt(repoRoot = process.cwd()) {
32
- const { distCliPath, sourceRoot, tsconfigPath, tscCliPath } = getLocatorPickerCompanionPaths(repoRoot)
33
- if (await pathExists(distCliPath)) {
34
- const [distMtime, srcMtime] = await Promise.all([
35
- getLatestModifiedTime(distCliPath),
36
- getLatestModifiedTime(sourceRoot),
37
- ])
38
- if (distMtime >= srcMtime) {
39
- return distCliPath
40
- }
41
- }
42
- await execa(process.execPath, [tscCliPath, '-p', tsconfigPath], {
43
- cwd: repoRoot,
44
- env: process.env,
45
- stdio: 'pipe',
46
- })
47
- if (!(await pathExists(distCliPath))) {
48
- throw new Error('Locator picker companion build completed without producing dist/cli.js.')
49
- }
50
- return distCliPath
51
- }
52
- export async function resolveLocatorPickerCompanionInvocation(cliArgs, repoRoot = process.cwd()) {
53
- const distCliPath = await ensureLocatorPickerCompanionBuilt(repoRoot)
54
- return {
55
- command: process.execPath,
56
- args: [distCliPath, ...cliArgs],
57
- }
58
- }
@@ -1,6 +0,0 @@
1
- import type { ElementHandle, Page } from 'playwright'
2
- import type { CompanionPickedLocatorPayload } from './types.js'
3
- export declare function generatePickedLocatorPayload(
4
- page: Page,
5
- elementHandle: ElementHandle,
6
- ): Promise<CompanionPickedLocatorPayload>
@@ -1,261 +0,0 @@
1
- function normalizeText(value) {
2
- return (value !== null && value !== void 0 ? value : '').replace(/\s+/g, ' ').trim()
3
- }
4
- function normalizeRoute(value) {
5
- if (!value) {
6
- return '/'
7
- }
8
- try {
9
- return new URL(value).pathname || '/'
10
- } catch (_a) {
11
- return value.startsWith('/') ? value : `/${value}`
12
- }
13
- }
14
- function escapeForCss(value) {
15
- return value.replace(/[^a-zA-Z0-9_-]/g, match => `\\${match}`)
16
- }
17
- function escapeForSelectorText(value) {
18
- return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
19
- }
20
- function isLikelyStableIdentifier(value) {
21
- if (!value) {
22
- return false
23
- }
24
- const normalized = value.trim()
25
- if (!normalized || normalized.length > 120) {
26
- return false
27
- }
28
- return !/\d{4,}/.test(normalized) && !/[A-Fa-f0-9]{8,}/.test(normalized)
29
- }
30
- function buildCssSelector(snapshot) {
31
- const parts = [snapshot.tagName]
32
- if (isLikelyStableIdentifier(snapshot.nameAttribute)) {
33
- parts.push(`[name="${escapeForSelectorText(snapshot.nameAttribute)}"]`)
34
- }
35
- if (isLikelyStableIdentifier(snapshot.typeAttribute)) {
36
- parts.push(`[type="${escapeForSelectorText(snapshot.typeAttribute)}"]`)
37
- }
38
- if (isLikelyStableIdentifier(snapshot.ariaLabel)) {
39
- parts.push(`[aria-label="${escapeForSelectorText(snapshot.ariaLabel)}"]`)
40
- }
41
- if (parts.length === 1 && snapshot.stableClasses.length > 0) {
42
- parts.push(...snapshot.stableClasses.slice(0, 2).map(className => `.${escapeForCss(className)}`))
43
- }
44
- return parts.length > 1 ? `css=${parts.join('')}` : ''
45
- }
46
- function buildXPathSelector(snapshot) {
47
- if (snapshot.id) {
48
- return `xpath=//*[@id="${snapshot.id.replace(/"/g, '\\"')}"]`
49
- }
50
- if (snapshot.text) {
51
- return `xpath=//${snapshot.tagName}[normalize-space()="${snapshot.text.replace(/"/g, '\\"')}"]`
52
- }
53
- return `xpath=//${snapshot.tagName}`
54
- }
55
- function buildPrimarySelector(snapshot) {
56
- if (isLikelyStableIdentifier(snapshot.testAttributeValue)) {
57
- return {
58
- selector: `css=[${snapshot.testAttributeName}="${escapeForSelectorText(snapshot.testAttributeValue)}"]`,
59
- strategy: 'test-id',
60
- }
61
- }
62
- if (snapshot.role && snapshot.accessibleName && snapshot.roleNameMatchCount === 1) {
63
- return {
64
- selector: `role=${snapshot.role}[name="${escapeForSelectorText(snapshot.accessibleName)}"]`,
65
- strategy: 'role',
66
- }
67
- }
68
- if (snapshot.labelText) {
69
- return {
70
- selector: `label="${escapeForSelectorText(snapshot.labelText)}"`,
71
- strategy: 'label',
72
- }
73
- }
74
- if (snapshot.placeholder) {
75
- return {
76
- selector: `placeholder="${escapeForSelectorText(snapshot.placeholder)}"`,
77
- strategy: 'placeholder',
78
- }
79
- }
80
- if (isLikelyStableIdentifier(snapshot.id)) {
81
- return {
82
- selector: `css=#${escapeForCss(snapshot.id)}`,
83
- strategy: 'id',
84
- }
85
- }
86
- const cssSelector = buildCssSelector(snapshot)
87
- if (cssSelector) {
88
- return {
89
- selector: cssSelector,
90
- strategy: 'css',
91
- }
92
- }
93
- return {
94
- selector: buildXPathSelector(snapshot),
95
- strategy: 'xpath',
96
- }
97
- }
98
- async function getElementSnapshot(elementHandle) {
99
- return elementHandle.evaluate(node => {
100
- const element = node
101
- const normalizeText = value => (value !== null && value !== void 0 ? value : '').replace(/\s+/g, ' ').trim()
102
- const isLikelyStableIdentifier = value => {
103
- if (!value) {
104
- return false
105
- }
106
- const normalized = value.trim()
107
- if (!normalized || normalized.length > 120) {
108
- return false
109
- }
110
- return !/\d{4,}/.test(normalized) && !/[A-Fa-f0-9]{8,}/.test(normalized)
111
- }
112
- const getLabelText = candidate => {
113
- if (!['input', 'textarea', 'select'].includes(candidate.tagName.toLowerCase())) {
114
- return ''
115
- }
116
- const input = candidate
117
- if (input.labels && input.labels.length > 0) {
118
- return normalizeText(
119
- Array.from(input.labels)
120
- .map(label => label.textContent || '')
121
- .join(' '),
122
- )
123
- }
124
- return ''
125
- }
126
- const getAccessibleName = candidate => {
127
- const ariaLabel = normalizeText(candidate.getAttribute('aria-label'))
128
- if (ariaLabel) {
129
- return ariaLabel
130
- }
131
- const labelledBy = normalizeText(candidate.getAttribute('aria-labelledby'))
132
- if (labelledBy) {
133
- const text = labelledBy
134
- .split(/\s+/)
135
- .map(id => {
136
- var _a
137
- return ((_a = document.getElementById(id)) === null || _a === void 0 ? void 0 : _a.textContent) || ''
138
- })
139
- .join(' ')
140
- const normalized = normalizeText(text)
141
- if (normalized) {
142
- return normalized
143
- }
144
- }
145
- const labelText = getLabelText(candidate)
146
- if (labelText) {
147
- return labelText
148
- }
149
- const alt = normalizeText(candidate.getAttribute('alt'))
150
- if (alt) {
151
- return alt
152
- }
153
- const title = normalizeText(candidate.getAttribute('title'))
154
- if (title) {
155
- return title
156
- }
157
- const placeholder = normalizeText(candidate.getAttribute('placeholder'))
158
- if (placeholder) {
159
- return placeholder
160
- }
161
- return normalizeText(candidate.textContent).slice(0, 240)
162
- }
163
- const getRole = candidate => {
164
- const explicitRole = normalizeText(candidate.getAttribute('role'))
165
- if (explicitRole) {
166
- return explicitRole
167
- }
168
- const tagName = candidate.tagName.toLowerCase()
169
- if (tagName === 'button') {
170
- return 'button'
171
- }
172
- if (tagName === 'a' && candidate.hasAttribute('href')) {
173
- return 'link'
174
- }
175
- if (tagName === 'textarea') {
176
- return 'textbox'
177
- }
178
- if (tagName === 'select') {
179
- return 'combobox'
180
- }
181
- if (tagName === 'img') {
182
- return 'img'
183
- }
184
- if (tagName === 'input') {
185
- const type = (candidate.getAttribute('type') || 'text').toLowerCase()
186
- if (['button', 'submit', 'reset'].includes(type)) {
187
- return 'button'
188
- }
189
- if (type === 'checkbox') {
190
- return 'checkbox'
191
- }
192
- if (type === 'radio') {
193
- return 'radio'
194
- }
195
- return 'textbox'
196
- }
197
- return ''
198
- }
199
- const role = getRole(element)
200
- const accessibleName = getAccessibleName(element)
201
- let roleNameMatchCount = 0
202
- if (role && accessibleName) {
203
- for (const candidate of Array.from(document.querySelectorAll('*'))) {
204
- if (getRole(candidate) !== role) {
205
- continue
206
- }
207
- if (getAccessibleName(candidate) !== accessibleName) {
208
- continue
209
- }
210
- roleNameMatchCount += 1
211
- if (roleNameMatchCount > 1) {
212
- break
213
- }
214
- }
215
- }
216
- let testAttributeName = ''
217
- let testAttributeValue = ''
218
- for (const attributeName of ['data-testid', 'data-test', 'data-qa']) {
219
- const value = element.getAttribute(attributeName)
220
- if (isLikelyStableIdentifier(value)) {
221
- testAttributeName = attributeName
222
- testAttributeValue = value
223
- break
224
- }
225
- }
226
- return {
227
- tagName: element.tagName.toLowerCase(),
228
- text: normalizeText(element.textContent).slice(0, 240),
229
- accessibleName,
230
- labelText: getLabelText(element),
231
- placeholder: normalizeText(element.getAttribute('placeholder')),
232
- id: normalizeText(element.getAttribute('id')),
233
- role,
234
- roleNameMatchCount,
235
- stableClasses: Array.from(element.classList || [])
236
- .filter(className => isLikelyStableIdentifier(className))
237
- .slice(0, 2),
238
- nameAttribute: normalizeText(element.getAttribute('name')),
239
- typeAttribute: normalizeText(element.getAttribute('type')),
240
- ariaLabel: normalizeText(element.getAttribute('aria-label')),
241
- testAttributeName,
242
- testAttributeValue,
243
- }
244
- })
245
- }
246
- export async function generatePickedLocatorPayload(page, elementHandle) {
247
- const snapshot = await getElementSnapshot(elementHandle)
248
- const selection = buildPrimarySelector(snapshot)
249
- const currentUrl = page.url()
250
- const pageTitle = normalizeText(await page.title().catch(() => ''))
251
- return {
252
- selector: selection.selector,
253
- strategy: selection.strategy,
254
- currentUrl,
255
- pathname: normalizeRoute(currentUrl),
256
- pageTitle,
257
- tagName: snapshot.tagName,
258
- text: snapshot.text || undefined,
259
- accessibleName: snapshot.accessibleName || undefined,
260
- }
261
- }
@@ -1,30 +0,0 @@
1
- import type { CompanionSessionFile } from './types.js'
2
- export declare function getLocatorPickerRootDir(repoRoot?: string): string
3
- export declare function getLocatorPickerSessionsDir(repoRoot?: string): string
4
- export declare function getLocatorPickerProfilesDir(repoRoot?: string): string
5
- export declare function getLocatorPickerLogsDir(repoRoot?: string): string
6
- export declare function getLocatorPickerProfileDir(sessionId: string, repoRoot?: string): string
7
- export declare function getLocatorPickerRuntimeDir(repoRoot?: string): string
8
- export declare function getLocatorPickerRuntimeHomeDir(repoRoot?: string): string
9
- export declare function getLocatorPickerRuntimeEnv(repoRoot?: string, baseEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv
10
- export declare function getLocatorPickerSessionFilePath(sessionId: string, repoRoot?: string): string
11
- export declare function getLocatorPickerCrashLogPath(sessionId: string, repoRoot?: string): string
12
- export declare function removeLocatorPickerProfileDir(sessionId: string, repoRoot?: string): Promise<void>
13
- export declare function removeLocatorPickerSessionFile(sessionId: string, repoRoot?: string): Promise<void>
14
- export declare function clearLocatorPickerCrashLogs(repoRoot?: string): Promise<void>
15
- export declare function createLocatorPickerCrashLog(logFilePath: string): Promise<void>
16
- export declare function appendLocatorPickerCrashLog(logFilePath: string, message: string): Promise<void>
17
- export declare function ensureLocatorPickerDirectories(repoRoot?: string): Promise<void>
18
- export declare function readLocatorPickerSessionFile(sessionFilePath: string): Promise<CompanionSessionFile | null>
19
- export declare function writeLocatorPickerSessionFile(
20
- sessionFilePath: string,
21
- session: Omit<CompanionSessionFile, 'updatedAt'> & {
22
- updatedAt?: string
23
- },
24
- ): Promise<CompanionSessionFile>
25
- export declare function patchLocatorPickerSessionFile(
26
- sessionFilePath: string,
27
- patch:
28
- | Partial<CompanionSessionFile>
29
- | ((current: CompanionSessionFile) => Partial<CompanionSessionFile> | CompanionSessionFile),
30
- ): Promise<CompanionSessionFile | null>
@@ -1,162 +0,0 @@
1
- import { randomUUID } from 'crypto'
2
- import { access, appendFile, mkdir, readFile, rename, rm, unlink, writeFile } from 'fs/promises'
3
- import path from 'path'
4
- const sessionFileOperationQueue = new Map()
5
- function withTimestamp(session) {
6
- var _a
7
- return Object.assign(Object.assign({}, session), {
8
- updatedAt: (_a = session.updatedAt) !== null && _a !== void 0 ? _a : new Date().toISOString(),
9
- })
10
- }
11
- export function getLocatorPickerRootDir(repoRoot = process.cwd()) {
12
- return path.join(repoRoot, '.tmp', 'locator-picker')
13
- }
14
- export function getLocatorPickerSessionsDir(repoRoot = process.cwd()) {
15
- return path.join(getLocatorPickerRootDir(repoRoot), 'sessions')
16
- }
17
- export function getLocatorPickerProfilesDir(repoRoot = process.cwd()) {
18
- return path.join(getLocatorPickerRootDir(repoRoot), 'profiles')
19
- }
20
- export function getLocatorPickerLogsDir(repoRoot = process.cwd()) {
21
- return path.join(getLocatorPickerRootDir(repoRoot), 'logs')
22
- }
23
- export function getLocatorPickerProfileDir(sessionId, repoRoot = process.cwd()) {
24
- return path.join(getLocatorPickerProfilesDir(repoRoot), sessionId)
25
- }
26
- export function getLocatorPickerRuntimeDir(repoRoot = process.cwd()) {
27
- return path.join(getLocatorPickerRootDir(repoRoot), 'runtime')
28
- }
29
- export function getLocatorPickerRuntimeHomeDir(repoRoot = process.cwd()) {
30
- return path.join(getLocatorPickerRuntimeDir(repoRoot), 'home')
31
- }
32
- export function getLocatorPickerRuntimeEnv(repoRoot = process.cwd(), baseEnv = process.env) {
33
- const runtimeDir = getLocatorPickerRuntimeDir(repoRoot)
34
- const runtimeHomeDir = getLocatorPickerRuntimeHomeDir(repoRoot)
35
- const tempDir = path.join(runtimeDir, 'tmp')
36
- const configDir = path.join(runtimeDir, 'config')
37
- const cacheDir = path.join(runtimeDir, 'cache')
38
- const appDataDir = path.join(runtimeDir, 'appdata')
39
- const localAppDataDir = path.join(runtimeDir, 'localappdata')
40
- return Object.assign(Object.assign({}, baseEnv), {
41
- HOME: runtimeHomeDir,
42
- USERPROFILE: runtimeHomeDir,
43
- APPDATA: appDataDir,
44
- LOCALAPPDATA: localAppDataDir,
45
- XDG_CONFIG_HOME: configDir,
46
- XDG_CACHE_HOME: cacheDir,
47
- TMPDIR: tempDir,
48
- TMP: tempDir,
49
- TEMP: tempDir,
50
- })
51
- }
52
- export function getLocatorPickerSessionFilePath(sessionId, repoRoot = process.cwd()) {
53
- return path.join(getLocatorPickerSessionsDir(repoRoot), `${sessionId}.json`)
54
- }
55
- export function getLocatorPickerCrashLogPath(sessionId, repoRoot = process.cwd()) {
56
- return path.join(getLocatorPickerLogsDir(repoRoot), `${sessionId}.log`)
57
- }
58
- export async function removeLocatorPickerProfileDir(sessionId, repoRoot = process.cwd()) {
59
- await rm(getLocatorPickerProfileDir(sessionId, repoRoot), {
60
- force: true,
61
- recursive: true,
62
- }).catch(() => undefined)
63
- }
64
- export async function removeLocatorPickerSessionFile(sessionId, repoRoot = process.cwd()) {
65
- await unlink(getLocatorPickerSessionFilePath(sessionId, repoRoot)).catch(() => undefined)
66
- }
67
- export async function clearLocatorPickerCrashLogs(repoRoot = process.cwd()) {
68
- await rm(getLocatorPickerLogsDir(repoRoot), {
69
- force: true,
70
- recursive: true,
71
- }).catch(() => console.error('Failed to clear locator picker crash logs.'))
72
- await mkdir(getLocatorPickerLogsDir(repoRoot), { recursive: true })
73
- }
74
- export async function createLocatorPickerCrashLog(logFilePath) {
75
- await mkdir(path.dirname(logFilePath), { recursive: true })
76
- await writeFile(logFilePath, '', 'utf8')
77
- }
78
- export async function appendLocatorPickerCrashLog(logFilePath, message) {
79
- try {
80
- await access(logFilePath)
81
- } catch (_a) {
82
- return
83
- }
84
- await appendFile(logFilePath, `[${new Date().toISOString()}] ${message}\n`, 'utf8')
85
- }
86
- export async function ensureLocatorPickerDirectories(repoRoot = process.cwd()) {
87
- const runtimeDir = getLocatorPickerRuntimeDir(repoRoot)
88
- const runtimeHomeDir = getLocatorPickerRuntimeHomeDir(repoRoot)
89
- const tempDir = path.join(runtimeDir, 'tmp')
90
- const configDir = path.join(runtimeDir, 'config')
91
- const cacheDir = path.join(runtimeDir, 'cache')
92
- const appDataDir = path.join(runtimeDir, 'appdata')
93
- const localAppDataDir = path.join(runtimeDir, 'localappdata')
94
- await mkdir(getLocatorPickerSessionsDir(repoRoot), { recursive: true })
95
- await mkdir(getLocatorPickerProfilesDir(repoRoot), { recursive: true })
96
- await mkdir(getLocatorPickerLogsDir(repoRoot), { recursive: true })
97
- await mkdir(runtimeHomeDir, { recursive: true })
98
- await mkdir(tempDir, { recursive: true })
99
- await mkdir(configDir, { recursive: true })
100
- await mkdir(cacheDir, { recursive: true })
101
- await mkdir(appDataDir, { recursive: true })
102
- await mkdir(localAppDataDir, { recursive: true })
103
- }
104
- export async function readLocatorPickerSessionFile(sessionFilePath) {
105
- try {
106
- const raw = await readFile(sessionFilePath, 'utf8')
107
- return JSON.parse(raw)
108
- } catch (_a) {
109
- return null
110
- }
111
- }
112
- async function enqueueSessionFileOperation(sessionFilePath, operation) {
113
- var _a
114
- const previousOperation =
115
- (_a = sessionFileOperationQueue.get(sessionFilePath)) !== null && _a !== void 0 ? _a : Promise.resolve()
116
- const nextOperation = previousOperation.catch(() => undefined).then(operation)
117
- sessionFileOperationQueue.set(sessionFilePath, nextOperation)
118
- try {
119
- return await nextOperation
120
- } finally {
121
- if (sessionFileOperationQueue.get(sessionFilePath) === nextOperation) {
122
- sessionFileOperationQueue.delete(sessionFilePath)
123
- }
124
- }
125
- }
126
- async function writeLocatorPickerSessionFileImmediate(sessionFilePath, session) {
127
- await mkdir(path.dirname(sessionFilePath), { recursive: true })
128
- const normalized = withTimestamp(session)
129
- const serialized = `${JSON.stringify(normalized, null, 2)}\n`
130
- const tempFilePath = `${sessionFilePath}.${process.pid}.${randomUUID()}.tmp`
131
- try {
132
- await writeFile(tempFilePath, serialized, 'utf8')
133
- await rename(tempFilePath, sessionFilePath)
134
- } catch (error) {
135
- await unlink(tempFilePath).catch(() => undefined)
136
- const code = error instanceof Error && 'code' in error ? String(error.code) : ''
137
- if (process.platform === 'win32' || code === 'EPERM' || code === 'EACCES' || code === 'ENOENT') {
138
- await writeFile(sessionFilePath, serialized, 'utf8')
139
- } else {
140
- throw error
141
- }
142
- }
143
- return normalized
144
- }
145
- export async function writeLocatorPickerSessionFile(sessionFilePath, session) {
146
- return enqueueSessionFileOperation(sessionFilePath, () =>
147
- writeLocatorPickerSessionFileImmediate(sessionFilePath, session),
148
- )
149
- }
150
- export async function patchLocatorPickerSessionFile(sessionFilePath, patch) {
151
- return enqueueSessionFileOperation(sessionFilePath, async () => {
152
- const current = await readLocatorPickerSessionFile(sessionFilePath)
153
- if (!current) {
154
- return null
155
- }
156
- const nextPatch = typeof patch === 'function' ? patch(current) : patch
157
- const nextSession = Object.assign(Object.assign(Object.assign({}, current), nextPatch), {
158
- updatedAt: new Date().toISOString(),
159
- })
160
- return writeLocatorPickerSessionFileImmediate(sessionFilePath, nextSession)
161
- })
162
- }
@@ -1,31 +0,0 @@
1
- export type CompanionSessionStatus = 'starting' | 'ready' | 'picked' | 'saving' | 'closed' | 'error'
2
- export type CompanionPickedLocatorStrategy = 'test-id' | 'role' | 'label' | 'placeholder' | 'id' | 'css' | 'xpath'
3
- export interface CompanionLaunchSource {
4
- environmentId?: string
5
- environmentName?: string
6
- url: string
7
- }
8
- export interface CompanionPickedLocatorPayload {
9
- selector: string
10
- currentUrl: string
11
- pathname: string
12
- pageTitle: string
13
- tagName: string
14
- text?: string
15
- accessibleName?: string
16
- strategy?: CompanionPickedLocatorStrategy
17
- }
18
- export interface CompanionSessionFile {
19
- sessionId: string
20
- status: CompanionSessionStatus
21
- launchSource: CompanionLaunchSource
22
- currentUrl: string
23
- currentPathname: string
24
- pageTitle: string
25
- companionPid: number | null
26
- crashLogPath?: string
27
- pickedLocator?: CompanionPickedLocatorPayload
28
- error?: string
29
- startedAt: string
30
- updatedAt: string
31
- }
Binary file