create-appraisejs 0.1.9 → 0.1.10-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.
- package/README.md +24 -17
- package/dist/cli.d.ts +2 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.e2e.test.js +11 -8
- package/dist/cli.e2e.test.js.map +1 -1
- package/dist/cli.js +32 -48
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/config.test.js +9 -5
- package/dist/config.test.js.map +1 -1
- package/dist/copy-template.d.ts +1 -1
- package/dist/copy-template.d.ts.map +1 -1
- package/dist/copy-template.js +7 -3
- package/dist/copy-template.js.map +1 -1
- package/dist/copy-template.test.js +14 -9
- package/dist/copy-template.test.js.map +1 -1
- package/dist/create-project.d.ts +23 -0
- package/dist/create-project.d.ts.map +1 -0
- package/dist/create-project.js +58 -0
- package/dist/create-project.js.map +1 -0
- package/dist/create-project.test.d.ts +2 -0
- package/dist/create-project.test.d.ts.map +1 -0
- package/dist/create-project.test.js +80 -0
- package/dist/create-project.test.js.map +1 -0
- package/dist/install.d.ts +8 -4
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +22 -72
- package/dist/install.js.map +1 -1
- package/dist/install.test.js +26 -10
- package/dist/install.test.js.map +1 -1
- package/dist/package-manager.d.ts +11 -0
- package/dist/package-manager.d.ts.map +1 -0
- package/dist/package-manager.js +47 -0
- package/dist/package-manager.js.map +1 -0
- package/dist/package-manager.test.d.ts +2 -0
- package/dist/package-manager.test.d.ts.map +1 -0
- package/dist/package-manager.test.js +51 -0
- package/dist/package-manager.test.js.map +1 -0
- package/dist/prepare-template-utils.d.ts +10 -0
- package/dist/prepare-template-utils.d.ts.map +1 -0
- package/dist/prepare-template-utils.js +53 -0
- package/dist/prepare-template-utils.js.map +1 -0
- package/dist/prepare-template-utils.test.d.ts +2 -0
- package/dist/prepare-template-utils.test.d.ts.map +1 -0
- package/dist/prepare-template-utils.test.js +67 -0
- package/dist/prepare-template-utils.test.js.map +1 -0
- package/dist/prompts.d.ts +2 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +11 -3
- package/dist/prompts.js.map +1 -1
- package/dist/prompts.test.js +17 -7
- package/dist/prompts.test.js.map +1 -1
- package/dist/template-sync-utils.test.d.ts +2 -0
- package/dist/template-sync-utils.test.d.ts.map +1 -0
- package/dist/template-sync-utils.test.js +41 -0
- package/dist/template-sync-utils.test.js.map +1 -0
- package/package.json +3 -2
- package/templates/default/.appraise-template-meta.json +5 -0
- package/templates/default/.env.example +1 -1
- package/templates/default/.vscode/settings.json +10 -3
- package/templates/default/README.md +27 -25
- package/templates/default/automation/features/base/login.feature +15 -0
- package/templates/default/automation/locators/base/home.json +3 -0
- package/templates/default/automation/locators/base/login.json +6 -0
- package/templates/default/automation/locators/base/test.json +1 -0
- package/templates/default/automation/mapping/locator-map.json +14 -0
- package/templates/default/{src/tests → automation}/steps/actions/click.step.ts +1 -4
- package/templates/default/{src/tests → automation}/steps/actions/hover.step.ts +1 -4
- package/templates/default/{src/tests → automation}/steps/actions/input.step.ts +1 -4
- package/templates/default/{src/tests → automation}/steps/actions/navigation.step.ts +1 -3
- package/templates/default/{src/tests → automation}/steps/actions/random_data.step.ts +1 -3
- package/templates/default/{src/tests → automation}/steps/actions/store.step.ts +1 -4
- package/templates/default/automation/steps/actions/wait.step.ts +91 -0
- package/templates/default/{src/tests → automation}/steps/validations/active_state_assertion.step.ts +1 -4
- package/templates/default/{src/tests → automation}/steps/validations/navigation_assertion.step.ts +1 -2
- package/templates/default/{src/tests → automation}/steps/validations/text_assertion.step.ts +1 -4
- package/templates/default/{src/tests → automation}/steps/validations/visibility_assertion.step.ts +1 -4
- package/templates/default/cucumber.mjs +6 -6
- package/templates/default/eslint.config.mjs +5 -4
- package/templates/default/package-lock.json +322 -485
- package/templates/default/package.json +11 -6
- package/templates/default/packages/cucumber-runtime/package.json +13 -0
- package/templates/default/packages/cucumber-runtime/src/cache.util.ts +93 -0
- package/templates/default/packages/cucumber-runtime/src/cli.ts +68 -0
- package/templates/default/packages/cucumber-runtime/src/environment.util.ts +21 -0
- package/templates/default/packages/cucumber-runtime/src/executor.ts +32 -0
- package/templates/default/{src/tests/hooks → packages/cucumber-runtime/src}/hooks.ts +17 -32
- package/templates/default/packages/cucumber-runtime/src/index.ts +17 -0
- package/templates/default/{src/tests/utils → packages/cucumber-runtime/src}/locator.util.ts +50 -64
- package/templates/default/packages/cucumber-runtime/src/parameter-types.ts +7 -0
- package/templates/default/packages/cucumber-runtime/src/paths.ts +33 -0
- package/templates/default/packages/cucumber-runtime/src/random-data.util.ts +35 -0
- package/templates/default/packages/cucumber-runtime/src/types.ts +13 -0
- package/templates/default/{src/tests/config/executor → packages/cucumber-runtime/src}/world.ts +4 -1
- package/templates/default/packages/cucumber-runtime/tsconfig.json +11 -0
- package/templates/default/scripts/setup-env.ts +4 -4
- package/templates/default/scripts/sync-appraise-base-template.ts +123 -105
- package/templates/default/scripts/sync-environments.ts +8 -5
- package/templates/default/scripts/sync-locator-groups.ts +7 -10
- package/templates/default/scripts/sync-locators.ts +5 -9
- package/templates/default/scripts/sync-modules.ts +9 -17
- package/templates/default/scripts/sync-tags.ts +2 -2
- package/templates/default/scripts/sync-template-step-groups.ts +16 -6
- package/templates/default/scripts/sync-template-steps.ts +16 -5
- package/templates/default/scripts/sync-test-cases.ts +6 -3
- package/templates/default/scripts/sync-test-suites.ts +7 -4
- package/templates/default/src/actions/environments/environment-actions.ts +6 -23
- package/templates/default/src/actions/locator/locator-actions.ts +36 -93
- package/templates/default/src/actions/locator-groups/locator-group-actions.ts +24 -78
- package/templates/default/src/actions/modules/module-actions.ts +4 -2
- package/templates/default/src/actions/tags/tag-actions.ts +4 -1
- package/templates/default/src/actions/template-step/template-step-actions.ts +10 -101
- package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +31 -130
- package/templates/default/src/actions/test-case/test-case-actions.ts +31 -94
- package/templates/default/src/actions/test-run/test-run-actions.ts +11 -13
- package/templates/default/src/actions/test-suite/test-suite-actions.ts +29 -82
- package/templates/default/src/app/(base)/locator-groups/page.tsx +1 -3
- package/templates/default/src/app/(base)/reports/page.tsx +1 -1
- package/templates/default/src/app/(base)/reports/test-cases/page.tsx +2 -2
- package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +1 -1
- package/templates/default/src/app/(base)/tags/page.tsx +2 -2
- package/templates/default/src/app/(base)/template-steps/page.tsx +1 -2
- package/templates/default/src/app/(base)/test-runs/page.tsx +2 -2
- package/templates/default/src/app/api/test-runs/[runId]/logs/route.ts +2 -1
- package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +2 -1
- package/templates/default/src/app/page.tsx +4 -5
- package/templates/default/src/components/diagram/dynamic-parameters.tsx +76 -40
- package/templates/default/src/components/diagram/options-header-node.tsx +1 -1
- package/templates/default/src/components/ui/data-table.tsx +33 -39
- package/templates/default/src/lib/automation/paths.ts +181 -0
- package/templates/default/src/lib/automation/projection-service.ts +230 -0
- package/templates/default/src/lib/environment-file-utils.ts +14 -51
- package/templates/default/src/lib/executor/local-executor-adapter.ts +101 -0
- package/templates/default/src/lib/executor/types.ts +24 -0
- package/templates/default/src/lib/feature-file-generator.ts +22 -112
- package/templates/default/src/lib/locator-group-file-utils.ts +57 -120
- package/templates/default/src/lib/process/task-spawner.ts +236 -0
- package/templates/default/src/lib/template-sync-utils.d.ts +7 -0
- package/templates/default/src/lib/template-sync-utils.d.ts.map +1 -0
- package/templates/default/src/lib/template-sync-utils.js +47 -0
- package/templates/default/src/lib/template-sync-utils.js.map +1 -0
- package/templates/default/src/lib/template-sync-utils.ts +63 -0
- package/templates/default/src/lib/test-run/process-manager.ts +9 -87
- package/templates/default/src/lib/test-run/test-run-executor.ts +7 -136
- package/templates/default/src/lib/test-run/winston-logger.ts +6 -35
- package/templates/default/src/lib/utils/template-step-file-generator.ts +22 -85
- package/templates/default/src/lib/utils/template-step-file-manager-intelligent.ts +7 -22
- package/templates/default/public/favicon.ico +0 -0
- package/templates/default/src/tests/executor.ts +0 -80
- package/templates/default/src/tests/mapping/locator-map.json +0 -1
- package/templates/default/src/tests/steps/actions/wait.step.ts +0 -107
- package/templates/default/src/tests/support/parameter-types.ts +0 -12
- package/templates/default/src/tests/utils/cache.util.ts +0 -260
- package/templates/default/src/tests/utils/cli.util.ts +0 -177
- package/templates/default/src/tests/utils/environment.util.ts +0 -65
- package/templates/default/src/tests/utils/random-data.util.ts +0 -45
- package/templates/default/src/tests/utils/spawner.util.ts +0 -617
|
@@ -127,7 +127,7 @@ export function DataTable<TData, TValue>({
|
|
|
127
127
|
<div className="flex justify-end">
|
|
128
128
|
<div className="mb-4 flex gap-2">
|
|
129
129
|
{createLink && (
|
|
130
|
-
<Button variant="default" size="icon">
|
|
130
|
+
<Button variant="default" size="icon" asChild>
|
|
131
131
|
<Link href={createLink}>
|
|
132
132
|
<PlusCircle className="h-4 w-4" />
|
|
133
133
|
</Link>
|
|
@@ -154,46 +154,40 @@ export function DataTable<TData, TValue>({
|
|
|
154
154
|
</DropdownMenu>
|
|
155
155
|
)}
|
|
156
156
|
{modifyLink && (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
<Pencil className="h-4 w-4" />
|
|
174
|
-
</Link>
|
|
175
|
-
</Button>
|
|
157
|
+
(() => {
|
|
158
|
+
const disabled =
|
|
159
|
+
table.getSelectedRowModel().rows.length === 0 || table.getSelectedRowModel().rows.length > 1
|
|
160
|
+
const href = `${modifyLink}/${disabled ? '' : (table.getSelectedRowModel().rows[0].original as { id: string }).id}`
|
|
161
|
+
return disabled ? (
|
|
162
|
+
<Button variant="outline" size="icon" disabled>
|
|
163
|
+
<Pencil className="h-4 w-4" />
|
|
164
|
+
</Button>
|
|
165
|
+
) : (
|
|
166
|
+
<Button variant="outline" size="icon" asChild>
|
|
167
|
+
<Link href={href}>
|
|
168
|
+
<Pencil className="h-4 w-4" />
|
|
169
|
+
</Link>
|
|
170
|
+
</Button>
|
|
171
|
+
)
|
|
172
|
+
})()
|
|
176
173
|
)}
|
|
177
174
|
{viewLink && (
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
<Eye className="h-4 w-4" />
|
|
195
|
-
</Link>
|
|
196
|
-
</Button>
|
|
175
|
+
(() => {
|
|
176
|
+
const disabled =
|
|
177
|
+
table.getSelectedRowModel().rows.length === 0 || table.getSelectedRowModel().rows.length > 1
|
|
178
|
+
const href = `${viewLink}/${disabled ? '' : (table.getSelectedRowModel().rows[0].original as { id: string }).id}`
|
|
179
|
+
return disabled ? (
|
|
180
|
+
<Button variant="outline" size="icon" disabled>
|
|
181
|
+
<Eye className="h-4 w-4" />
|
|
182
|
+
</Button>
|
|
183
|
+
) : (
|
|
184
|
+
<Button variant="outline" size="icon" asChild>
|
|
185
|
+
<Link href={href}>
|
|
186
|
+
<Eye className="h-4 w-4" />
|
|
187
|
+
</Link>
|
|
188
|
+
</Button>
|
|
189
|
+
)
|
|
190
|
+
})()
|
|
197
191
|
)}
|
|
198
192
|
{deleteAction && (
|
|
199
193
|
<DeletePrompt
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
const repoRoot = process.cwd()
|
|
5
|
+
const automationRoot = path.join(repoRoot, 'automation')
|
|
6
|
+
const legacyTestsRoot = path.join(repoRoot, 'src', 'tests')
|
|
7
|
+
const runtimeImport =
|
|
8
|
+
"import { When, Then, CustomWorld, expect, SelectorName, resolveLocator, getEnvironment, generateRandomData, RandomDataType } from '../../../packages/cucumber-runtime/src/index.js'"
|
|
9
|
+
|
|
10
|
+
const mutableLegacyDirectories = ['features', 'locators', 'mapping', 'reports', 'steps'] as const
|
|
11
|
+
|
|
12
|
+
let automationWorkspaceReadyPromise: Promise<void> | null = null
|
|
13
|
+
|
|
14
|
+
async function pathExists(targetPath: string): Promise<boolean> {
|
|
15
|
+
try {
|
|
16
|
+
await fs.access(targetPath)
|
|
17
|
+
return true
|
|
18
|
+
} catch {
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function rewriteLegacyStepImports(): Promise<void> {
|
|
24
|
+
const stepsDir = getAutomationStepsDir()
|
|
25
|
+
|
|
26
|
+
if (!(await pathExists(stepsDir))) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const stepFiles = await fs.readdir(stepsDir, { recursive: true })
|
|
31
|
+
for (const entry of stepFiles) {
|
|
32
|
+
if (typeof entry !== 'string' || !entry.endsWith('.ts')) {
|
|
33
|
+
continue
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const filePath = path.join(stepsDir, entry)
|
|
37
|
+
let content = await fs.readFile(filePath, 'utf8')
|
|
38
|
+
|
|
39
|
+
content = content
|
|
40
|
+
.replace(/^import \{ (When|Then) \} from '@cucumber\/cucumber';?\r?\n/gm, '')
|
|
41
|
+
.replace(/^import \{ CustomWorld(?:, expect)? \} from '\.\.\/\.\.\/config\/executor\/world\.js';?\r?\n/gm, '')
|
|
42
|
+
.replace(/^import \{ SelectorName \} from '(?:@\/types\/locator\/locator\.type|\.\.\/\.\.\/\.\.\/types\/locator\/locator\.type)';?\r?\n/gm, '')
|
|
43
|
+
.replace(/^import \{ resolveLocator \} from '\.\.\/\.\.\/utils\/locator\.util\.js';?\r?\n/gm, '')
|
|
44
|
+
.replace(/^import \{ getEnvironment \} from '\.\.\/\.\.\/utils\/environment\.util\.js';?\r?\n/gm, '')
|
|
45
|
+
.replace(/^import \{ generateRandomData, RandomDataType \} from '\.\.\/\.\.\/utils\/random-data\.util\.js';?\r?\n/gm, '')
|
|
46
|
+
.trimStart()
|
|
47
|
+
|
|
48
|
+
if (!content.startsWith(runtimeImport)) {
|
|
49
|
+
content = `${runtimeImport}\n${content}`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
await fs.writeFile(filePath, content)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function copyLegacyMutableWorkspace(): Promise<void> {
|
|
57
|
+
const legacyExists = await pathExists(legacyTestsRoot)
|
|
58
|
+
const automationExists = await pathExists(automationRoot)
|
|
59
|
+
|
|
60
|
+
if (!legacyExists || automationExists) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await fs.mkdir(automationRoot, { recursive: true })
|
|
65
|
+
|
|
66
|
+
const legacyEnvironmentsDir = path.join(legacyTestsRoot, 'config', 'environments')
|
|
67
|
+
if (await pathExists(legacyEnvironmentsDir)) {
|
|
68
|
+
await fs.cp(legacyEnvironmentsDir, getAutomationEnvironmentsDir(), { recursive: true })
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const directory of mutableLegacyDirectories) {
|
|
72
|
+
const sourceDirectory = path.join(legacyTestsRoot, directory)
|
|
73
|
+
const destinationDirectory = path.join(automationRoot, directory)
|
|
74
|
+
|
|
75
|
+
if (await pathExists(sourceDirectory)) {
|
|
76
|
+
await fs.cp(sourceDirectory, destinationDirectory, { recursive: true })
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function copyLegacyEnvironmentsIfNeeded(): Promise<void> {
|
|
82
|
+
const legacyEnvironmentsDir = path.join(legacyTestsRoot, 'config', 'environments')
|
|
83
|
+
const automationEnvironmentsFile = path.join(getAutomationEnvironmentsDir(), 'environments.json')
|
|
84
|
+
|
|
85
|
+
if (!(await pathExists(legacyEnvironmentsDir)) || (await pathExists(automationEnvironmentsFile))) {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await fs.mkdir(getAutomationEnvironmentsDir(), { recursive: true })
|
|
90
|
+
await fs.cp(legacyEnvironmentsDir, getAutomationEnvironmentsDir(), { recursive: true })
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function ensureMutableAutomationDirectories(): Promise<void> {
|
|
94
|
+
const requiredDirectories = [
|
|
95
|
+
getAutomationRoot(),
|
|
96
|
+
getAutomationConfigDir(),
|
|
97
|
+
getAutomationEnvironmentsDir(),
|
|
98
|
+
getAutomationFeaturesDir(),
|
|
99
|
+
getAutomationLocatorsDir(),
|
|
100
|
+
getAutomationMappingDir(),
|
|
101
|
+
getAutomationReportsDir(),
|
|
102
|
+
getAutomationReportLogsDir(),
|
|
103
|
+
getAutomationReportTracesDir(),
|
|
104
|
+
getAutomationStepsDir(),
|
|
105
|
+
getAutomationActionStepsDir(),
|
|
106
|
+
getAutomationValidationStepsDir(),
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
await Promise.all(requiredDirectories.map(directory => fs.mkdir(directory, { recursive: true })))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function removeLegacyRuntimeArtifactsFromAutomation(): Promise<void> {
|
|
113
|
+
await fs.rm(path.join(getAutomationConfigDir(), 'executor'), { recursive: true, force: true })
|
|
114
|
+
await fs.rm(path.join(getAutomationRoot(), 'hooks'), { recursive: true, force: true })
|
|
115
|
+
await fs.rm(path.join(getAutomationRoot(), 'support'), { recursive: true, force: true })
|
|
116
|
+
await fs.rm(path.join(getAutomationRoot(), 'utils'), { recursive: true, force: true })
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function ensureAutomationWorkspaceReady(): Promise<void> {
|
|
120
|
+
automationWorkspaceReadyPromise ??= (async () => {
|
|
121
|
+
await copyLegacyMutableWorkspace()
|
|
122
|
+
await copyLegacyEnvironmentsIfNeeded()
|
|
123
|
+
await ensureMutableAutomationDirectories()
|
|
124
|
+
await removeLegacyRuntimeArtifactsFromAutomation()
|
|
125
|
+
await rewriteLegacyStepImports()
|
|
126
|
+
})()
|
|
127
|
+
|
|
128
|
+
return automationWorkspaceReadyPromise
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function getAutomationRoot(): string {
|
|
132
|
+
return automationRoot
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function getLegacyTestsRoot(): string {
|
|
136
|
+
return legacyTestsRoot
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function getAutomationConfigDir(): string {
|
|
140
|
+
return path.join(getAutomationRoot(), 'config')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function getAutomationEnvironmentsDir(): string {
|
|
144
|
+
return path.join(getAutomationConfigDir(), 'environments')
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function getAutomationFeaturesDir(): string {
|
|
148
|
+
return path.join(getAutomationRoot(), 'features')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function getAutomationLocatorsDir(): string {
|
|
152
|
+
return path.join(getAutomationRoot(), 'locators')
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function getAutomationMappingDir(): string {
|
|
156
|
+
return path.join(getAutomationRoot(), 'mapping')
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function getAutomationReportsDir(): string {
|
|
160
|
+
return path.join(getAutomationRoot(), 'reports')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function getAutomationReportLogsDir(): string {
|
|
164
|
+
return path.join(getAutomationReportsDir(), 'logs')
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function getAutomationReportTracesDir(): string {
|
|
168
|
+
return path.join(getAutomationReportsDir(), 'traces')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function getAutomationStepsDir(): string {
|
|
172
|
+
return path.join(getAutomationRoot(), 'steps')
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function getAutomationActionStepsDir(): string {
|
|
176
|
+
return path.join(getAutomationStepsDir(), 'actions')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function getAutomationValidationStepsDir(): string {
|
|
180
|
+
return path.join(getAutomationStepsDir(), 'validations')
|
|
181
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import prisma from '@/config/db-config'
|
|
2
|
+
import { createOrUpdateEnvironmentsFile } from '@/lib/environment-file-utils'
|
|
3
|
+
import {
|
|
4
|
+
createEmptyLocatorGroupFile,
|
|
5
|
+
createOrUpdateLocatorGroupFile,
|
|
6
|
+
deleteLocatorGroupFile,
|
|
7
|
+
moveLocatorGroupFile,
|
|
8
|
+
removeLocatorMapEntry,
|
|
9
|
+
renameLocatorGroupFile,
|
|
10
|
+
updateLocatorMapFile,
|
|
11
|
+
} from '@/lib/locator-group-file-utils'
|
|
12
|
+
import { deleteFeatureFile, generateFeatureFile, regenerateAllFeatureFiles } from '@/lib/feature-file-generator'
|
|
13
|
+
import {
|
|
14
|
+
createTemplateStepGroupFile,
|
|
15
|
+
removeTemplateStepGroupFile,
|
|
16
|
+
renameTemplateStepGroupFile,
|
|
17
|
+
} from '@/lib/utils/template-step-file-manager-intelligent'
|
|
18
|
+
import { generateFileContent, writeTemplateStepFile } from '@/lib/utils/template-step-file-generator'
|
|
19
|
+
import { ensureAutomationWorkspaceReady } from './paths'
|
|
20
|
+
|
|
21
|
+
type TemplateStepGroupType = 'ACTION' | 'VALIDATION'
|
|
22
|
+
|
|
23
|
+
function getTemplateStepGroupType(type: string | null | undefined): TemplateStepGroupType {
|
|
24
|
+
return type === 'VALIDATION' ? 'VALIDATION' : 'ACTION'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class AutomationProjectionService {
|
|
28
|
+
async syncEnvironments(): Promise<boolean> {
|
|
29
|
+
await ensureAutomationWorkspaceReady()
|
|
30
|
+
return createOrUpdateEnvironmentsFile()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async createEmptyLocatorGroup(locatorGroupId: string): Promise<boolean> {
|
|
34
|
+
await ensureAutomationWorkspaceReady()
|
|
35
|
+
return createEmptyLocatorGroupFile(locatorGroupId)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async syncLocatorGroup(locatorGroupId: string): Promise<boolean> {
|
|
39
|
+
await ensureAutomationWorkspaceReady()
|
|
40
|
+
return createOrUpdateLocatorGroupFile(locatorGroupId)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async renameLocatorGroup(locatorGroupId: string, newName: string, oldName?: string): Promise<boolean> {
|
|
44
|
+
await ensureAutomationWorkspaceReady()
|
|
45
|
+
return renameLocatorGroupFile(locatorGroupId, newName, oldName)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async moveLocatorGroup(locatorGroupId: string, previousFilePath?: string): Promise<boolean> {
|
|
49
|
+
await ensureAutomationWorkspaceReady()
|
|
50
|
+
return moveLocatorGroupFile(locatorGroupId, previousFilePath)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async deleteLocatorGroup(locatorGroupId: string): Promise<boolean> {
|
|
54
|
+
await ensureAutomationWorkspaceReady()
|
|
55
|
+
return deleteLocatorGroupFile(locatorGroupId)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async syncLocatorMap(
|
|
59
|
+
currentLocatorGroupRoute: string,
|
|
60
|
+
newLocatorGroupRoute: string,
|
|
61
|
+
currentLocatorGroupName: string,
|
|
62
|
+
newLocatorGroupName: string,
|
|
63
|
+
): Promise<boolean>
|
|
64
|
+
async syncLocatorMap(newLocatorGroupName: string, newLocatorGroupRoute: string): Promise<boolean>
|
|
65
|
+
async syncLocatorMap(param1: string, param2: string, param3?: string, param4?: string): Promise<boolean> {
|
|
66
|
+
await ensureAutomationWorkspaceReady()
|
|
67
|
+
if (param3 === undefined || param4 === undefined) {
|
|
68
|
+
return updateLocatorMapFile(param1, param2)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return updateLocatorMapFile(param1, param2, param3, param4)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async deleteLocatorMapEntries(locatorGroupNames: string[]): Promise<boolean> {
|
|
75
|
+
await ensureAutomationWorkspaceReady()
|
|
76
|
+
return removeLocatorMapEntry(locatorGroupNames)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async syncTemplateStepGroup(groupId: string): Promise<void> {
|
|
80
|
+
await ensureAutomationWorkspaceReady()
|
|
81
|
+
|
|
82
|
+
const group = await prisma.templateStepGroup.findUnique({
|
|
83
|
+
where: { id: groupId },
|
|
84
|
+
include: {
|
|
85
|
+
templateSteps: {
|
|
86
|
+
orderBy: {
|
|
87
|
+
createdAt: 'asc',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
if (!group) {
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const groupType = getTemplateStepGroupType((group as { type?: string | null }).type)
|
|
98
|
+
|
|
99
|
+
if (group.templateSteps.length === 0) {
|
|
100
|
+
await createTemplateStepGroupFile(group.name, groupType, group.description)
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const content = generateFileContent(group.templateSteps)
|
|
105
|
+
await writeTemplateStepFile(group.name, content, groupType)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async deleteTemplateStepGroup(groupId: string): Promise<void> {
|
|
109
|
+
await ensureAutomationWorkspaceReady()
|
|
110
|
+
|
|
111
|
+
const group = await prisma.templateStepGroup.findUnique({
|
|
112
|
+
where: { id: groupId },
|
|
113
|
+
select: {
|
|
114
|
+
name: true,
|
|
115
|
+
type: true,
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
if (!group) {
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
await removeTemplateStepGroupFile(group.name, getTemplateStepGroupType(group.type))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async renameTemplateStepGroup(
|
|
127
|
+
groupId: string,
|
|
128
|
+
newName: string,
|
|
129
|
+
newType: string,
|
|
130
|
+
newDescription?: string | null,
|
|
131
|
+
): Promise<void> {
|
|
132
|
+
await ensureAutomationWorkspaceReady()
|
|
133
|
+
|
|
134
|
+
const currentGroup = await prisma.templateStepGroup.findUnique({
|
|
135
|
+
where: { id: groupId },
|
|
136
|
+
select: {
|
|
137
|
+
name: true,
|
|
138
|
+
type: true,
|
|
139
|
+
},
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
if (!currentGroup) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await renameTemplateStepGroupFile(
|
|
147
|
+
currentGroup.name,
|
|
148
|
+
newName,
|
|
149
|
+
getTemplateStepGroupType(currentGroup.type),
|
|
150
|
+
getTemplateStepGroupType(newType),
|
|
151
|
+
newDescription,
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async syncTemplateStep(stepId: string): Promise<void> {
|
|
156
|
+
const step = await prisma.templateStep.findUnique({
|
|
157
|
+
where: { id: stepId },
|
|
158
|
+
select: { templateStepGroupId: true },
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
if (!step?.templateStepGroupId) {
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
await this.syncTemplateStepGroup(step.templateStepGroupId)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async deleteTemplateStep(stepId: string): Promise<void> {
|
|
169
|
+
const step = await prisma.templateStep.findUnique({
|
|
170
|
+
where: { id: stepId },
|
|
171
|
+
select: { templateStepGroupId: true },
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
if (!step?.templateStepGroupId) {
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
await this.syncTemplateStepGroup(step.templateStepGroupId)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async generateFeature(testSuiteId: string): Promise<string> {
|
|
182
|
+
await ensureAutomationWorkspaceReady()
|
|
183
|
+
|
|
184
|
+
const testSuite = await prisma.testSuite.findUnique({
|
|
185
|
+
where: { id: testSuiteId },
|
|
186
|
+
select: {
|
|
187
|
+
name: true,
|
|
188
|
+
description: true,
|
|
189
|
+
},
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
if (!testSuite) {
|
|
193
|
+
throw new Error(`Test suite ${testSuiteId} not found`)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return generateFeatureFile(testSuiteId, testSuite.name, testSuite.description || undefined)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async deleteFeature(testSuiteId: string): Promise<boolean> {
|
|
200
|
+
await ensureAutomationWorkspaceReady()
|
|
201
|
+
return deleteFeatureFile(testSuiteId)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async regenerateAllFeatures(): Promise<string[]> {
|
|
205
|
+
await ensureAutomationWorkspaceReady()
|
|
206
|
+
return regenerateAllFeatureFiles()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async regenerateAllPathDependentArtifacts(): Promise<void> {
|
|
210
|
+
await ensureAutomationWorkspaceReady()
|
|
211
|
+
|
|
212
|
+
const locatorGroups = await prisma.locatorGroup.findMany({
|
|
213
|
+
select: { id: true },
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
const templateStepGroups = await prisma.templateStepGroup.findMany({
|
|
217
|
+
select: { id: true },
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
await Promise.all([
|
|
221
|
+
this.syncEnvironments(),
|
|
222
|
+
...locatorGroups.map(locatorGroup => this.syncLocatorGroup(locatorGroup.id)),
|
|
223
|
+
...templateStepGroups.map(group => this.syncTemplateStepGroup(group.id)),
|
|
224
|
+
])
|
|
225
|
+
|
|
226
|
+
await this.regenerateAllFeatures()
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export const automationProjectionService = new AutomationProjectionService()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from 'fs'
|
|
2
2
|
import * as path from 'path'
|
|
3
3
|
import prisma from '@/config/db-config'
|
|
4
|
+
import { ensureAutomationWorkspaceReady, getAutomationEnvironmentsDir } from '@/lib/automation/paths'
|
|
4
5
|
|
|
5
6
|
interface EnvironmentConfig {
|
|
6
7
|
baseUrl: string
|
|
@@ -9,29 +10,15 @@ interface EnvironmentConfig {
|
|
|
9
10
|
password: string
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
/**
|
|
13
|
-
* Gets the file path for the environments.json file
|
|
14
|
-
*/
|
|
15
13
|
export function getEnvironmentsFilePath(): string {
|
|
16
|
-
return path.join(
|
|
14
|
+
return path.join(getAutomationEnvironmentsDir(), 'environments.json')
|
|
17
15
|
}
|
|
18
16
|
|
|
19
|
-
/**
|
|
20
|
-
* Ensures the config directory exists
|
|
21
|
-
*/
|
|
22
17
|
export async function ensureConfigDirectoryExists(): Promise<void> {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
await fs.access(dir)
|
|
27
|
-
} catch {
|
|
28
|
-
await fs.mkdir(dir, { recursive: true })
|
|
29
|
-
}
|
|
18
|
+
await ensureAutomationWorkspaceReady()
|
|
19
|
+
await fs.mkdir(path.dirname(getEnvironmentsFilePath()), { recursive: true })
|
|
30
20
|
}
|
|
31
21
|
|
|
32
|
-
/**
|
|
33
|
-
* Generates JSON content for environments from database
|
|
34
|
-
*/
|
|
35
22
|
export async function generateEnvironmentsContent(): Promise<Record<string, EnvironmentConfig>> {
|
|
36
23
|
try {
|
|
37
24
|
const environments = await prisma.environment.findMany({
|
|
@@ -57,17 +44,14 @@ export async function generateEnvironmentsContent(): Promise<Record<string, Envi
|
|
|
57
44
|
}
|
|
58
45
|
}
|
|
59
46
|
|
|
60
|
-
/**
|
|
61
|
-
* Creates or updates the environments.json file
|
|
62
|
-
*/
|
|
63
47
|
export async function createOrUpdateEnvironmentsFile(): Promise<boolean> {
|
|
64
48
|
try {
|
|
49
|
+
await ensureAutomationWorkspaceReady()
|
|
65
50
|
const filePath = getEnvironmentsFilePath()
|
|
66
51
|
await ensureConfigDirectoryExists()
|
|
67
52
|
|
|
68
53
|
const content = await generateEnvironmentsContent()
|
|
69
54
|
|
|
70
|
-
// If no environments exist, delete the file
|
|
71
55
|
if (Object.keys(content).length === 0) {
|
|
72
56
|
await deleteEnvironmentsFile()
|
|
73
57
|
return true
|
|
@@ -81,18 +65,15 @@ export async function createOrUpdateEnvironmentsFile(): Promise<boolean> {
|
|
|
81
65
|
}
|
|
82
66
|
}
|
|
83
67
|
|
|
84
|
-
/**
|
|
85
|
-
* Deletes the environments.json file
|
|
86
|
-
*/
|
|
87
68
|
export async function deleteEnvironmentsFile(): Promise<boolean> {
|
|
88
69
|
try {
|
|
70
|
+
await ensureAutomationWorkspaceReady()
|
|
89
71
|
const filePath = getEnvironmentsFilePath()
|
|
90
72
|
|
|
91
|
-
// Check if file exists before trying to delete
|
|
92
73
|
try {
|
|
93
74
|
await fs.access(filePath)
|
|
94
75
|
} catch {
|
|
95
|
-
return true
|
|
76
|
+
return true
|
|
96
77
|
}
|
|
97
78
|
|
|
98
79
|
await fs.unlink(filePath)
|
|
@@ -103,20 +84,18 @@ export async function deleteEnvironmentsFile(): Promise<boolean> {
|
|
|
103
84
|
}
|
|
104
85
|
}
|
|
105
86
|
|
|
106
|
-
/**
|
|
107
|
-
* Reads and parses the content of the environments.json file
|
|
108
|
-
*/
|
|
109
87
|
export async function readEnvironmentsFile(): Promise<{
|
|
110
88
|
filePath: string
|
|
111
89
|
content: Record<string, EnvironmentConfig>
|
|
112
90
|
} | null> {
|
|
113
91
|
try {
|
|
92
|
+
await ensureAutomationWorkspaceReady()
|
|
114
93
|
const filePath = getEnvironmentsFilePath()
|
|
115
94
|
|
|
116
95
|
try {
|
|
117
96
|
await fs.access(filePath)
|
|
118
97
|
} catch {
|
|
119
|
-
return null
|
|
98
|
+
return null
|
|
120
99
|
}
|
|
121
100
|
|
|
122
101
|
const fileContent = await fs.readFile(filePath, 'utf-8')
|
|
@@ -129,12 +108,9 @@ export async function readEnvironmentsFile(): Promise<{
|
|
|
129
108
|
}
|
|
130
109
|
}
|
|
131
110
|
|
|
132
|
-
/**
|
|
133
|
-
* Updates a specific environment entry in the environments.json file
|
|
134
|
-
*/
|
|
135
111
|
export async function updateEnvironmentEntry(environmentId: string, oldName?: string): Promise<boolean> {
|
|
136
112
|
try {
|
|
137
|
-
|
|
113
|
+
await ensureAutomationWorkspaceReady()
|
|
138
114
|
const environment = await prisma.environment.findUnique({
|
|
139
115
|
where: { id: environmentId },
|
|
140
116
|
})
|
|
@@ -145,24 +121,21 @@ export async function updateEnvironmentEntry(environmentId: string, oldName?: st
|
|
|
145
121
|
}
|
|
146
122
|
|
|
147
123
|
const filePath = getEnvironmentsFilePath()
|
|
148
|
-
|
|
149
|
-
// Read existing content
|
|
150
124
|
let environmentsConfig: Record<string, EnvironmentConfig> = {}
|
|
125
|
+
|
|
151
126
|
try {
|
|
152
127
|
await fs.access(filePath)
|
|
153
128
|
const fileContent = await fs.readFile(filePath, 'utf-8')
|
|
154
129
|
environmentsConfig = JSON.parse(fileContent)
|
|
155
130
|
} catch {
|
|
156
|
-
|
|
131
|
+
environmentsConfig = {}
|
|
157
132
|
}
|
|
158
133
|
|
|
159
|
-
// Remove old entry if name changed
|
|
160
134
|
if (oldName) {
|
|
161
135
|
const oldKey = oldName.toLowerCase().replace(/\s+/g, '_')
|
|
162
136
|
delete environmentsConfig[oldKey]
|
|
163
137
|
}
|
|
164
138
|
|
|
165
|
-
// Add/update the environment entry
|
|
166
139
|
const envKey = environment.name.toLowerCase().replace(/\s+/g, '_')
|
|
167
140
|
environmentsConfig[envKey] = {
|
|
168
141
|
baseUrl: environment.baseUrl,
|
|
@@ -171,10 +144,7 @@ export async function updateEnvironmentEntry(environmentId: string, oldName?: st
|
|
|
171
144
|
password: environment.password || '',
|
|
172
145
|
}
|
|
173
146
|
|
|
174
|
-
// Ensure directory exists
|
|
175
147
|
await ensureConfigDirectoryExists()
|
|
176
|
-
|
|
177
|
-
// Write updated content
|
|
178
148
|
await fs.writeFile(filePath, JSON.stringify(environmentsConfig, null, 2))
|
|
179
149
|
return true
|
|
180
150
|
} catch (error) {
|
|
@@ -183,35 +153,28 @@ export async function updateEnvironmentEntry(environmentId: string, oldName?: st
|
|
|
183
153
|
}
|
|
184
154
|
}
|
|
185
155
|
|
|
186
|
-
/**
|
|
187
|
-
* Removes a specific environment entry from the environments.json file
|
|
188
|
-
*/
|
|
189
156
|
export async function removeEnvironmentEntry(environmentName: string): Promise<boolean> {
|
|
190
157
|
try {
|
|
158
|
+
await ensureAutomationWorkspaceReady()
|
|
191
159
|
const filePath = getEnvironmentsFilePath()
|
|
192
160
|
|
|
193
|
-
// Check if file exists
|
|
194
161
|
try {
|
|
195
162
|
await fs.access(filePath)
|
|
196
163
|
} catch {
|
|
197
|
-
return true
|
|
164
|
+
return true
|
|
198
165
|
}
|
|
199
166
|
|
|
200
|
-
// Read existing content
|
|
201
167
|
const fileContent = await fs.readFile(filePath, 'utf-8')
|
|
202
168
|
const environmentsConfig: Record<string, EnvironmentConfig> = JSON.parse(fileContent)
|
|
203
169
|
|
|
204
|
-
// Remove the environment entry
|
|
205
170
|
const envKey = environmentName.toLowerCase().replace(/\s+/g, '_')
|
|
206
171
|
delete environmentsConfig[envKey]
|
|
207
172
|
|
|
208
|
-
// If no environments left, delete the file
|
|
209
173
|
if (Object.keys(environmentsConfig).length === 0) {
|
|
210
174
|
await deleteEnvironmentsFile()
|
|
211
175
|
return true
|
|
212
176
|
}
|
|
213
177
|
|
|
214
|
-
// Write updated content
|
|
215
178
|
await fs.writeFile(filePath, JSON.stringify(environmentsConfig, null, 2))
|
|
216
179
|
return true
|
|
217
180
|
} catch (error) {
|