create-appraisejs 0.1.9 → 0.1.10-alpha.1
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 +12 -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 +124 -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
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker'
|
|
2
|
+
|
|
3
|
+
export enum RandomDataType {
|
|
4
|
+
FULL_NAME = 'fullName',
|
|
5
|
+
FIRST_NAME = 'firstName',
|
|
6
|
+
LAST_NAME = 'lastName',
|
|
7
|
+
EMAIL = 'email',
|
|
8
|
+
PASSWORD = 'password',
|
|
9
|
+
PHONE = 'phone',
|
|
10
|
+
ADDRESS = 'address',
|
|
11
|
+
UNIQUE_TEXT = 'uniqueText',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function generateRandomData(randomDataType: RandomDataType): string {
|
|
15
|
+
switch (randomDataType) {
|
|
16
|
+
case RandomDataType.FULL_NAME:
|
|
17
|
+
return faker.person.fullName()
|
|
18
|
+
case RandomDataType.FIRST_NAME:
|
|
19
|
+
return faker.person.firstName()
|
|
20
|
+
case RandomDataType.LAST_NAME:
|
|
21
|
+
return faker.person.lastName()
|
|
22
|
+
case RandomDataType.EMAIL:
|
|
23
|
+
return faker.internet.email()
|
|
24
|
+
case RandomDataType.PASSWORD:
|
|
25
|
+
return faker.internet.password()
|
|
26
|
+
case RandomDataType.PHONE:
|
|
27
|
+
return faker.phone.number()
|
|
28
|
+
case RandomDataType.ADDRESS:
|
|
29
|
+
return faker.location.streetAddress()
|
|
30
|
+
case RandomDataType.UNIQUE_TEXT:
|
|
31
|
+
return faker.string.uuid()
|
|
32
|
+
default:
|
|
33
|
+
throw new Error(`Invalid random data type: ${randomDataType}`)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type BrowserName = 'chromium' | 'firefox' | 'webkit'
|
|
2
|
+
|
|
3
|
+
export type CSSSelector = string
|
|
4
|
+
export type XPathSelector = `/${string}` | `//${string}`
|
|
5
|
+
export type SelectorName = string
|
|
6
|
+
export type Selector = CSSSelector | XPathSelector
|
|
7
|
+
export type Locator = Record<string, Selector>
|
|
8
|
+
export type LocatorMap = {
|
|
9
|
+
name: string
|
|
10
|
+
path: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type LocatorCollection = Record<string, Locator>
|
package/templates/default/{src/tests/config/executor → packages/cucumber-runtime/src}/world.ts
RENAMED
|
@@ -9,12 +9,14 @@ export interface ScenarioData {
|
|
|
9
9
|
token?: string
|
|
10
10
|
vars: Record<string, unknown>
|
|
11
11
|
}
|
|
12
|
+
|
|
12
13
|
export class CustomWorld extends World {
|
|
13
14
|
context!: BrowserContext
|
|
14
15
|
page!: Page
|
|
15
16
|
data: ScenarioData = {
|
|
16
17
|
vars: {},
|
|
17
18
|
}
|
|
19
|
+
|
|
18
20
|
constructor(options: IWorldOptions) {
|
|
19
21
|
super(options)
|
|
20
22
|
}
|
|
@@ -27,6 +29,7 @@ export class CustomWorld extends World {
|
|
|
27
29
|
if (!(key in this.data.vars)) {
|
|
28
30
|
throw new Error(`Variable ${key} not found`)
|
|
29
31
|
}
|
|
32
|
+
|
|
30
33
|
return this.data.vars[key] as T
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -36,6 +39,6 @@ export class CustomWorld extends World {
|
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
setWorldConstructor(CustomWorld)
|
|
39
|
-
|
|
40
42
|
chai.use(chaiAsPromised)
|
|
43
|
+
|
|
41
44
|
export const expect = chai.expect
|
|
@@ -4,16 +4,16 @@ import fs from 'fs'
|
|
|
4
4
|
import path from 'path'
|
|
5
5
|
|
|
6
6
|
const envContent = `# Database configuration for local development
|
|
7
|
-
DATABASE_URL="file:./
|
|
7
|
+
DATABASE_URL="file:./dev.db"
|
|
8
8
|
`
|
|
9
9
|
|
|
10
10
|
const envPath = path.join(process.cwd(), '.env')
|
|
11
11
|
|
|
12
12
|
if (!fs.existsSync(envPath)) {
|
|
13
13
|
fs.writeFileSync(envPath, envContent)
|
|
14
|
-
console.log('
|
|
14
|
+
console.log('? Created .env file with SQLite configuration')
|
|
15
15
|
} else {
|
|
16
|
-
console.log('
|
|
16
|
+
console.log('?? .env file already exists, skipping creation')
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
console.log('
|
|
19
|
+
console.log('?? Environment setup complete!')
|
|
@@ -1,103 +1,113 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* Sync the default template from the base app (repo root).
|
|
4
|
-
* Copies src/, prisma/, public/, scripts/,
|
|
5
|
-
*
|
|
4
|
+
* Copies src/, automation/, prisma/, public/, scripts/, the cucumber runtime package,
|
|
5
|
+
* and root config files into templates/default/ while preserving template-only files.
|
|
6
6
|
*
|
|
7
7
|
* Usage: npx tsx scripts/sync-appraise-base-template.ts
|
|
8
8
|
* Or: npm run sync-template
|
|
9
9
|
*/
|
|
10
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'fs'
|
|
11
|
+
import { dirname, join } from 'path'
|
|
12
|
+
import { fileURLToPath } from 'url'
|
|
13
|
+
import { shouldBackfillLegacyEnvironmentConfig, shouldExcludeTemplatePath } from '../src/lib/template-sync-utils'
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
-
const repoRoot = join(__dirname, '..');
|
|
17
|
-
const target = join(repoRoot, 'templates', 'default');
|
|
18
|
-
|
|
19
|
-
const EXCLUDED_DIRS = new Set(['node_modules', '.next', '.git']);
|
|
20
|
-
const EXCLUDED_EXTENSIONS = new Set(['.db', '.sqlite', '.sqlite3']);
|
|
21
|
-
const EXCLUDED_TEST_DATA_PREFIXES = [
|
|
22
|
-
'tests/features/',
|
|
23
|
-
'tests/config/environments/',
|
|
24
|
-
'tests/locators/',
|
|
25
|
-
'tests/reports/',
|
|
26
|
-
];
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
16
|
+
const repoRoot = join(__dirname, '..')
|
|
17
|
+
const target = join(repoRoot, 'templates', 'default')
|
|
27
18
|
|
|
28
19
|
function shouldExclude(relativePath: string): boolean {
|
|
29
|
-
|
|
30
|
-
if (parts.some((p) => EXCLUDED_DIRS.has(p))) return true;
|
|
31
|
-
if (EXCLUDED_TEST_DATA_PREFIXES.some((prefix) => relativePath.startsWith(prefix))) return true;
|
|
32
|
-
const ext = relativePath.endsWith('.sqlite3')
|
|
33
|
-
? '.sqlite3'
|
|
34
|
-
: relativePath.endsWith('.sqlite')
|
|
35
|
-
? '.sqlite'
|
|
36
|
-
: relativePath.slice(relativePath.lastIndexOf('.'));
|
|
37
|
-
if (EXCLUDED_EXTENSIONS.has(ext)) return true;
|
|
38
|
-
return false;
|
|
20
|
+
return shouldExcludeTemplatePath(relativePath)
|
|
39
21
|
}
|
|
40
22
|
|
|
41
|
-
function copyDirWithFilter(src: string, dest: string): void {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
23
|
+
function copyDirWithFilter(src: string, dest: string, base = src): void {
|
|
24
|
+
if (!existsSync(src)) return
|
|
25
|
+
|
|
26
|
+
mkdirSync(dest, { recursive: true })
|
|
27
|
+
for (const entry of readdirSync(src, { withFileTypes: true })) {
|
|
28
|
+
const srcPath = join(src, entry.name)
|
|
29
|
+
const relativePath = srcPath.slice(base.length + 1).replace(/\\/g, '/')
|
|
30
|
+
if (shouldExclude(relativePath)) continue
|
|
31
|
+
|
|
32
|
+
const destPath = join(dest, entry.name)
|
|
33
|
+
if (entry.isDirectory()) {
|
|
34
|
+
copyDirWithFilter(srcPath, destPath, base)
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
cpSync(srcPath, destPath, { force: true })
|
|
39
|
+
}
|
|
50
40
|
}
|
|
51
41
|
|
|
52
42
|
function copyFile(src: string, dest: string): void {
|
|
53
|
-
mkdirSync(dirname(dest), { recursive: true })
|
|
54
|
-
cpSync(src, dest, { force: true })
|
|
43
|
+
mkdirSync(dirname(dest), { recursive: true })
|
|
44
|
+
cpSync(src, dest, { force: true })
|
|
55
45
|
}
|
|
56
46
|
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
join(target, 'src', 'tests', 'features'),
|
|
72
|
-
join(target, 'src', 'tests', 'locators'),
|
|
73
|
-
join(target, 'src', 'tests', 'reports'),
|
|
74
|
-
];
|
|
75
|
-
for (const dir of dirsToClear) {
|
|
76
|
-
if (existsSync(dir)) {
|
|
77
|
-
rmSync(dir, { recursive: true });
|
|
47
|
+
function resetAutomationReports(templateRoot: string): void {
|
|
48
|
+
const reportsRoot = join(templateRoot, 'automation', 'reports')
|
|
49
|
+
rmSync(reportsRoot, { recursive: true, force: true })
|
|
50
|
+
mkdirSync(join(reportsRoot, 'logs'), { recursive: true })
|
|
51
|
+
mkdirSync(join(reportsRoot, 'traces'), { recursive: true })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function syncLegacyEnvironmentConfig(): void {
|
|
55
|
+
const legacyEnvironmentsDir = join(repoRoot, 'src', 'tests', 'config', 'environments')
|
|
56
|
+
const targetEnvironmentsDir = join(target, 'automation', 'config', 'environments')
|
|
57
|
+
const targetEnvironmentsFile = join(targetEnvironmentsDir, 'environments.json')
|
|
58
|
+
|
|
59
|
+
if (!shouldBackfillLegacyEnvironmentConfig(existsSync(targetEnvironmentsFile), existsSync(legacyEnvironmentsDir))) {
|
|
60
|
+
return
|
|
78
61
|
}
|
|
62
|
+
|
|
63
|
+
mkdirSync(targetEnvironmentsDir, { recursive: true })
|
|
64
|
+
cpSync(legacyEnvironmentsDir, targetEnvironmentsDir, { recursive: true, force: true })
|
|
65
|
+
console.log('Backfilled automation/config/environments from legacy src/tests config.')
|
|
79
66
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
join(target, '
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
67
|
+
|
|
68
|
+
function syncCucumberRuntimePackage(): void {
|
|
69
|
+
const runtimeTarget = join(target, 'packages', 'cucumber-runtime')
|
|
70
|
+
rmSync(runtimeTarget, { recursive: true, force: true })
|
|
71
|
+
mkdirSync(runtimeTarget, { recursive: true })
|
|
72
|
+
|
|
73
|
+
copyFile(join(repoRoot, 'packages', 'cucumber-runtime', 'package.json'), join(runtimeTarget, 'package.json'))
|
|
74
|
+
copyFile(join(repoRoot, 'packages', 'cucumber-runtime', 'tsconfig.json'), join(runtimeTarget, 'tsconfig.json'))
|
|
75
|
+
copyDirWithFilter(join(repoRoot, 'packages', 'cucumber-runtime', 'src'), join(runtimeTarget, 'src'))
|
|
89
76
|
}
|
|
90
|
-
writeFileSync(join(target, 'src', 'tests', 'config', 'environments', 'environments.json'), JSON.stringify({}) + '\n');
|
|
91
|
-
writeFileSync(join(target, 'src', 'tests', 'mapping', 'locator-map.json'), JSON.stringify([]) + '\n');
|
|
92
77
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
78
|
+
const readmePath = join(target, 'README.md')
|
|
79
|
+
const appraisejsConfigPath = join(target, 'appraisejs.config.json')
|
|
80
|
+
const savedReadme = existsSync(readmePath) ? readFileSync(readmePath, 'utf8') : null
|
|
81
|
+
const savedAppraisejsConfig = existsSync(appraisejsConfigPath)
|
|
82
|
+
? readFileSync(appraisejsConfigPath, 'utf8')
|
|
83
|
+
: null
|
|
84
|
+
|
|
85
|
+
rmSync(target, { recursive: true, force: true })
|
|
86
|
+
mkdirSync(target, { recursive: true })
|
|
87
|
+
|
|
88
|
+
console.log('Copying src/...')
|
|
89
|
+
copyDirWithFilter(join(repoRoot, 'src'), join(target, 'src'))
|
|
90
|
+
|
|
91
|
+
console.log('Copying automation/...')
|
|
92
|
+
copyDirWithFilter(join(repoRoot, 'automation'), join(target, 'automation'))
|
|
93
|
+
syncLegacyEnvironmentConfig()
|
|
94
|
+
resetAutomationReports(target)
|
|
95
|
+
|
|
96
|
+
console.log('Copying cucumber runtime package...')
|
|
97
|
+
syncCucumberRuntimePackage()
|
|
98
|
+
|
|
99
|
+
console.log('Copying prisma/...')
|
|
100
|
+
copyDirWithFilter(join(repoRoot, 'prisma'), join(target, 'prisma'))
|
|
101
|
+
console.log('Copying public/...')
|
|
102
|
+
copyDirWithFilter(join(repoRoot, 'public'), join(target, 'public'))
|
|
103
|
+
console.log('Copying scripts/...')
|
|
104
|
+
copyDirWithFilter(join(repoRoot, 'scripts'), join(target, 'scripts'))
|
|
105
|
+
|
|
106
|
+
const legacyTestsRoot = join(target, 'src', 'tests')
|
|
107
|
+
if (existsSync(legacyTestsRoot)) {
|
|
108
|
+
rmSync(legacyTestsRoot, { recursive: true, force: true })
|
|
109
|
+
}
|
|
99
110
|
|
|
100
|
-
// 3. Copy root config files and package lock files
|
|
101
111
|
const configFiles = [
|
|
102
112
|
'.gitignore',
|
|
103
113
|
'eslint.config.mjs',
|
|
@@ -106,51 +116,60 @@ const configFiles = [
|
|
|
106
116
|
'postcss.config.mjs',
|
|
107
117
|
'components.json',
|
|
108
118
|
'next.config.ts',
|
|
119
|
+
'next-env.d.ts',
|
|
109
120
|
'.env.example',
|
|
110
121
|
'package-lock.json',
|
|
111
122
|
'yarn.lock',
|
|
112
123
|
'pnpm-lock.yaml',
|
|
113
|
-
|
|
114
|
-
|
|
124
|
+
'bun.lockb',
|
|
125
|
+
]
|
|
115
126
|
for (const name of configFiles) {
|
|
116
|
-
const src = join(repoRoot, name)
|
|
127
|
+
const src = join(repoRoot, name)
|
|
117
128
|
if (existsSync(src)) {
|
|
118
|
-
copyFile(src, join(target, name))
|
|
129
|
+
copyFile(src, join(target, name))
|
|
119
130
|
}
|
|
120
131
|
}
|
|
121
132
|
|
|
122
|
-
|
|
123
|
-
const cucumberSource = join(repoRoot, 'cucumber.mjs');
|
|
133
|
+
const cucumberSource = join(repoRoot, 'cucumber.mjs')
|
|
124
134
|
if (existsSync(cucumberSource)) {
|
|
125
|
-
copyFile(cucumberSource, join(target, 'cucumber.mjs'))
|
|
126
|
-
console.log('Synced cucumber.mjs to template')
|
|
135
|
+
copyFile(cucumberSource, join(target, 'cucumber.mjs'))
|
|
136
|
+
console.log('Synced cucumber.mjs to template')
|
|
127
137
|
}
|
|
128
138
|
|
|
129
|
-
|
|
130
|
-
const vscodeSource = join(repoRoot, '.vscode');
|
|
139
|
+
const vscodeSource = join(repoRoot, '.vscode')
|
|
131
140
|
if (existsSync(vscodeSource)) {
|
|
132
|
-
cpSync(vscodeSource, join(target, '.vscode'), { recursive: true, force: true })
|
|
133
|
-
console.log('Synced .vscode to template')
|
|
141
|
+
cpSync(vscodeSource, join(target, '.vscode'), { recursive: true, force: true })
|
|
142
|
+
console.log('Synced .vscode to template')
|
|
134
143
|
}
|
|
135
144
|
|
|
136
|
-
// 4. package.json: base + template-only scripts
|
|
137
145
|
const rootPkg = JSON.parse(readFileSync(join(repoRoot, 'package.json'), 'utf8')) as {
|
|
138
|
-
scripts: Record<string, string
|
|
139
|
-
[key: string]: unknown
|
|
140
|
-
}
|
|
141
|
-
rootPkg.scripts
|
|
142
|
-
rootPkg.scripts
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
scripts: Record<string, string>
|
|
147
|
+
[key: string]: unknown
|
|
148
|
+
}
|
|
149
|
+
rootPkg.scripts = {
|
|
150
|
+
...rootPkg.scripts,
|
|
151
|
+
build: 'npm run build:local',
|
|
152
|
+
'build:local': 'npm run generate-db-client && npm run build:cucumber-runtime && next build',
|
|
153
|
+
start: 'next start',
|
|
154
|
+
'generate-db-client': 'npx prisma generate --schema prisma/schema.prisma',
|
|
155
|
+
'migrate-db': 'npx prisma migrate deploy',
|
|
156
|
+
'install-playwright': 'npx playwright install',
|
|
157
|
+
setup: 'npm run install-dependencies && npm run setup:db && npm run build:local',
|
|
158
|
+
'setup:db': 'npm run setup-env && npm run generate-db-client && npm run migrate-db && npm run sync-all',
|
|
159
|
+
'setup:full': 'npm run install-dependencies && npm run setup:db && npm run build:local',
|
|
160
|
+
'appraisejs:setup': 'npm run setup',
|
|
161
|
+
'appraisejs:sync': 'npm run sync-all',
|
|
162
|
+
}
|
|
163
|
+
writeFileSync(join(target, 'package.json'), JSON.stringify(rootPkg, null, 2) + '\n')
|
|
164
|
+
console.log('Wrote template package.json with production-first scaffold scripts.')
|
|
165
|
+
|
|
147
166
|
if (savedReadme) {
|
|
148
|
-
writeFileSync(readmePath, savedReadme)
|
|
149
|
-
console.log('Restored README.md')
|
|
167
|
+
writeFileSync(readmePath, savedReadme)
|
|
168
|
+
console.log('Restored README.md')
|
|
150
169
|
}
|
|
151
170
|
if (savedAppraisejsConfig) {
|
|
152
|
-
writeFileSync(appraisejsConfigPath, savedAppraisejsConfig)
|
|
153
|
-
console.log('Restored appraisejs.config.json')
|
|
171
|
+
writeFileSync(appraisejsConfigPath, savedAppraisejsConfig)
|
|
172
|
+
console.log('Restored appraisejs.config.json')
|
|
154
173
|
}
|
|
155
174
|
|
|
156
|
-
console.log('Synced base app to templates/default.')
|
|
175
|
+
console.log('Synced base app to templates/default with starter automation assets.')
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { promises as fs } from 'fs'
|
|
12
|
-
import { join } from 'path'
|
|
13
12
|
import prisma from '../src/config/db-config'
|
|
13
|
+
import { ensureAutomationWorkspaceReady, getAutomationEnvironmentsDir } from '../src/lib/automation/paths'
|
|
14
14
|
|
|
15
15
|
interface EnvironmentConfig {
|
|
16
16
|
baseUrl: string
|
|
@@ -43,8 +43,8 @@ interface SyncResult {
|
|
|
43
43
|
/**
|
|
44
44
|
* Reads and parses the environments.json file
|
|
45
45
|
*/
|
|
46
|
-
async function readEnvironmentsFromFile(
|
|
47
|
-
const filePath =
|
|
46
|
+
async function readEnvironmentsFromFile(): Promise<Record<string, EnvironmentConfig>> {
|
|
47
|
+
const filePath = `${getAutomationEnvironmentsDir()}/environments.json`
|
|
48
48
|
|
|
49
49
|
try {
|
|
50
50
|
await fs.access(filePath)
|
|
@@ -285,11 +285,11 @@ async function main() {
|
|
|
285
285
|
console.log('🔄 Starting environments sync...')
|
|
286
286
|
console.log('This will scan environments.json and sync environments to database.\n')
|
|
287
287
|
|
|
288
|
-
|
|
288
|
+
await ensureAutomationWorkspaceReady()
|
|
289
289
|
|
|
290
290
|
// Read environments from file
|
|
291
291
|
console.log('📁 Reading environments.json...')
|
|
292
|
-
const jsonContent = await readEnvironmentsFromFile(
|
|
292
|
+
const jsonContent = await readEnvironmentsFromFile()
|
|
293
293
|
const environmentKeys = Object.keys(jsonContent)
|
|
294
294
|
console.log(` Found ${environmentKeys.length} environment(s): ${environmentKeys.join(', ') || 'none'}`)
|
|
295
295
|
|
|
@@ -321,3 +321,6 @@ async function main() {
|
|
|
321
321
|
|
|
322
322
|
main()
|
|
323
323
|
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
@@ -14,6 +14,7 @@ import { join } from 'path'
|
|
|
14
14
|
import { glob } from 'glob'
|
|
15
15
|
import prisma from '../src/config/db-config'
|
|
16
16
|
import { findModuleByPath, buildModuleHierarchy } from '../src/lib/module-hierarchy-builder'
|
|
17
|
+
import { extractModulePathFromAutomationFile, getAutomationLocatorMapPath } from '../src/lib/template-sync-utils'
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Represents a locator group from the filesystem
|
|
@@ -52,7 +53,7 @@ interface SyncResult {
|
|
|
52
53
|
* Reads and parses the locator-map.json file
|
|
53
54
|
*/
|
|
54
55
|
async function readLocatorMap(baseDir: string): Promise<LocatorMapEntry[]> {
|
|
55
|
-
const locatorMapPath =
|
|
56
|
+
const locatorMapPath = getAutomationLocatorMapPath(baseDir)
|
|
56
57
|
|
|
57
58
|
try {
|
|
58
59
|
await fs.access(locatorMapPath)
|
|
@@ -82,7 +83,7 @@ async function readLocatorMap(baseDir: string): Promise<LocatorMapEntry[]> {
|
|
|
82
83
|
* Scans the locators directory to find all locator group files
|
|
83
84
|
*/
|
|
84
85
|
async function scanLocatorGroupFiles(baseDir: string): Promise<string[]> {
|
|
85
|
-
const pattern = '
|
|
86
|
+
const pattern = 'automation/locators/**/*.json'
|
|
86
87
|
|
|
87
88
|
try {
|
|
88
89
|
const files = await glob(pattern, {
|
|
@@ -96,15 +97,11 @@ async function scanLocatorGroupFiles(baseDir: string): Promise<string[]> {
|
|
|
96
97
|
|
|
97
98
|
/**
|
|
98
99
|
* Extracts module path from locator file path
|
|
99
|
-
* Example:
|
|
100
|
-
* Example:
|
|
100
|
+
* Example: automation/locators/home/home.json -> /home
|
|
101
|
+
* Example: automation/locators/users/admins/directors.json -> /users/admins
|
|
101
102
|
*/
|
|
102
103
|
function extractModulePathFromLocatorFile(filePath: string, baseDir: string): string {
|
|
103
|
-
|
|
104
|
-
const relativePath = filePath.replace(testsDir, '').replace(/\\/g, '/')
|
|
105
|
-
const pathParts = relativePath.split('/').filter(p => p && p !== 'locators')
|
|
106
|
-
const moduleParts = pathParts.slice(0, -1) // Remove filename
|
|
107
|
-
return moduleParts.length > 0 ? '/' + moduleParts.join('/') : '/'
|
|
104
|
+
return extractModulePathFromAutomationFile(filePath, baseDir, 'locators')
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
/**
|
|
@@ -374,7 +371,7 @@ async function main() {
|
|
|
374
371
|
console.log(` Found ${locatorMap.length} entry(ies) in locator map`)
|
|
375
372
|
|
|
376
373
|
// Build locator groups from filesystem
|
|
377
|
-
console.log('\n📁 Scanning
|
|
374
|
+
console.log('\n📁 Scanning automation/locators directory...')
|
|
378
375
|
const locatorGroups = await buildLocatorGroupsFromFS(baseDir, locatorMap)
|
|
379
376
|
result.locatorGroupsScanned = locatorGroups.length
|
|
380
377
|
console.log(` Found ${locatorGroups.length} locator group(s) in filesystem`)
|
|
@@ -15,6 +15,7 @@ import { glob } from 'glob'
|
|
|
15
15
|
import prisma from '../src/config/db-config'
|
|
16
16
|
import { buildModuleHierarchy } from '../src/lib/module-hierarchy-builder'
|
|
17
17
|
import { getLocatorGroupFilePath } from '../src/lib/locator-group-file-utils'
|
|
18
|
+
import { extractModulePathFromAutomationFile } from '../src/lib/template-sync-utils'
|
|
18
19
|
|
|
19
20
|
interface SyncResult {
|
|
20
21
|
locatorsScanned: number
|
|
@@ -34,7 +35,7 @@ interface SyncResult {
|
|
|
34
35
|
* Scans locator directory for all JSON files
|
|
35
36
|
*/
|
|
36
37
|
async function scanLocatorFiles(baseDir: string): Promise<string[]> {
|
|
37
|
-
const pattern = '
|
|
38
|
+
const pattern = 'automation/locators/**/*.json'
|
|
38
39
|
try {
|
|
39
40
|
const files = await glob(pattern, {
|
|
40
41
|
cwd: baseDir,
|
|
@@ -47,14 +48,10 @@ async function scanLocatorFiles(baseDir: string): Promise<string[]> {
|
|
|
47
48
|
|
|
48
49
|
/**
|
|
49
50
|
* Extracts module path from locator file path
|
|
50
|
-
* Example:
|
|
51
|
+
* Example: automation/locators/users/admins/directors/directors.json -> /users/admins/directors
|
|
51
52
|
*/
|
|
52
53
|
function extractModulePathFromLocatorFile(filePath: string, baseDir: string): string {
|
|
53
|
-
|
|
54
|
-
const relativePath = filePath.replace(testsDir, '').replace(/\\/g, '/')
|
|
55
|
-
const pathParts = relativePath.split('/').filter(p => p && p !== 'locators')
|
|
56
|
-
const moduleParts = pathParts.slice(0, -1) // Remove filename
|
|
57
|
-
return moduleParts.length > 0 ? '/' + moduleParts.join('/') : '/'
|
|
54
|
+
return extractModulePathFromAutomationFile(filePath, baseDir, 'locators')
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
/**
|
|
@@ -368,7 +365,7 @@ async function main() {
|
|
|
368
365
|
const baseDir = process.cwd()
|
|
369
366
|
|
|
370
367
|
// Scan locator files
|
|
371
|
-
console.log('📁 Scanning
|
|
368
|
+
console.log('📁 Scanning automation/locators...')
|
|
372
369
|
const files = await scanLocatorFiles(baseDir)
|
|
373
370
|
console.log(` Found ${files.length} locator file(s)`)
|
|
374
371
|
|
|
@@ -399,4 +396,3 @@ async function main() {
|
|
|
399
396
|
}
|
|
400
397
|
|
|
401
398
|
main()
|
|
402
|
-
|
|
@@ -13,6 +13,7 @@ import { buildModuleHierarchy, findModuleByPath, getAllModulesWithPaths } from '
|
|
|
13
13
|
import { join } from 'path'
|
|
14
14
|
import { glob } from 'glob'
|
|
15
15
|
import prisma from '../src/config/db-config'
|
|
16
|
+
import { extractModulePathFromAutomationFile } from '../src/lib/template-sync-utils'
|
|
16
17
|
|
|
17
18
|
interface SyncResult {
|
|
18
19
|
modulesScanned: number
|
|
@@ -33,7 +34,7 @@ async function scanLocatorDirectories(baseDir: string): Promise<string[]> {
|
|
|
33
34
|
|
|
34
35
|
try {
|
|
35
36
|
// Get all JSON files in locators directory
|
|
36
|
-
const pattern = '
|
|
37
|
+
const pattern = 'automation/locators/**/*.json'
|
|
37
38
|
const files = await glob(pattern, {
|
|
38
39
|
cwd: baseDir,
|
|
39
40
|
})
|
|
@@ -61,7 +62,7 @@ async function scanFeatureDirectories(baseDir: string): Promise<string[]> {
|
|
|
61
62
|
|
|
62
63
|
try {
|
|
63
64
|
// Get all feature files
|
|
64
|
-
const pattern = '
|
|
65
|
+
const pattern = 'automation/features/**/*.feature'
|
|
65
66
|
const files = await glob(pattern, {
|
|
66
67
|
cwd: baseDir,
|
|
67
68
|
})
|
|
@@ -83,26 +84,18 @@ async function scanFeatureDirectories(baseDir: string): Promise<string[]> {
|
|
|
83
84
|
|
|
84
85
|
/**
|
|
85
86
|
* Extracts module path from locator file path
|
|
86
|
-
* Example:
|
|
87
|
+
* Example: automation/locators/home/home.json -> /home
|
|
87
88
|
*/
|
|
88
89
|
function extractModulePathFromLocatorFile(filePath: string, baseDir: string): string {
|
|
89
|
-
|
|
90
|
-
const relativePath = filePath.replace(testsDir, '').replace(/\\/g, '/')
|
|
91
|
-
const pathParts = relativePath.split('/').filter(p => p && p !== 'locators')
|
|
92
|
-
const moduleParts = pathParts.slice(0, -1) // Remove filename
|
|
93
|
-
return moduleParts.length > 0 ? '/' + moduleParts.join('/') : '/'
|
|
90
|
+
return extractModulePathFromAutomationFile(filePath, baseDir, 'locators')
|
|
94
91
|
}
|
|
95
92
|
|
|
96
93
|
/**
|
|
97
94
|
* Extracts module path from feature file path
|
|
98
|
-
* Example:
|
|
95
|
+
* Example: automation/features/login/demo.feature -> /login
|
|
99
96
|
*/
|
|
100
97
|
function extractModulePathFromFeatureFile(filePath: string, baseDir: string): string {
|
|
101
|
-
|
|
102
|
-
const relativePath = filePath.replace(featuresBaseDir, '').replace(/\\/g, '/')
|
|
103
|
-
const pathParts = relativePath.split('/').filter(part => part && part !== '')
|
|
104
|
-
const moduleParts = pathParts.slice(0, -1) // Remove filename
|
|
105
|
-
return moduleParts.length > 0 ? '/' + moduleParts.join('/') : '/'
|
|
98
|
+
return extractModulePathFromAutomationFile(filePath, baseDir, 'features')
|
|
106
99
|
}
|
|
107
100
|
|
|
108
101
|
/**
|
|
@@ -301,11 +294,11 @@ async function main() {
|
|
|
301
294
|
const baseDir = process.cwd()
|
|
302
295
|
|
|
303
296
|
// Scan directories
|
|
304
|
-
console.log('📁 Scanning
|
|
297
|
+
console.log('📁 Scanning automation/locators...')
|
|
305
298
|
const locatorModulePaths = await scanLocatorDirectories(baseDir)
|
|
306
299
|
console.log(` Found ${locatorModulePaths.length} module path(s): ${locatorModulePaths.join(', ') || 'none'}`)
|
|
307
300
|
|
|
308
|
-
console.log('\n📁 Scanning
|
|
301
|
+
console.log('\n📁 Scanning automation/features...')
|
|
309
302
|
const featureModulePaths = await scanFeatureDirectories(baseDir)
|
|
310
303
|
console.log(` Found ${featureModulePaths.length} module path(s): ${featureModulePaths.join(', ') || 'none'}`)
|
|
311
304
|
|
|
@@ -346,4 +339,3 @@ async function main() {
|
|
|
346
339
|
}
|
|
347
340
|
|
|
348
341
|
main()
|
|
349
|
-
|
|
@@ -235,10 +235,10 @@ async function main() {
|
|
|
235
235
|
console.log('Filesystem is the source of truth - tags in DB but not in FS will be deleted.\n')
|
|
236
236
|
|
|
237
237
|
const baseDir = process.cwd()
|
|
238
|
-
const featuresDir = join(baseDir, '
|
|
238
|
+
const featuresDir = join(baseDir, 'automation', 'features')
|
|
239
239
|
|
|
240
240
|
// Scan feature files
|
|
241
|
-
console.log('📁 Scanning feature files in
|
|
241
|
+
console.log('📁 Scanning feature files in automation/features...')
|
|
242
242
|
const parsedFeatures = await scanFeatureFiles(featuresDir)
|
|
243
243
|
console.log(` Found ${parsedFeatures.length} feature file(s)`)
|
|
244
244
|
|