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
|
@@ -2,46 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
import prisma from '@/config/db-config'
|
|
4
4
|
import { templateStepGroupSchema } from '@/constants/form-opts/template-step-group-form-opts'
|
|
5
|
+
import { automationProjectionService } from '@/lib/automation/projection-service'
|
|
5
6
|
import { ActionResponse } from '@/types/form/actionHandler'
|
|
6
7
|
import { Prisma } from '@prisma/client'
|
|
7
8
|
import { revalidatePath } from 'next/cache'
|
|
8
9
|
import { z, ZodError } from 'zod'
|
|
9
10
|
|
|
10
|
-
// TemplateStepGroupType will be available after Prisma migration
|
|
11
11
|
type TemplateStepGroupType = 'ACTION' | 'VALIDATION'
|
|
12
12
|
|
|
13
|
-
// Type helper to safely extract type from Prisma records
|
|
14
|
-
type TemplateStepGroupWithType = {
|
|
15
|
-
id: string
|
|
16
|
-
name: string
|
|
17
|
-
description: string | null
|
|
18
|
-
type?: TemplateStepGroupType
|
|
19
|
-
createdAt: Date
|
|
20
|
-
updatedAt: Date
|
|
21
|
-
}
|
|
22
|
-
|
|
23
13
|
function getGroupType(group: unknown): TemplateStepGroupType {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
if (type === 'VALIDATION' || type === 'ACTION') {
|
|
27
|
-
return type
|
|
28
|
-
}
|
|
29
|
-
return 'ACTION' // default
|
|
14
|
+
const type = (group as { type?: TemplateStepGroupType }).type
|
|
15
|
+
return type === 'VALIDATION' ? 'VALIDATION' : 'ACTION'
|
|
30
16
|
}
|
|
31
17
|
|
|
32
|
-
import {
|
|
33
|
-
createTemplateStepGroupFile,
|
|
34
|
-
removeTemplateStepGroupFile,
|
|
35
|
-
renameTemplateStepGroupFile,
|
|
36
|
-
ensureGroupJSDoc,
|
|
37
|
-
} from '@/lib/utils/template-step-file-manager-intelligent'
|
|
38
|
-
import { promises as fs } from 'fs'
|
|
39
|
-
import { getFilePath, formatFileContent } from '@/lib/utils/template-step-file-generator'
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Get all template step groups
|
|
43
|
-
* @returns ActionResponse
|
|
44
|
-
*/
|
|
45
18
|
export async function getAllTemplateStepGroupsAction(): Promise<ActionResponse> {
|
|
46
19
|
try {
|
|
47
20
|
const templateStepGroups = await prisma.templateStepGroup.findMany()
|
|
@@ -49,20 +22,14 @@ export async function getAllTemplateStepGroupsAction(): Promise<ActionResponse>
|
|
|
49
22
|
status: 200,
|
|
50
23
|
data: templateStepGroups,
|
|
51
24
|
}
|
|
52
|
-
} catch (
|
|
25
|
+
} catch (error) {
|
|
53
26
|
return {
|
|
54
27
|
status: 500,
|
|
55
|
-
error: `Server error occurred: ${
|
|
28
|
+
error: `Server error occurred: ${error}`,
|
|
56
29
|
}
|
|
57
30
|
}
|
|
58
31
|
}
|
|
59
32
|
|
|
60
|
-
/**
|
|
61
|
-
* Create a new template step group
|
|
62
|
-
* @param _prev - Previous state
|
|
63
|
-
* @param value - Template step group data
|
|
64
|
-
* @returns ActionResponse
|
|
65
|
-
*/
|
|
66
33
|
export async function createTemplateStepGroupAction(
|
|
67
34
|
_prev: unknown,
|
|
68
35
|
value: z.infer<typeof templateStepGroupSchema>,
|
|
@@ -71,36 +38,32 @@ export async function createTemplateStepGroupAction(
|
|
|
71
38
|
templateStepGroupSchema.parse(value)
|
|
72
39
|
|
|
73
40
|
const type: TemplateStepGroupType = (value.type as string) === 'VALIDATION' ? 'VALIDATION' : 'ACTION'
|
|
74
|
-
|
|
75
|
-
// First, try to create the file - if this fails, we won't create the database record
|
|
76
|
-
await createTemplateStepGroupFile(value.name, type, value.description)
|
|
77
|
-
|
|
78
|
-
// If file creation succeeds, create the database record
|
|
79
|
-
// Note: Using type assertion because Prisma client hasn't been regenerated yet
|
|
80
|
-
await prisma.templateStepGroup.create({
|
|
41
|
+
const createdGroup = await prisma.templateStepGroup.create({
|
|
81
42
|
data: {
|
|
82
43
|
name: value.name,
|
|
83
44
|
description: value.description,
|
|
84
|
-
type
|
|
45
|
+
type,
|
|
85
46
|
} as Parameters<typeof prisma.templateStepGroup.create>[0]['data'],
|
|
86
47
|
})
|
|
87
48
|
|
|
49
|
+
await automationProjectionService.syncTemplateStepGroup(createdGroup.id)
|
|
50
|
+
|
|
88
51
|
revalidatePath('/template-step-groups')
|
|
89
52
|
return {
|
|
90
53
|
status: 200,
|
|
91
54
|
message: 'Template step group created successfully',
|
|
92
55
|
}
|
|
93
|
-
} catch (
|
|
94
|
-
if (
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if (error instanceof ZodError) {
|
|
95
58
|
return {
|
|
96
59
|
status: 400,
|
|
97
|
-
error:
|
|
60
|
+
error: error.message,
|
|
98
61
|
}
|
|
99
62
|
}
|
|
100
|
-
if (
|
|
63
|
+
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
|
101
64
|
return {
|
|
102
65
|
status: 500,
|
|
103
|
-
error:
|
|
66
|
+
error: error.message,
|
|
104
67
|
}
|
|
105
68
|
}
|
|
106
69
|
return {
|
|
@@ -110,54 +73,27 @@ export async function createTemplateStepGroupAction(
|
|
|
110
73
|
}
|
|
111
74
|
}
|
|
112
75
|
|
|
113
|
-
|
|
114
|
-
* Delete a template step group
|
|
115
|
-
* @param id - Template step group id(s)
|
|
116
|
-
* @returns ActionResponse
|
|
117
|
-
*/
|
|
118
|
-
export async function deleteTemplateStepGroupAction(id: string[]): Promise<ActionResponse> {
|
|
76
|
+
export async function deleteTemplateStepGroupAction(ids: string[]): Promise<ActionResponse> {
|
|
119
77
|
try {
|
|
120
|
-
|
|
121
|
-
const groupsToDelete = await prisma.templateStepGroup.findMany({
|
|
122
|
-
where: { id: { in: id } },
|
|
123
|
-
})
|
|
78
|
+
await Promise.all(ids.map(id => automationProjectionService.deleteTemplateStepGroup(id)))
|
|
124
79
|
|
|
125
|
-
// With proper cascade deletes in the schema, we can now rely on the database
|
|
126
|
-
// to handle the deletion of related entities automatically
|
|
127
80
|
await prisma.templateStepGroup.deleteMany({
|
|
128
|
-
where: { id: { in:
|
|
81
|
+
where: { id: { in: ids } },
|
|
129
82
|
})
|
|
130
83
|
|
|
131
|
-
// Clean up the files after successful database deletion
|
|
132
|
-
for (const group of groupsToDelete) {
|
|
133
|
-
try {
|
|
134
|
-
const groupType = getGroupType(group)
|
|
135
|
-
await removeTemplateStepGroupFile(group.name, groupType)
|
|
136
|
-
} catch (fileError) {
|
|
137
|
-
console.error(`Failed to delete file for group "${group.name}":`, fileError)
|
|
138
|
-
// Don't fail the entire operation if file deletion fails
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
84
|
revalidatePath('/template-step-groups')
|
|
143
85
|
return {
|
|
144
86
|
status: 200,
|
|
145
87
|
message: 'Template step group(s) deleted successfully',
|
|
146
88
|
}
|
|
147
|
-
} catch (
|
|
148
|
-
console.error('Error deleting template step group:', e)
|
|
89
|
+
} catch (error) {
|
|
149
90
|
return {
|
|
150
91
|
status: 500,
|
|
151
|
-
error: `Server error occurred: ${
|
|
92
|
+
error: `Server error occurred: ${error}`,
|
|
152
93
|
}
|
|
153
94
|
}
|
|
154
95
|
}
|
|
155
96
|
|
|
156
|
-
/**
|
|
157
|
-
* Get a template step group by id
|
|
158
|
-
* @param id - Template step group id
|
|
159
|
-
* @returns ActionResponse
|
|
160
|
-
*/
|
|
161
97
|
export async function getTemplateStepGroupByIdAction(id: string): Promise<ActionResponse> {
|
|
162
98
|
try {
|
|
163
99
|
const templateStepGroup = await prisma.templateStepGroup.findUnique({
|
|
@@ -167,19 +103,12 @@ export async function getTemplateStepGroupByIdAction(id: string): Promise<Action
|
|
|
167
103
|
status: 200,
|
|
168
104
|
data: templateStepGroup,
|
|
169
105
|
}
|
|
170
|
-
} catch (
|
|
171
|
-
console.error(
|
|
172
|
-
throw
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error(error)
|
|
108
|
+
throw error
|
|
173
109
|
}
|
|
174
110
|
}
|
|
175
111
|
|
|
176
|
-
/**
|
|
177
|
-
* Update a template step group
|
|
178
|
-
* @param _prev - Previous state
|
|
179
|
-
* @param value - Template step group data
|
|
180
|
-
* @param id - Template step group id
|
|
181
|
-
* @returns ActionResponse
|
|
182
|
-
*/
|
|
183
112
|
export async function updateTemplateStepGroupAction(
|
|
184
113
|
_prev: unknown,
|
|
185
114
|
value: z.infer<typeof templateStepGroupSchema>,
|
|
@@ -195,7 +124,6 @@ export async function updateTemplateStepGroupAction(
|
|
|
195
124
|
}
|
|
196
125
|
}
|
|
197
126
|
|
|
198
|
-
// Get the current group to check if name or type changed
|
|
199
127
|
const currentGroup = await prisma.templateStepGroup.findUnique({
|
|
200
128
|
where: { id },
|
|
201
129
|
})
|
|
@@ -209,40 +137,11 @@ export async function updateTemplateStepGroupAction(
|
|
|
209
137
|
|
|
210
138
|
const newType: TemplateStepGroupType = (value.type as string) === 'VALIDATION' ? 'VALIDATION' : 'ACTION'
|
|
211
139
|
const currentType = getGroupType(currentGroup)
|
|
212
|
-
const nameChanged = currentGroup.name !== value.name
|
|
213
|
-
const typeChanged = currentType !== newType
|
|
214
|
-
const descriptionChanged = currentGroup.description !== value.description
|
|
215
|
-
|
|
216
|
-
// If name or type changed, we need to handle file renaming/moving
|
|
217
|
-
if (nameChanged || typeChanged) {
|
|
218
|
-
// Rename/move the file to preserve all existing content and update JSDoc
|
|
219
|
-
try {
|
|
220
|
-
await renameTemplateStepGroupFile(currentGroup.name, value.name, currentType, newType, value.description)
|
|
221
|
-
} catch (fileError) {
|
|
222
|
-
console.error(
|
|
223
|
-
`Failed to rename/move file from "${currentGroup.name}" (${currentType}) to "${value.name}" (${newType}):`,
|
|
224
|
-
fileError,
|
|
225
|
-
)
|
|
226
|
-
// Continue with the update even if file rename fails
|
|
227
|
-
}
|
|
228
|
-
} else if (descriptionChanged) {
|
|
229
|
-
// Only description changed, update JSDoc in place
|
|
230
|
-
try {
|
|
231
|
-
const filePath = getFilePath(value.name, newType)
|
|
232
|
-
|
|
233
|
-
let fileContent = await fs.readFile(filePath, 'utf8')
|
|
234
|
-
fileContent = ensureGroupJSDoc(fileContent, value.name, value.description || null, newType)
|
|
235
140
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
} catch (fileError) {
|
|
239
|
-
console.error(`Failed to update JSDoc for group "${value.name}":`, fileError)
|
|
240
|
-
// Continue with the update even if file update fails
|
|
241
|
-
}
|
|
141
|
+
if (currentGroup.name !== value.name || currentType !== newType) {
|
|
142
|
+
await automationProjectionService.renameTemplateStepGroup(id, value.name, newType, value.description)
|
|
242
143
|
}
|
|
243
144
|
|
|
244
|
-
// Update the database record
|
|
245
|
-
// Note: Using type assertion because Prisma client hasn't been regenerated yet
|
|
246
145
|
await prisma.templateStepGroup.update({
|
|
247
146
|
where: { id },
|
|
248
147
|
data: {
|
|
@@ -252,22 +151,24 @@ export async function updateTemplateStepGroupAction(
|
|
|
252
151
|
} as Parameters<typeof prisma.templateStepGroup.update>[0]['data'],
|
|
253
152
|
})
|
|
254
153
|
|
|
154
|
+
await automationProjectionService.syncTemplateStepGroup(id)
|
|
155
|
+
|
|
255
156
|
revalidatePath('/template-step-groups')
|
|
256
157
|
return {
|
|
257
158
|
status: 200,
|
|
258
159
|
message: 'Template step group updated successfully',
|
|
259
160
|
}
|
|
260
|
-
} catch (
|
|
261
|
-
if (
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (error instanceof ZodError) {
|
|
262
163
|
return {
|
|
263
164
|
status: 400,
|
|
264
|
-
error:
|
|
165
|
+
error: error.message,
|
|
265
166
|
}
|
|
266
167
|
}
|
|
267
|
-
if (
|
|
168
|
+
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
|
268
169
|
return {
|
|
269
170
|
status: 500,
|
|
270
|
-
error:
|
|
171
|
+
error: error.message,
|
|
271
172
|
}
|
|
272
173
|
}
|
|
273
174
|
return {
|
|
@@ -4,16 +4,11 @@ import prisma from '@/config/db-config'
|
|
|
4
4
|
import { ActionResponse } from '@/types/form/actionHandler'
|
|
5
5
|
import { revalidatePath } from 'next/cache'
|
|
6
6
|
import { testCaseSchema } from '@/constants/form-opts/test-case-form-opts'
|
|
7
|
+
import { automationProjectionService } from '@/lib/automation/projection-service'
|
|
7
8
|
import { z } from 'zod'
|
|
8
|
-
import { generateFeatureFile } from '@/lib/feature-file-generator'
|
|
9
|
-
|
|
10
9
|
import { StepParameterType, TagType } from '@prisma/client'
|
|
11
10
|
import { generateUniqueTestCaseIdentifier } from '@/lib/test-case-utils'
|
|
12
11
|
|
|
13
|
-
/**
|
|
14
|
-
* Get all test cases
|
|
15
|
-
* @returns ActionResponse
|
|
16
|
-
*/
|
|
17
12
|
export async function getAllTestCasesAction(): Promise<ActionResponse> {
|
|
18
13
|
try {
|
|
19
14
|
const testCases = await prisma.testCase.findMany({
|
|
@@ -31,35 +26,27 @@ export async function getAllTestCasesAction(): Promise<ActionResponse> {
|
|
|
31
26
|
status: 200,
|
|
32
27
|
data: testCases,
|
|
33
28
|
}
|
|
34
|
-
} catch (
|
|
29
|
+
} catch (error) {
|
|
35
30
|
return {
|
|
36
31
|
status: 500,
|
|
37
|
-
error: `Server error occurred: ${
|
|
32
|
+
error: `Server error occurred: ${error}`,
|
|
38
33
|
}
|
|
39
34
|
}
|
|
40
35
|
}
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
* Delete a test case
|
|
44
|
-
* @param id - Test case id
|
|
45
|
-
* @returns ActionResponse
|
|
46
|
-
*/
|
|
47
|
-
export async function deleteTestCaseAction(id: string[]): Promise<ActionResponse> {
|
|
37
|
+
export async function deleteTestCaseAction(ids: string[]): Promise<ActionResponse> {
|
|
48
38
|
try {
|
|
49
|
-
// Get the test suites that will be affected before deletion
|
|
50
39
|
const affectedTestSuites = await prisma.testSuite.findMany({
|
|
51
40
|
where: {
|
|
52
41
|
testCases: {
|
|
53
42
|
some: {
|
|
54
43
|
id: {
|
|
55
|
-
in:
|
|
44
|
+
in: ids,
|
|
56
45
|
},
|
|
57
46
|
},
|
|
58
47
|
},
|
|
59
48
|
},
|
|
60
|
-
|
|
61
|
-
module: true,
|
|
62
|
-
},
|
|
49
|
+
select: { id: true },
|
|
63
50
|
})
|
|
64
51
|
|
|
65
52
|
const testCaseIdentifierTags = await prisma.tag.findMany({
|
|
@@ -68,7 +55,7 @@ export async function deleteTestCaseAction(id: string[]): Promise<ActionResponse
|
|
|
68
55
|
testCases: {
|
|
69
56
|
some: {
|
|
70
57
|
id: {
|
|
71
|
-
in:
|
|
58
|
+
in: ids,
|
|
72
59
|
},
|
|
73
60
|
},
|
|
74
61
|
},
|
|
@@ -79,96 +66,74 @@ export async function deleteTestCaseAction(id: string[]): Promise<ActionResponse
|
|
|
79
66
|
})
|
|
80
67
|
|
|
81
68
|
await prisma.$transaction(async tx => {
|
|
82
|
-
// Delete all test run test cases associated with the test cases
|
|
83
|
-
// Note: This has RESTRICT constraint in the database, so must be deleted first
|
|
84
69
|
await tx.testRunTestCase.deleteMany({
|
|
85
70
|
where: {
|
|
86
71
|
testCaseId: {
|
|
87
|
-
in:
|
|
72
|
+
in: ids,
|
|
88
73
|
},
|
|
89
74
|
},
|
|
90
75
|
})
|
|
91
76
|
|
|
92
|
-
// Delete all reviews associated with the test cases
|
|
93
77
|
await tx.review.deleteMany({
|
|
94
78
|
where: {
|
|
95
79
|
testCaseId: {
|
|
96
|
-
in:
|
|
80
|
+
in: ids,
|
|
97
81
|
},
|
|
98
82
|
},
|
|
99
83
|
})
|
|
100
84
|
|
|
101
|
-
// Delete all linked Jira tickets associated with the test cases
|
|
102
85
|
await tx.linkedJiraTicket.deleteMany({
|
|
103
86
|
where: {
|
|
104
87
|
testCaseId: {
|
|
105
|
-
in:
|
|
88
|
+
in: ids,
|
|
106
89
|
},
|
|
107
90
|
},
|
|
108
91
|
})
|
|
109
92
|
|
|
110
|
-
// Delete all step parameters associated with the test case steps
|
|
111
93
|
await tx.testCaseStepParameter.deleteMany({
|
|
112
94
|
where: {
|
|
113
95
|
testCaseStep: {
|
|
114
96
|
testCaseId: {
|
|
115
|
-
in:
|
|
97
|
+
in: ids,
|
|
116
98
|
},
|
|
117
99
|
},
|
|
118
100
|
},
|
|
119
101
|
})
|
|
120
102
|
|
|
121
|
-
// Delete all test case steps
|
|
122
103
|
await tx.testCaseStep.deleteMany({
|
|
123
104
|
where: {
|
|
124
105
|
testCaseId: {
|
|
125
|
-
in:
|
|
106
|
+
in: ids,
|
|
126
107
|
},
|
|
127
108
|
},
|
|
128
109
|
})
|
|
129
110
|
|
|
130
|
-
// Delete all test case identifier tags associated with the test cases
|
|
131
111
|
await tx.tag.deleteMany({
|
|
132
112
|
where: {
|
|
133
113
|
id: { in: testCaseIdentifierTags.map(tag => tag.id) },
|
|
134
114
|
},
|
|
135
115
|
})
|
|
136
116
|
|
|
137
|
-
// Delete the test cases
|
|
138
117
|
await tx.testCase.deleteMany({
|
|
139
|
-
where: { id: { in:
|
|
118
|
+
where: { id: { in: ids } },
|
|
140
119
|
})
|
|
141
120
|
})
|
|
142
121
|
|
|
143
|
-
|
|
144
|
-
for (const testSuite of affectedTestSuites) {
|
|
145
|
-
try {
|
|
146
|
-
await generateFeatureFile(testSuite.id, testSuite.name, testSuite.description || undefined)
|
|
147
|
-
} catch (featureFileError) {
|
|
148
|
-
console.error(`Error regenerating feature file for test suite ${testSuite.name}:`, featureFileError)
|
|
149
|
-
// Don't fail the deletion if feature file regeneration fails
|
|
150
|
-
}
|
|
151
|
-
}
|
|
122
|
+
await Promise.all(affectedTestSuites.map(testSuite => automationProjectionService.generateFeature(testSuite.id)))
|
|
152
123
|
|
|
153
124
|
revalidatePath('/test-cases')
|
|
154
125
|
return {
|
|
155
126
|
status: 200,
|
|
156
127
|
message: 'Test case(s) deleted successfully',
|
|
157
128
|
}
|
|
158
|
-
} catch (
|
|
159
|
-
console.error('Error deleting test case(s):', e)
|
|
129
|
+
} catch (error) {
|
|
160
130
|
return {
|
|
161
131
|
status: 500,
|
|
162
|
-
error: `Server error occurred: ${
|
|
132
|
+
error: `Server error occurred: ${error}`,
|
|
163
133
|
}
|
|
164
134
|
}
|
|
165
135
|
}
|
|
166
136
|
|
|
167
|
-
/**
|
|
168
|
-
* Create a test case
|
|
169
|
-
* @param testCase - Test case
|
|
170
|
-
* @returns ActionResponse
|
|
171
|
-
*/
|
|
172
137
|
export async function createTestCaseAction(value: z.infer<typeof testCaseSchema>): Promise<ActionResponse> {
|
|
173
138
|
try {
|
|
174
139
|
testCaseSchema.parse(value)
|
|
@@ -180,6 +145,7 @@ export async function createTestCaseAction(value: z.infer<typeof testCaseSchema>
|
|
|
180
145
|
tagExpression: `@${uniqueTestCaseIdentifier}`,
|
|
181
146
|
},
|
|
182
147
|
})
|
|
148
|
+
|
|
183
149
|
const baseData = {
|
|
184
150
|
title: value.title,
|
|
185
151
|
description: value.description ?? '',
|
|
@@ -205,7 +171,6 @@ export async function createTestCaseAction(value: z.infer<typeof testCaseSchema>
|
|
|
205
171
|
},
|
|
206
172
|
}
|
|
207
173
|
|
|
208
|
-
// Only include tags if there are tagIds provided
|
|
209
174
|
const data =
|
|
210
175
|
value.tagIds && value.tagIds.length > 0
|
|
211
176
|
? {
|
|
@@ -225,22 +190,14 @@ export async function createTestCaseAction(value: z.infer<typeof testCaseSchema>
|
|
|
225
190
|
data,
|
|
226
191
|
include: {
|
|
227
192
|
TestSuite: {
|
|
228
|
-
|
|
229
|
-
|
|
193
|
+
select: {
|
|
194
|
+
id: true,
|
|
230
195
|
},
|
|
231
196
|
},
|
|
232
197
|
},
|
|
233
198
|
})
|
|
234
199
|
|
|
235
|
-
|
|
236
|
-
for (const testSuite of newTestCase.TestSuite) {
|
|
237
|
-
try {
|
|
238
|
-
await generateFeatureFile(testSuite.id, testSuite.name, testSuite.description || undefined)
|
|
239
|
-
} catch (featureFileError) {
|
|
240
|
-
console.error(`Error regenerating feature file for test suite ${testSuite.name}:`, featureFileError)
|
|
241
|
-
// Don't fail the creation if feature file regeneration fails
|
|
242
|
-
}
|
|
243
|
-
}
|
|
200
|
+
await Promise.all(newTestCase.TestSuite.map(testSuite => automationProjectionService.generateFeature(testSuite.id)))
|
|
244
201
|
|
|
245
202
|
revalidatePath('/test-cases')
|
|
246
203
|
return {
|
|
@@ -248,19 +205,14 @@ export async function createTestCaseAction(value: z.infer<typeof testCaseSchema>
|
|
|
248
205
|
message: 'Test case created successfully',
|
|
249
206
|
data: newTestCase,
|
|
250
207
|
}
|
|
251
|
-
} catch (
|
|
208
|
+
} catch (error) {
|
|
252
209
|
return {
|
|
253
210
|
status: 500,
|
|
254
|
-
error: `Server error occurred: ${
|
|
211
|
+
error: `Server error occurred: ${error}`,
|
|
255
212
|
}
|
|
256
213
|
}
|
|
257
214
|
}
|
|
258
215
|
|
|
259
|
-
/**
|
|
260
|
-
* Get a test case by id
|
|
261
|
-
* @param id - Test case id
|
|
262
|
-
* @returns ActionResponse
|
|
263
|
-
*/
|
|
264
216
|
export async function getTestCaseByIdAction(id: string): Promise<ActionResponse> {
|
|
265
217
|
try {
|
|
266
218
|
const testCase = await prisma.testCase.findUnique({
|
|
@@ -294,10 +246,10 @@ export async function getTestCaseByIdAction(id: string): Promise<ActionResponse>
|
|
|
294
246
|
tagIds: testCase?.tags.map(tag => tag.id) || [],
|
|
295
247
|
},
|
|
296
248
|
}
|
|
297
|
-
} catch (
|
|
249
|
+
} catch (error) {
|
|
298
250
|
return {
|
|
299
251
|
status: 500,
|
|
300
|
-
error: `Server error occurred: ${
|
|
252
|
+
error: `Server error occurred: ${error}`,
|
|
301
253
|
}
|
|
302
254
|
}
|
|
303
255
|
}
|
|
@@ -310,40 +262,35 @@ export async function updateTestCaseAction(
|
|
|
310
262
|
throw new Error("updateTestCaseAction: 'id' parameter is required for updating a test case.")
|
|
311
263
|
}
|
|
312
264
|
try {
|
|
313
|
-
// Get the test suites that will be affected before updating
|
|
314
265
|
const affectedTestSuites = await prisma.testSuite.findMany({
|
|
315
266
|
where: {
|
|
316
267
|
testCases: {
|
|
317
268
|
some: {
|
|
318
|
-
id
|
|
269
|
+
id,
|
|
319
270
|
},
|
|
320
271
|
},
|
|
321
272
|
},
|
|
322
|
-
|
|
323
|
-
|
|
273
|
+
select: {
|
|
274
|
+
id: true,
|
|
324
275
|
},
|
|
325
276
|
})
|
|
326
277
|
|
|
327
|
-
// 1. Find all step IDs for the test case
|
|
328
278
|
const steps = await prisma.testCaseStep.findMany({
|
|
329
279
|
where: { testCaseId: id },
|
|
330
280
|
select: { id: true },
|
|
331
281
|
})
|
|
332
282
|
const stepIds = steps.map(step => step.id)
|
|
333
283
|
|
|
334
|
-
// 2. Delete all parameters for those steps
|
|
335
284
|
if (stepIds.length > 0) {
|
|
336
285
|
await prisma.testCaseStepParameter.deleteMany({
|
|
337
286
|
where: { testCaseStepId: { in: stepIds } },
|
|
338
287
|
})
|
|
339
288
|
}
|
|
340
289
|
|
|
341
|
-
// 3. Delete all steps for the test case
|
|
342
290
|
await prisma.testCaseStep.deleteMany({
|
|
343
291
|
where: { testCaseId: id },
|
|
344
292
|
})
|
|
345
293
|
|
|
346
|
-
// 4. Get existing IDENTIFIER tags to preserve them
|
|
347
294
|
const existingTestCase = await prisma.testCase.findUnique({
|
|
348
295
|
where: { id },
|
|
349
296
|
include: {
|
|
@@ -358,19 +305,17 @@ export async function updateTestCaseAction(
|
|
|
358
305
|
},
|
|
359
306
|
})
|
|
360
307
|
|
|
361
|
-
// 5. Combine IDENTIFIER tags with FILTER tags from the form
|
|
362
308
|
const identifierTagIds = existingTestCase?.tags.map(tag => tag.id) || []
|
|
363
309
|
const filterTagIds = value.tagIds || []
|
|
364
310
|
const allTagIds = [...identifierTagIds, ...filterTagIds]
|
|
365
311
|
|
|
366
|
-
// 6. Then, update the test case with new steps
|
|
367
312
|
const testCase = await prisma.testCase.update({
|
|
368
313
|
where: { id },
|
|
369
314
|
data: {
|
|
370
315
|
title: value.title,
|
|
371
316
|
description: value.description ?? '',
|
|
372
317
|
tags: {
|
|
373
|
-
set: allTagIds.map(
|
|
318
|
+
set: allTagIds.map(tagId => ({ id: tagId })),
|
|
374
319
|
},
|
|
375
320
|
steps: {
|
|
376
321
|
create: value.steps.map(step => ({
|
|
@@ -395,25 +340,17 @@ export async function updateTestCaseAction(
|
|
|
395
340
|
},
|
|
396
341
|
})
|
|
397
342
|
|
|
398
|
-
|
|
399
|
-
for (const testSuite of affectedTestSuites) {
|
|
400
|
-
try {
|
|
401
|
-
await generateFeatureFile(testSuite.id, testSuite.name, testSuite.description || undefined)
|
|
402
|
-
} catch (featureFileError) {
|
|
403
|
-
console.error(`Error regenerating feature file for test suite ${testSuite.name}:`, featureFileError)
|
|
404
|
-
// Don't fail the update if feature file regeneration fails
|
|
405
|
-
}
|
|
406
|
-
}
|
|
343
|
+
await Promise.all(affectedTestSuites.map(testSuite => automationProjectionService.generateFeature(testSuite.id)))
|
|
407
344
|
|
|
408
345
|
return {
|
|
409
346
|
status: 200,
|
|
410
347
|
message: 'Test case updated successfully',
|
|
411
348
|
data: testCase,
|
|
412
349
|
}
|
|
413
|
-
} catch (
|
|
350
|
+
} catch (error) {
|
|
414
351
|
return {
|
|
415
352
|
status: 500,
|
|
416
|
-
error: `Server error occurred: ${
|
|
353
|
+
error: `Server error occurred: ${error}`,
|
|
417
354
|
}
|
|
418
355
|
}
|
|
419
356
|
}
|
|
@@ -12,8 +12,7 @@ import {
|
|
|
12
12
|
TagType,
|
|
13
13
|
Tag,
|
|
14
14
|
} from '@prisma/client'
|
|
15
|
-
import {
|
|
16
|
-
import { waitForTask, taskSpawner, killTask } from '@/tests/utils/spawner.util'
|
|
15
|
+
import { localExecutorAdapter } from '@/lib/executor/local-executor-adapter'
|
|
17
16
|
import { revalidatePath } from 'next/cache'
|
|
18
17
|
import { formatLogsForStorage, parseLogsFromStorage, type LogEntry } from '@/lib/test-run/log-formatter'
|
|
19
18
|
import { processManager } from '@/lib/test-run/process-manager'
|
|
@@ -471,7 +470,7 @@ export async function createTestRunAction(
|
|
|
471
470
|
|
|
472
471
|
// Execute test run asynchronously (don't await, let it run in background)
|
|
473
472
|
try {
|
|
474
|
-
const { process: spawnedProcess, reportPath } = await executeTestRun({
|
|
473
|
+
const { process: spawnedProcess, reportPath } = await localExecutorAdapter.executeTestRun({
|
|
475
474
|
testRunId: testRun.runId,
|
|
476
475
|
environment,
|
|
477
476
|
tags,
|
|
@@ -533,7 +532,7 @@ export async function createTestRunAction(
|
|
|
533
532
|
executePromise
|
|
534
533
|
.then(async spawnedProcess => {
|
|
535
534
|
// Wait for process to complete
|
|
536
|
-
const exitCode = await
|
|
535
|
+
const exitCode = await localExecutorAdapter.waitForProcess(spawnedProcess.name)
|
|
537
536
|
|
|
538
537
|
// Collect all logs from the process output
|
|
539
538
|
const logEntries: LogEntry[] = []
|
|
@@ -906,7 +905,7 @@ export async function checkTraceViewerStatusAction(testRunId: string, testCaseId
|
|
|
906
905
|
|
|
907
906
|
// Check if trace viewer process is running
|
|
908
907
|
const processName = `trace-viewer-${testCaseId}`
|
|
909
|
-
const process =
|
|
908
|
+
const process = localExecutorAdapter.getProcess(processName)
|
|
910
909
|
const isRunning = process?.isRunning ?? false
|
|
911
910
|
|
|
912
911
|
return {
|
|
@@ -989,12 +988,7 @@ export async function spawnTraceViewerAction(testRunId: string, testCaseId: stri
|
|
|
989
988
|
|
|
990
989
|
// Spawn playwright show-trace command
|
|
991
990
|
// The process is self-closing when the user closes the trace viewer
|
|
992
|
-
const spawnedProcess = await
|
|
993
|
-
streamLogs: true,
|
|
994
|
-
prefixLogs: true,
|
|
995
|
-
logPrefix: `trace-viewer-${testCaseId}`,
|
|
996
|
-
captureOutput: false, // No need to capture output for trace viewer
|
|
997
|
-
})
|
|
991
|
+
const spawnedProcess = await localExecutorAdapter.spawnTraceViewer(testCaseId, absoluteTracePath)
|
|
998
992
|
|
|
999
993
|
console.log(
|
|
1000
994
|
`[TestRunAction] Spawned trace viewer process for testCaseId: ${testCaseId}, tracePath: ${absoluteTracePath}`,
|
|
@@ -1077,10 +1071,10 @@ export async function cancelTestRunAction(testRunId: string): Promise<ActionResp
|
|
|
1077
1071
|
}
|
|
1078
1072
|
}
|
|
1079
1073
|
|
|
1080
|
-
const killed =
|
|
1074
|
+
const killed = localExecutorAdapter.killProcess(process.name, 'SIGTERM')
|
|
1081
1075
|
console.log(`[TestRunAction] Killed: ${killed}`)
|
|
1082
1076
|
if (!killed) {
|
|
1083
|
-
const forceKilled =
|
|
1077
|
+
const forceKilled = localExecutorAdapter.killProcess(process.name, 'SIGKILL')
|
|
1084
1078
|
if (!forceKilled) {
|
|
1085
1079
|
console.warn(`[TestRunAction] Failed to force kill process for testRunId: ${testRunId}`)
|
|
1086
1080
|
}
|
|
@@ -1183,3 +1177,7 @@ export async function checkTestRunNameUniqueAction(name: string, excludeId?: str
|
|
|
1183
1177
|
}
|
|
1184
1178
|
}
|
|
1185
1179
|
}
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
|