@sun-asterisk/sungen 2.4.5 → 2.5.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/dist/cli/commands/delivery.d.ts +7 -0
- package/dist/cli/commands/delivery.d.ts.map +1 -0
- package/dist/cli/commands/delivery.js +348 -0
- package/dist/cli/commands/delivery.js.map +1 -0
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +2 -0
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +64 -1
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/index.js +4 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/exporters/csv-exporter.d.ts +32 -0
- package/dist/exporters/csv-exporter.d.ts.map +1 -0
- package/dist/exporters/csv-exporter.js +311 -0
- package/dist/exporters/csv-exporter.js.map +1 -0
- package/dist/exporters/feature-parser.d.ts +48 -0
- package/dist/exporters/feature-parser.d.ts.map +1 -0
- package/dist/exporters/feature-parser.js +178 -0
- package/dist/exporters/feature-parser.js.map +1 -0
- package/dist/exporters/package-info.d.ts +9 -0
- package/dist/exporters/package-info.d.ts.map +1 -0
- package/dist/exporters/package-info.js +73 -0
- package/dist/exporters/package-info.js.map +1 -0
- package/dist/exporters/playwright-report-parser.d.ts +21 -0
- package/dist/exporters/playwright-report-parser.d.ts.map +1 -0
- package/dist/exporters/playwright-report-parser.js +184 -0
- package/dist/exporters/playwright-report-parser.js.map +1 -0
- package/dist/exporters/scenario-merger.d.ts +21 -0
- package/dist/exporters/scenario-merger.d.ts.map +1 -0
- package/dist/exporters/scenario-merger.js +51 -0
- package/dist/exporters/scenario-merger.js.map +1 -0
- package/dist/exporters/spec-parser.d.ts +20 -0
- package/dist/exporters/spec-parser.d.ts.map +1 -0
- package/dist/exporters/spec-parser.js +259 -0
- package/dist/exporters/spec-parser.js.map +1 -0
- package/dist/exporters/step-formatter.d.ts +32 -0
- package/dist/exporters/step-formatter.d.ts.map +1 -0
- package/dist/exporters/step-formatter.js +76 -0
- package/dist/exporters/step-formatter.js.map +1 -0
- package/dist/exporters/test-data-resolver.d.ts +20 -0
- package/dist/exporters/test-data-resolver.d.ts.map +1 -0
- package/dist/exporters/test-data-resolver.js +96 -0
- package/dist/exporters/test-data-resolver.js.map +1 -0
- package/dist/exporters/types.d.ts +104 -0
- package/dist/exporters/types.d.ts.map +1 -0
- package/dist/exporters/types.js +6 -0
- package/dist/exporters/types.js.map +1 -0
- package/dist/exporters/xlsx-exporter.d.ts +19 -0
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -0
- package/dist/exporters/xlsx-exporter.js +309 -0
- package/dist/exporters/xlsx-exporter.js.map +1 -0
- package/dist/generators/gherkin-parser/index.d.ts +1 -0
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +3 -0
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts +29 -1
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +21 -1
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js +11 -2
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/after-all.hbs +8 -0
- package/dist/generators/test-generator/adapters/playwright/templates/after-each.hbs +8 -0
- package/dist/generators/test-generator/adapters/playwright/templates/before-all.hbs +8 -0
- package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +24 -0
- package/dist/generators/test-generator/code-generator.d.ts +2 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +109 -12
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +1 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +1 -1
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts +29 -1
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +11 -2
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.d.ts +11 -2
- package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.js +36 -25
- package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/runtime-data-transformer.d.ts +7 -0
- package/dist/generators/test-generator/utils/runtime-data-transformer.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/runtime-data-transformer.js +42 -0
- package/dist/generators/test-generator/utils/runtime-data-transformer.js.map +1 -0
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +26 -0
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/generators/types.d.ts +1 -0
- package/dist/generators/types.d.ts.map +1 -1
- package/dist/generators/types.js.map +1 -1
- package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
- package/dist/orchestrator/ai-rules-updater.js +12 -0
- package/dist/orchestrator/ai-rules-updater.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +21 -1
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +158 -74
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/screen-manager.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +2 -0
- package/dist/orchestrator/screen-manager.js.map +1 -1
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +15 -17
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +7 -5
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-delivery.md +71 -0
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +28 -1
- package/dist/orchestrator/templates/ai-instructions/claude-config.md +23 -4
- package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-figma.md +142 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-live.md +100 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-local.md +73 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-delivery.md +103 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +68 -13
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +22 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +54 -3
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +13 -15
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +6 -4
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-delivery.md +71 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +38 -14
- package/dist/orchestrator/templates/ai-instructions/copilot-config.md +23 -4
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-figma.md +142 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-live.md +100 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-local.md +73 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-delivery.md +103 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +88 -13
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +22 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +54 -3
- package/dist/orchestrator/templates/playwright.config.d.ts.map +1 -1
- package/dist/orchestrator/templates/playwright.config.js +6 -1
- package/dist/orchestrator/templates/playwright.config.js.map +1 -1
- package/dist/orchestrator/templates/playwright.config.ts +6 -1
- package/dist/orchestrator/templates/specs-base.d.ts +12 -1
- package/dist/orchestrator/templates/specs-base.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-base.js +47 -5
- package/dist/orchestrator/templates/specs-base.js.map +1 -1
- package/dist/orchestrator/templates/specs-base.ts +65 -7
- package/dist/orchestrator/templates/specs-test-data.d.ts +14 -0
- package/dist/orchestrator/templates/specs-test-data.d.ts.map +1 -0
- package/dist/orchestrator/templates/specs-test-data.js +100 -0
- package/dist/orchestrator/templates/specs-test-data.js.map +1 -0
- package/dist/orchestrator/templates/specs-test-data.ts +66 -0
- package/package.json +2 -1
- package/src/cli/commands/delivery.ts +348 -0
- package/src/cli/commands/generate.ts +2 -0
- package/src/cli/commands/update.ts +84 -2
- package/src/cli/index.ts +4 -2
- package/src/exporters/csv-exporter.ts +304 -0
- package/src/exporters/feature-parser.ts +168 -0
- package/src/exporters/package-info.ts +35 -0
- package/src/exporters/playwright-report-parser.ts +168 -0
- package/src/exporters/scenario-merger.ts +63 -0
- package/src/exporters/spec-parser.ts +247 -0
- package/src/exporters/step-formatter.ts +80 -0
- package/src/exporters/test-data-resolver.ts +59 -0
- package/src/exporters/types.ts +112 -0
- package/src/exporters/xlsx-exporter.ts +301 -0
- package/src/generators/gherkin-parser/index.ts +4 -0
- package/src/generators/test-generator/adapters/adapter-interface.ts +12 -1
- package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +14 -2
- package/src/generators/test-generator/adapters/playwright/templates/after-all.hbs +8 -0
- package/src/generators/test-generator/adapters/playwright/templates/after-each.hbs +8 -0
- package/src/generators/test-generator/adapters/playwright/templates/before-all.hbs +8 -0
- package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +24 -0
- package/src/generators/test-generator/code-generator.ts +122 -13
- package/src/generators/test-generator/step-mapper.ts +2 -2
- package/src/generators/test-generator/template-engine.ts +28 -2
- package/src/generators/test-generator/utils/data-resolver.ts +45 -27
- package/src/generators/test-generator/utils/runtime-data-transformer.ts +51 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +26 -0
- package/src/generators/types.ts +1 -0
- package/src/orchestrator/ai-rules-updater.ts +12 -0
- package/src/orchestrator/project-initializer.ts +187 -80
- package/src/orchestrator/screen-manager.ts +2 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +15 -17
- package/src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +7 -5
- package/src/orchestrator/templates/ai-instructions/claude-cmd-delivery.md +71 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +28 -1
- package/src/orchestrator/templates/ai-instructions/claude-config.md +23 -4
- package/src/orchestrator/templates/ai-instructions/claude-skill-capture-figma.md +142 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-capture-live.md +100 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-capture-local.md +73 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-delivery.md +103 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +68 -13
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-keys.md +22 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +54 -3
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +13 -15
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +6 -4
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-delivery.md +71 -0
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +38 -14
- package/src/orchestrator/templates/ai-instructions/copilot-config.md +23 -4
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-figma.md +142 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-live.md +100 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-local.md +73 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-delivery.md +103 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +88 -13
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +22 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +54 -3
- package/src/orchestrator/templates/playwright.config.ts +6 -1
- package/src/orchestrator/templates/specs-base.ts +65 -7
- package/src/orchestrator/templates/specs-test-data.ts +66 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the delivery exporter.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Test case row in the final CSV output.
|
|
7
|
+
*/
|
|
8
|
+
export interface TestCaseRow {
|
|
9
|
+
tcId: string; // e.g. KUDOS-UI-001
|
|
10
|
+
category1: string; // Scenario name (human part)
|
|
11
|
+
category2: string; // "Accessing" | "GUI" | "Function"
|
|
12
|
+
category3: string; // Feature name (e.g. "Create Kudo Modal")
|
|
13
|
+
category4: string; // Screen name (e.g. "kudos")
|
|
14
|
+
precondition: string; // Natural language Given + auth
|
|
15
|
+
testData: string; // semicolon-separated key: value
|
|
16
|
+
steps: string; // numbered steps
|
|
17
|
+
expectedResults: string; // numbered expected results
|
|
18
|
+
priority: string; // Critical | High | Normal | Low
|
|
19
|
+
testcaseType: string; // Auto | Manual | Not compiled
|
|
20
|
+
testResult: string; // Passed | Failed | Pending | N/A
|
|
21
|
+
executedDate: string; // dd/mm/yyyy
|
|
22
|
+
testExecutor: string; // git user.name
|
|
23
|
+
testEnvironment: string; // baseURL + project
|
|
24
|
+
note: string; // error + trace for failed
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Summary stats per screen.
|
|
29
|
+
*/
|
|
30
|
+
export interface ScreenSummary {
|
|
31
|
+
screen: string;
|
|
32
|
+
total: number;
|
|
33
|
+
passed: number;
|
|
34
|
+
failed: number;
|
|
35
|
+
pending: number;
|
|
36
|
+
na: number;
|
|
37
|
+
notCompiled: number;
|
|
38
|
+
outputFile: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Pre-flight check result for a single screen.
|
|
43
|
+
*/
|
|
44
|
+
export interface PreflightCheck {
|
|
45
|
+
screen: string;
|
|
46
|
+
featureOk: boolean;
|
|
47
|
+
testDataOk: boolean;
|
|
48
|
+
selectorsOk: boolean;
|
|
49
|
+
specOk: boolean;
|
|
50
|
+
resultsOk: boolean;
|
|
51
|
+
missing: string[]; // Human-readable list of missing items
|
|
52
|
+
suggestions: string[]; // Suggested commands to run
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Metadata extracted from a .feature file.
|
|
57
|
+
*/
|
|
58
|
+
export interface FeatureMetadata {
|
|
59
|
+
featureName: string; // e.g., "Create Kudo Modal"
|
|
60
|
+
featurePath?: string; // e.g., "/kudos"
|
|
61
|
+
featureTags: string[]; // Feature-level tags
|
|
62
|
+
scenarios: ScenarioMetadata[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ScenarioMetadata {
|
|
66
|
+
name: string; // Full scenario name (with VP prefix)
|
|
67
|
+
tags: string[]; // Scenario-level tags (merged with feature tags)
|
|
68
|
+
stepsName?: string; // @steps:<name>
|
|
69
|
+
extendsName?: string; // @extend:<name>
|
|
70
|
+
referencedVars: string[]; // Variables {{var}} used in this scenario
|
|
71
|
+
rawGivenSteps: string[]; // Raw Given/And-after-Given step text
|
|
72
|
+
rawWhenSteps: string[]; // Raw When/And-after-When step text
|
|
73
|
+
rawThenSteps: string[]; // Raw Then/And-after-Then step text
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Structure extracted from a compiled .spec.ts test block.
|
|
78
|
+
*/
|
|
79
|
+
export interface SpecTest {
|
|
80
|
+
describeName: string; // test.describe title
|
|
81
|
+
authRole?: string; // nested describe for auth (e.g., "user")
|
|
82
|
+
testTitle: string; // Full Playwright test title (matches results.json)
|
|
83
|
+
scenarioName: string; // Scenario name without VP prefix stripped
|
|
84
|
+
vpId?: string; // Extracted VP-XX-NNN if present
|
|
85
|
+
precondition: string[]; // Lines before first // [VP-...] marker
|
|
86
|
+
steps: string[]; // Lines between marker and first expect()
|
|
87
|
+
expectations: string[]; // Lines with expect() calls
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface SpecFileData {
|
|
91
|
+
tests: SpecTest[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Result from results.json per test.
|
|
96
|
+
*/
|
|
97
|
+
export interface PlaywrightResult {
|
|
98
|
+
testTitle: string;
|
|
99
|
+
status: 'passed' | 'failed' | 'skipped' | 'timedOut' | 'interrupted' | 'unknown';
|
|
100
|
+
startTime?: string;
|
|
101
|
+
error?: string;
|
|
102
|
+
tracePath?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Environment metadata.
|
|
107
|
+
*/
|
|
108
|
+
export interface EnvironmentInfo {
|
|
109
|
+
baseURL: string;
|
|
110
|
+
projectName: string;
|
|
111
|
+
executor: string;
|
|
112
|
+
}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the same rows/summary the CSV exporter produces into a styled .xlsx
|
|
3
|
+
* workbook. Keeps the BM-2-901-13 layout but adds:
|
|
4
|
+
* - bold header row with filled background + white font
|
|
5
|
+
* - borders on the data region
|
|
6
|
+
* - text wrapping in Steps / Expected / Pre-condition / Test Data
|
|
7
|
+
* - frozen header so the first data rows stay aligned with the title row
|
|
8
|
+
* - sensible column widths
|
|
9
|
+
* - category-separator rows rendered as a filled bar spanning the table
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import ExcelJS from 'exceljs';
|
|
15
|
+
import { ScreenSummary, TestCaseRow } from './types';
|
|
16
|
+
import { getPackageVersion } from './package-info';
|
|
17
|
+
|
|
18
|
+
const COL_COUNT = 16;
|
|
19
|
+
const HEADER_FILL = 'FF1F4E78'; // deep blue band
|
|
20
|
+
const HEADER_FONT = 'FFFFFFFF';
|
|
21
|
+
const META_FILL = 'FFDCE6F1'; // soft blue for meta rows
|
|
22
|
+
const CATEGORY_FILL = 'FFFFF2CC'; // soft yellow for group dividers
|
|
23
|
+
const BORDER_COLOR = 'FFBFBFBF';
|
|
24
|
+
|
|
25
|
+
type AnyCell = ExcelJS.Cell;
|
|
26
|
+
type AnyRow = ExcelJS.Row;
|
|
27
|
+
|
|
28
|
+
function applyBorder(cell: AnyCell): void {
|
|
29
|
+
cell.border = {
|
|
30
|
+
top: { style: 'thin', color: { argb: BORDER_COLOR } },
|
|
31
|
+
left: { style: 'thin', color: { argb: BORDER_COLOR } },
|
|
32
|
+
bottom: { style: 'thin', color: { argb: BORDER_COLOR } },
|
|
33
|
+
right: { style: 'thin', color: { argb: BORDER_COLOR } },
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function renderXlsx(
|
|
38
|
+
summary: ScreenSummary,
|
|
39
|
+
rows: TestCaseRow[],
|
|
40
|
+
specLink: string
|
|
41
|
+
): ExcelJS.Workbook {
|
|
42
|
+
const wb = new ExcelJS.Workbook();
|
|
43
|
+
wb.creator = 'sungen delivery';
|
|
44
|
+
wb.created = new Date();
|
|
45
|
+
const ws = wb.addWorksheet('Testcases', {
|
|
46
|
+
views: [{ state: 'frozen', ySplit: 0, xSplit: 0 }],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const issueDate = (() => {
|
|
50
|
+
const d = new Date();
|
|
51
|
+
return `${String(d.getDate()).padStart(2, '0')}/${String(d.getMonth() + 1).padStart(2, '0')}/${d.getFullYear()}`;
|
|
52
|
+
})();
|
|
53
|
+
|
|
54
|
+
const total = summary.total || 1;
|
|
55
|
+
const pct = (n: number) => `${Math.round((n / total) * 100)}%`;
|
|
56
|
+
|
|
57
|
+
// -- Column widths (matches CSV column order) --
|
|
58
|
+
ws.columns = [
|
|
59
|
+
{ width: 18 }, // TC ID
|
|
60
|
+
{ width: 42 }, // Category 1
|
|
61
|
+
{ width: 14 }, // Category 2
|
|
62
|
+
{ width: 26 }, // Category 3
|
|
63
|
+
{ width: 14 }, // Category 4
|
|
64
|
+
{ width: 36 }, // Pre-condition
|
|
65
|
+
{ width: 30 }, // Test Data
|
|
66
|
+
{ width: 46 }, // Steps
|
|
67
|
+
{ width: 52 }, // Expected results
|
|
68
|
+
{ width: 10 }, // Priority
|
|
69
|
+
{ width: 14 }, // Testcase type
|
|
70
|
+
{ width: 14 }, // Test Result
|
|
71
|
+
{ width: 14 }, // Executed Date
|
|
72
|
+
{ width: 16 }, // Test Executor
|
|
73
|
+
{ width: 38 }, // Test Environment
|
|
74
|
+
{ width: 40 }, // Note
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
// -- Top metadata band (rows 1-4) --
|
|
78
|
+
const titleLabel = `${summary.screen.toUpperCase()} TESTCASE`;
|
|
79
|
+
const titleRow = ws.addRow(['', '', '', titleLabel, '', '', 'No: BM-2-901-13']);
|
|
80
|
+
titleRow.getCell(4).font = { bold: true, size: 16 };
|
|
81
|
+
titleRow.getCell(4).alignment = { horizontal: 'center', vertical: 'middle' };
|
|
82
|
+
ws.mergeCells(titleRow.number, 4, titleRow.number, 6);
|
|
83
|
+
titleRow.getCell(7).font = { bold: true };
|
|
84
|
+
titleRow.height = 22;
|
|
85
|
+
|
|
86
|
+
const versionRow = ws.addRow(['', '', '', '', '', '', `Version: ${getPackageVersion()}`]);
|
|
87
|
+
versionRow.getCell(7).font = { bold: true };
|
|
88
|
+
const dateRow = ws.addRow(['', '', '', '', '', '', `Issue Date: ${issueDate}`]);
|
|
89
|
+
dateRow.getCell(7).font = { bold: true };
|
|
90
|
+
const companyRow = ws.addRow([
|
|
91
|
+
'SUN ASTERISK VIETNAM CO., LTD',
|
|
92
|
+
'',
|
|
93
|
+
'',
|
|
94
|
+
'',
|
|
95
|
+
'',
|
|
96
|
+
'',
|
|
97
|
+
'ISO/IEC 27001:2022 & ISO 9001:2015',
|
|
98
|
+
]);
|
|
99
|
+
companyRow.getCell(1).font = { bold: true };
|
|
100
|
+
companyRow.getCell(7).font = { bold: true };
|
|
101
|
+
|
|
102
|
+
ws.addRow([]); // spacer
|
|
103
|
+
|
|
104
|
+
// -- Summary band --
|
|
105
|
+
const summaryHeader = ws.addRow([
|
|
106
|
+
'',
|
|
107
|
+
'',
|
|
108
|
+
'Total TCs',
|
|
109
|
+
'Passed',
|
|
110
|
+
'Failed',
|
|
111
|
+
'Pending',
|
|
112
|
+
'N/A',
|
|
113
|
+
'Remaining',
|
|
114
|
+
]);
|
|
115
|
+
for (let i = 3; i <= 8; i++) {
|
|
116
|
+
const c = summaryHeader.getCell(i);
|
|
117
|
+
c.font = { bold: true, color: { argb: HEADER_FONT } };
|
|
118
|
+
c.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: HEADER_FILL } };
|
|
119
|
+
c.alignment = { horizontal: 'center', vertical: 'middle' };
|
|
120
|
+
applyBorder(c);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const summaryCounts = ws.addRow([
|
|
124
|
+
'',
|
|
125
|
+
'',
|
|
126
|
+
summary.total,
|
|
127
|
+
summary.passed,
|
|
128
|
+
summary.failed,
|
|
129
|
+
summary.pending,
|
|
130
|
+
summary.na,
|
|
131
|
+
summary.pending + summary.na,
|
|
132
|
+
]);
|
|
133
|
+
for (let i = 3; i <= 8; i++) {
|
|
134
|
+
const c = summaryCounts.getCell(i);
|
|
135
|
+
c.font = { bold: true };
|
|
136
|
+
c.alignment = { horizontal: 'center' };
|
|
137
|
+
c.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: META_FILL } };
|
|
138
|
+
applyBorder(c);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const summaryPct = ws.addRow([
|
|
142
|
+
'',
|
|
143
|
+
'',
|
|
144
|
+
'',
|
|
145
|
+
pct(summary.passed),
|
|
146
|
+
pct(summary.failed),
|
|
147
|
+
pct(summary.pending),
|
|
148
|
+
pct(summary.na),
|
|
149
|
+
pct(summary.pending + summary.na),
|
|
150
|
+
]);
|
|
151
|
+
for (let i = 4; i <= 8; i++) {
|
|
152
|
+
const c = summaryPct.getCell(i);
|
|
153
|
+
c.alignment = { horizontal: 'center' };
|
|
154
|
+
c.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: META_FILL } };
|
|
155
|
+
applyBorder(c);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
ws.addRow([]); // spacer
|
|
159
|
+
|
|
160
|
+
const specRow = ws.addRow(['Spec/Design link:', specLink]);
|
|
161
|
+
specRow.getCell(1).font = { bold: true };
|
|
162
|
+
specRow.getCell(2).font = { color: { argb: 'FF1F4E78' }, underline: true };
|
|
163
|
+
|
|
164
|
+
ws.addRow([]); // spacer
|
|
165
|
+
|
|
166
|
+
const legendRow = ws.addRow(['*: Mandatory']);
|
|
167
|
+
legendRow.getCell(1).font = { italic: true, color: { argb: 'FF808080' } };
|
|
168
|
+
|
|
169
|
+
// -- Column header row (bold, filled, wrapped) --
|
|
170
|
+
const headerRow = ws.addRow([
|
|
171
|
+
'TC ID*',
|
|
172
|
+
'{Category 1}',
|
|
173
|
+
'{Category 2}',
|
|
174
|
+
'{Category 3}',
|
|
175
|
+
'{Category 4}',
|
|
176
|
+
'Pre-condition',
|
|
177
|
+
'Test Data',
|
|
178
|
+
'Steps*',
|
|
179
|
+
'Expected results*',
|
|
180
|
+
'Priority',
|
|
181
|
+
'Testcase type',
|
|
182
|
+
'Test Result*',
|
|
183
|
+
'Executed Date*',
|
|
184
|
+
'Test Executor*',
|
|
185
|
+
'Test Environment',
|
|
186
|
+
'Note\n(Test evidence, DefectID, Actual result)',
|
|
187
|
+
]);
|
|
188
|
+
headerRow.height = 38;
|
|
189
|
+
headerRow.eachCell((cell) => {
|
|
190
|
+
cell.font = { bold: true, color: { argb: HEADER_FONT } };
|
|
191
|
+
cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: HEADER_FILL } };
|
|
192
|
+
cell.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
|
|
193
|
+
applyBorder(cell);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Freeze everything above and below the header row — reveal header when scrolling.
|
|
197
|
+
ws.views = [{ state: 'frozen', ySplit: headerRow.number }];
|
|
198
|
+
|
|
199
|
+
// -- Data rows grouped by category --
|
|
200
|
+
const order = ['Accessing', 'GUI', 'Function'];
|
|
201
|
+
const grouped = new Map<string, TestCaseRow[]>();
|
|
202
|
+
for (const r of rows) {
|
|
203
|
+
const g = grouped.get(r.category2) || [];
|
|
204
|
+
g.push(r);
|
|
205
|
+
grouped.set(r.category2, g);
|
|
206
|
+
}
|
|
207
|
+
const emittedGroups = new Set<string>();
|
|
208
|
+
|
|
209
|
+
function emitGroup(groupName: string, groupRows: TestCaseRow[]): void {
|
|
210
|
+
const divider = ws.addRow(['', groupName]);
|
|
211
|
+
divider.getCell(2).font = { bold: true };
|
|
212
|
+
divider.eachCell({ includeEmpty: false }, (cell) => {
|
|
213
|
+
cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: CATEGORY_FILL } };
|
|
214
|
+
});
|
|
215
|
+
// Fill the full width so the band spans the whole table
|
|
216
|
+
for (let i = 1; i <= COL_COUNT; i++) {
|
|
217
|
+
const c = divider.getCell(i);
|
|
218
|
+
c.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: CATEGORY_FILL } };
|
|
219
|
+
applyBorder(c);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
for (const r of groupRows) {
|
|
223
|
+
const row = ws.addRow([
|
|
224
|
+
r.tcId,
|
|
225
|
+
r.category1,
|
|
226
|
+
'',
|
|
227
|
+
'',
|
|
228
|
+
'',
|
|
229
|
+
r.precondition,
|
|
230
|
+
r.testData,
|
|
231
|
+
r.steps,
|
|
232
|
+
r.expectedResults,
|
|
233
|
+
r.priority,
|
|
234
|
+
r.testcaseType,
|
|
235
|
+
r.testResult,
|
|
236
|
+
r.executedDate,
|
|
237
|
+
r.testExecutor,
|
|
238
|
+
r.testEnvironment,
|
|
239
|
+
r.note,
|
|
240
|
+
]);
|
|
241
|
+
row.alignment = { vertical: 'top', wrapText: true };
|
|
242
|
+
row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
|
|
243
|
+
applyBorder(cell);
|
|
244
|
+
if (colNumber === 1) {
|
|
245
|
+
cell.font = { bold: true };
|
|
246
|
+
}
|
|
247
|
+
if (colNumber === 12) {
|
|
248
|
+
// Test Result — colour-code
|
|
249
|
+
const val = String(cell.value || '');
|
|
250
|
+
if (val === 'Passed') {
|
|
251
|
+
cell.font = { color: { argb: 'FF107C41' }, bold: true };
|
|
252
|
+
} else if (val === 'Failed') {
|
|
253
|
+
cell.font = { color: { argb: 'FFC00000' }, bold: true };
|
|
254
|
+
} else if (val === 'Pending') {
|
|
255
|
+
cell.font = { color: { argb: 'FF996D00' }, bold: true };
|
|
256
|
+
} else if (val === 'N/A') {
|
|
257
|
+
cell.font = { color: { argb: 'FF808080' } };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
for (const groupName of order) {
|
|
265
|
+
const groupRows = grouped.get(groupName);
|
|
266
|
+
if (!groupRows || groupRows.length === 0) continue;
|
|
267
|
+
emittedGroups.add(groupName);
|
|
268
|
+
emitGroup(groupName, groupRows);
|
|
269
|
+
}
|
|
270
|
+
for (const [groupName, groupRows] of grouped.entries()) {
|
|
271
|
+
if (emittedGroups.has(groupName)) continue;
|
|
272
|
+
emitGroup(groupName, groupRows);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Auto-filter on the header row so QA can filter by result / priority / category
|
|
276
|
+
ws.autoFilter = {
|
|
277
|
+
from: { row: headerRow.number, column: 1 },
|
|
278
|
+
to: { row: ws.rowCount, column: COL_COUNT },
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
return wb;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Write the workbook to `qa/deliverables/<screen>-testcases.xlsx`.
|
|
286
|
+
* Directory is created on demand. Returns the absolute output path.
|
|
287
|
+
*/
|
|
288
|
+
export async function writeXlsx(
|
|
289
|
+
cwd: string,
|
|
290
|
+
screen: string,
|
|
291
|
+
wb: ExcelJS.Workbook
|
|
292
|
+
): Promise<string> {
|
|
293
|
+
const outDir = path.join(cwd, 'qa', 'deliverables');
|
|
294
|
+
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
|
295
|
+
const outPath = path.join(outDir, `${screen}-testcases.xlsx`);
|
|
296
|
+
await wb.xlsx.writeFile(outPath);
|
|
297
|
+
return outPath;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
void applyBorder;
|
|
301
|
+
void ({} as AnyRow);
|
|
@@ -34,6 +34,7 @@ export interface ParsedScenario {
|
|
|
34
34
|
steps: ParsedStep[];
|
|
35
35
|
stepsName?: string; // set when scenario has @steps:<name> — marks it as a reusable block
|
|
36
36
|
extendsName?: string; // set when scenario has @extend:<name> — merges base steps inline
|
|
37
|
+
hookType?: 'beforeAll' | 'afterEach' | 'afterAll'; // set when scenario has @beforeAll/@afterEach/@afterAll
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export interface ParsedFeature {
|
|
@@ -120,12 +121,15 @@ export class GherkinParser {
|
|
|
120
121
|
const tags = scenario.tags.map((tag) => tag.name);
|
|
121
122
|
const stepsName = tags.find(t => t.startsWith('@steps:'))?.replace('@steps:', '') || undefined;
|
|
122
123
|
const extendsName = tags.find(t => t.startsWith('@extend:'))?.replace('@extend:', '') || undefined;
|
|
124
|
+
const hookTag = tags.find(t => /^@(beforeAll|afterEach|afterAll)$/.test(t));
|
|
125
|
+
const hookType = hookTag?.replace('@', '') as ParsedScenario['hookType'];
|
|
123
126
|
return {
|
|
124
127
|
name: scenario.name,
|
|
125
128
|
tags,
|
|
126
129
|
steps: scenario.steps.map((step) => this.parseStep(step)),
|
|
127
130
|
stepsName,
|
|
128
131
|
extendsName,
|
|
132
|
+
hookType,
|
|
129
133
|
};
|
|
130
134
|
});
|
|
131
135
|
}
|
|
@@ -13,6 +13,14 @@ export interface TestFileData {
|
|
|
13
13
|
featureName: string;
|
|
14
14
|
featureDescription?: string;
|
|
15
15
|
background?: string;
|
|
16
|
+
beforeAll?: string;
|
|
17
|
+
afterEach?: string;
|
|
18
|
+
afterAll?: string;
|
|
19
|
+
cleanupConfig?: string; // Pre-formatted autoCleanup config from @cleanup:* tags
|
|
20
|
+
screenshotOnFailure?: boolean; // @screenshot:on-failure tag
|
|
21
|
+
runtimeData?: boolean; // --runtime-data flag: testData.get() instead of hardcoded values
|
|
22
|
+
screenName?: string; // Screen name for TestDataLoader.load()
|
|
23
|
+
featureFileName?: string; // Feature file name for TestDataLoader.load()
|
|
16
24
|
scenarios: string[];
|
|
17
25
|
authGroups?: AuthGroup[]; // Grouped by auth role for nested describes
|
|
18
26
|
singleAuthRole?: string; // Auth role when all scenarios share the same role
|
|
@@ -49,8 +57,11 @@ export interface TestGeneratorAdapter {
|
|
|
49
57
|
// Template rendering methods
|
|
50
58
|
renderTestFile(data: TestFileData): string;
|
|
51
59
|
renderScenario(data: ScenarioData): string;
|
|
52
|
-
renderImports(): string;
|
|
60
|
+
renderImports(options?: { runtimeData?: boolean }): string;
|
|
53
61
|
renderBeforeEach(data: { steps: Array<{ comment?: string; code: string }> }): string;
|
|
62
|
+
renderBeforeAll(data: { steps: Array<{ comment?: string; code: string }> }): string;
|
|
63
|
+
renderAfterEach(data: { steps: Array<{ comment?: string; code: string }> }): string;
|
|
64
|
+
renderAfterAll(data: { steps: Array<{ comment?: string; code: string }> }): string;
|
|
54
65
|
|
|
55
66
|
// Step rendering
|
|
56
67
|
renderStep(templateName: string, data: any): string;
|
|
@@ -26,14 +26,26 @@ export class PlaywrightAdapter implements TestGeneratorAdapter {
|
|
|
26
26
|
return this.templateEngine.renderScenario(data);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
renderImports(): string {
|
|
30
|
-
return this.templateEngine.renderImports();
|
|
29
|
+
renderImports(options?: { runtimeData?: boolean }): string {
|
|
30
|
+
return this.templateEngine.renderImports(options);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
renderBeforeEach(data: { steps: Array<{ comment?: string; code: string }> }): string {
|
|
34
34
|
return this.templateEngine.renderBeforeEach(data);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
renderBeforeAll(data: { steps: Array<{ comment?: string; code: string }> }): string {
|
|
38
|
+
return this.templateEngine.renderBeforeAll(data);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
renderAfterEach(data: { steps: Array<{ comment?: string; code: string }> }): string {
|
|
42
|
+
return this.templateEngine.renderAfterEach(data);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
renderAfterAll(data: { steps: Array<{ comment?: string; code: string }> }): string {
|
|
46
|
+
return this.templateEngine.renderAfterAll(data);
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
renderStep(templateName: string, data: any): string {
|
|
38
50
|
return this.templateEngine.renderStep(templateName, data);
|
|
39
51
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
{{imports}}
|
|
2
|
+
{{#if runtimeData}}
|
|
3
|
+
|
|
4
|
+
const testData = TestDataLoader.load('{{screenName}}', '{{featureFileName}}');
|
|
5
|
+
{{/if}}
|
|
2
6
|
|
|
3
7
|
{{#if featureDescription}}
|
|
4
8
|
/**
|
|
@@ -11,10 +15,30 @@ test.describe('{{featureName}}', () => {
|
|
|
11
15
|
{{#if singleAuthRole}}
|
|
12
16
|
test.use({ storageState: 'specs/.auth/{{singleAuthRole}}.json' });
|
|
13
17
|
|
|
18
|
+
{{/if}}
|
|
19
|
+
{{#if cleanupConfig}}
|
|
20
|
+
test.use({ autoCleanup: { {{cleanupConfig}} } });
|
|
21
|
+
|
|
22
|
+
{{/if}}
|
|
23
|
+
{{#if screenshotOnFailure}}
|
|
24
|
+
test.use({ screenshotOnFailure: true });
|
|
25
|
+
|
|
26
|
+
{{/if}}
|
|
27
|
+
{{#if beforeAll}}
|
|
28
|
+
{{beforeAll}}
|
|
29
|
+
|
|
14
30
|
{{/if}}
|
|
15
31
|
{{#if background}}
|
|
16
32
|
{{background}}
|
|
17
33
|
|
|
34
|
+
{{/if}}
|
|
35
|
+
{{#if afterEach}}
|
|
36
|
+
{{afterEach}}
|
|
37
|
+
|
|
38
|
+
{{/if}}
|
|
39
|
+
{{#if afterAll}}
|
|
40
|
+
{{afterAll}}
|
|
41
|
+
|
|
18
42
|
{{/if}}
|
|
19
43
|
{{#if authGroups}}
|
|
20
44
|
{{#each authGroups}}
|