@sun-asterisk/sungen 2.6.0 → 2.6.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/dist/cli/index.js +1 -1
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts +15 -0
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +2 -0
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/scenario.hbs +20 -1
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +84 -4
- package/dist/generators/test-generator/code-generator.d.ts +1 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +76 -6
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/interaction-patterns.js +22 -3
- package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/navigation-patterns.js +8 -3
- package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts +13 -0
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +1 -1
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/orchestrator/screen-manager.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +3 -1
- package/dist/orchestrator/screen-manager.js.map +1 -1
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +70 -10
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +23 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +70 -10
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +23 -0
- package/dist/orchestrator/templates/specs-base.d.ts +3 -4
- package/dist/orchestrator/templates/specs-base.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-base.js +53 -39
- package/dist/orchestrator/templates/specs-base.js.map +1 -1
- package/dist/orchestrator/templates/specs-base.ts +55 -45
- package/dist/orchestrator/templates/specs-test-data.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-test-data.js +43 -0
- package/dist/orchestrator/templates/specs-test-data.js.map +1 -1
- package/dist/orchestrator/templates/specs-test-data.ts +47 -0
- package/package.json +1 -1
- package/src/cli/index.ts +1 -1
- package/src/generators/test-generator/adapters/adapter-interface.ts +6 -1
- package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/scenario.hbs +20 -1
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +84 -4
- package/src/generators/test-generator/code-generator.ts +88 -7
- package/src/generators/test-generator/patterns/interaction-patterns.ts +25 -3
- package/src/generators/test-generator/patterns/navigation-patterns.ts +8 -3
- package/src/generators/test-generator/template-engine.ts +5 -2
- package/src/orchestrator/screen-manager.ts +3 -1
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +70 -10
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +23 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +70 -10
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +23 -0
- package/src/orchestrator/templates/specs-base.ts +55 -45
- package/src/orchestrator/templates/specs-test-data.ts +47 -0
|
@@ -1,10 +1,62 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.expect = exports.test = void 0;
|
|
4
|
+
exports.cleanupPage = cleanupPage;
|
|
4
5
|
const test_1 = require("@playwright/test");
|
|
5
6
|
Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return test_1.expect; } });
|
|
7
|
+
async function cleanupPage(page, config) {
|
|
8
|
+
if (config.overlay) {
|
|
9
|
+
await page.keyboard.press('Escape').catch(() => { });
|
|
10
|
+
await page.locator('body').click({ position: { x: 1, y: 1 }, force: true }).catch(() => { });
|
|
11
|
+
const hasOverlay = await page.evaluate(`(() => {
|
|
12
|
+
const el = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2);
|
|
13
|
+
if (!el) return false;
|
|
14
|
+
let current = el;
|
|
15
|
+
while (current && current !== document.body) {
|
|
16
|
+
if (getComputedStyle(current).position === 'fixed') return true;
|
|
17
|
+
current = current.parentElement;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
})()`).catch(() => false);
|
|
21
|
+
if (hasOverlay) {
|
|
22
|
+
await page.keyboard.press('Escape').catch(() => { });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (config.forms) {
|
|
26
|
+
await page.evaluate(`(() => {
|
|
27
|
+
const inputSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;
|
|
28
|
+
const textareaSetter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;
|
|
29
|
+
document.querySelectorAll('input:not([type=hidden]):not([type=submit]):not([type=checkbox]):not([type=radio])').forEach(el => {
|
|
30
|
+
inputSetter?.call(el, '');
|
|
31
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
32
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
33
|
+
});
|
|
34
|
+
document.querySelectorAll('textarea').forEach(el => {
|
|
35
|
+
textareaSetter?.call(el, '');
|
|
36
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
37
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
38
|
+
});
|
|
39
|
+
document.querySelectorAll('select').forEach(el => {
|
|
40
|
+
el.selectedIndex = 0;
|
|
41
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
42
|
+
});
|
|
43
|
+
document.querySelectorAll('input[type=checkbox]').forEach(el => {
|
|
44
|
+
if (el.checked !== el.defaultChecked) {
|
|
45
|
+
el.checked = el.defaultChecked;
|
|
46
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
document.querySelectorAll('form').forEach(f => f.reset());
|
|
50
|
+
})()`).catch(() => { });
|
|
51
|
+
}
|
|
52
|
+
if (config.scroll) {
|
|
53
|
+
await page.evaluate('window.scrollTo(0, 0)').catch(() => { });
|
|
54
|
+
}
|
|
55
|
+
if (config.storage) {
|
|
56
|
+
await page.evaluate('sessionStorage.clear()').catch(() => { });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
6
59
|
const test = test_1.test.extend({
|
|
7
|
-
autoCleanup: [{}, { option: true }],
|
|
8
60
|
screenshotOnFailure: [false, { option: true }],
|
|
9
61
|
page: async ({ browser, storageState }, use) => {
|
|
10
62
|
const context = storageState
|
|
@@ -15,44 +67,6 @@ const test = test_1.test.extend({
|
|
|
15
67
|
await page.close();
|
|
16
68
|
await context.close();
|
|
17
69
|
},
|
|
18
|
-
// Auto-cleanup fixture: runs teardown after each test based on @cleanup:* tags
|
|
19
|
-
_autoCleanup: [async ({ page, autoCleanup }, use, testInfo) => {
|
|
20
|
-
await use();
|
|
21
|
-
// Only run cleanup when the test failed — avoids masking state for the next test
|
|
22
|
-
if (testInfo.status === testInfo.expectedStatus)
|
|
23
|
-
return;
|
|
24
|
-
if (autoCleanup.overlay) {
|
|
25
|
-
await page.keyboard.press('Escape').catch(() => { });
|
|
26
|
-
await page.locator('body').click({ position: { x: 1, y: 1 }, force: true }).catch(() => { });
|
|
27
|
-
const hasOverlay = await page.evaluate(`(() => {
|
|
28
|
-
const el = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2);
|
|
29
|
-
if (!el) return false;
|
|
30
|
-
let current = el;
|
|
31
|
-
while (current && current !== document.body) {
|
|
32
|
-
if (getComputedStyle(current).position === 'fixed') return true;
|
|
33
|
-
current = current.parentElement;
|
|
34
|
-
}
|
|
35
|
-
return false;
|
|
36
|
-
})()`).catch(() => false);
|
|
37
|
-
if (hasOverlay) {
|
|
38
|
-
await page.keyboard.press('Escape').catch(() => { });
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
if (autoCleanup.forms) {
|
|
42
|
-
await page.evaluate(`(() => {
|
|
43
|
-
document.querySelectorAll('input:not([type=hidden]):not([type=submit])').forEach(el => { el.value = ''; });
|
|
44
|
-
document.querySelectorAll('textarea').forEach(el => { el.value = ''; });
|
|
45
|
-
document.querySelectorAll('select').forEach(el => { el.selectedIndex = 0; });
|
|
46
|
-
})()`).catch(() => { });
|
|
47
|
-
}
|
|
48
|
-
if (autoCleanup.scroll) {
|
|
49
|
-
await page.evaluate('window.scrollTo(0, 0)').catch(() => { });
|
|
50
|
-
}
|
|
51
|
-
if (autoCleanup.storage) {
|
|
52
|
-
await page.evaluate('sessionStorage.clear()').catch(() => { });
|
|
53
|
-
}
|
|
54
|
-
}, { auto: true }],
|
|
55
|
-
// Auto-screenshot fixture: captures screenshot on test failure
|
|
56
70
|
_autoScreenshot: [async ({ page, screenshotOnFailure }, use, testInfo) => {
|
|
57
71
|
await use();
|
|
58
72
|
if (screenshotOnFailure && testInfo.status !== testInfo.expectedStatus) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-base.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-base.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"specs-base.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-base.ts"],"names":[],"mappings":";;;AA0FuB,kCAAW;AA1FlC,2CAAmE;AA0FpD,uFA1FQ,aAAM,OA0FR;AAjFrB,KAAK,UAAU,WAAW,CAAC,IAAU,EAAE,MAAqB;IAC1D,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACpD,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC5F,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC;;;;;;;;;SASlC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;SAwBf,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,IAAI,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,IAAI,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED,MAAM,IAAI,GAAG,WAAI,CAAC,MAAM,CAGrB;IACD,mBAAmB,EAAE,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAE9C,IAAI,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,GAAG,EAAE,EAAE;QAC7C,MAAM,OAAO,GAAG,YAAY;YAC1B,CAAC,CAAC,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;YAC5C,CAAC,CAAC,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC;QAChB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACnB,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE;YACvE,MAAM,GAAG,EAAE,CAAC;YAEZ,IAAI,mBAAmB,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,cAAc,EAAE,CAAC;gBACvE,MAAM,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE;oBAClC,IAAI,EAAE,MAAM,IAAI,CAAC,UAAU,EAAE;oBAC7B,WAAW,EAAE,WAAW;iBACzB,CAAC,CAAC;YACL,CAAC;QACH,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;CACnB,CAAC,CAAC;AAEM,oBAAI"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { test as base, expect } from '@playwright/test';
|
|
1
|
+
import { test as base, expect, type Page } from '@playwright/test';
|
|
2
2
|
|
|
3
3
|
type CleanupConfig = {
|
|
4
4
|
overlay?: boolean;
|
|
@@ -7,13 +7,63 @@ type CleanupConfig = {
|
|
|
7
7
|
storage?: boolean;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
+
async function cleanupPage(page: Page, config: CleanupConfig): Promise<void> {
|
|
11
|
+
if (config.overlay) {
|
|
12
|
+
await page.keyboard.press('Escape').catch(() => {});
|
|
13
|
+
await page.locator('body').click({ position: { x: 1, y: 1 }, force: true }).catch(() => {});
|
|
14
|
+
const hasOverlay = await page.evaluate(`(() => {
|
|
15
|
+
const el = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2);
|
|
16
|
+
if (!el) return false;
|
|
17
|
+
let current = el;
|
|
18
|
+
while (current && current !== document.body) {
|
|
19
|
+
if (getComputedStyle(current).position === 'fixed') return true;
|
|
20
|
+
current = current.parentElement;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
})()`).catch(() => false);
|
|
24
|
+
if (hasOverlay) {
|
|
25
|
+
await page.keyboard.press('Escape').catch(() => {});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (config.forms) {
|
|
29
|
+
await page.evaluate(`(() => {
|
|
30
|
+
const inputSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;
|
|
31
|
+
const textareaSetter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;
|
|
32
|
+
document.querySelectorAll('input:not([type=hidden]):not([type=submit]):not([type=checkbox]):not([type=radio])').forEach(el => {
|
|
33
|
+
inputSetter?.call(el, '');
|
|
34
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
35
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
36
|
+
});
|
|
37
|
+
document.querySelectorAll('textarea').forEach(el => {
|
|
38
|
+
textareaSetter?.call(el, '');
|
|
39
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
40
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
41
|
+
});
|
|
42
|
+
document.querySelectorAll('select').forEach(el => {
|
|
43
|
+
el.selectedIndex = 0;
|
|
44
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
45
|
+
});
|
|
46
|
+
document.querySelectorAll('input[type=checkbox]').forEach(el => {
|
|
47
|
+
if (el.checked !== el.defaultChecked) {
|
|
48
|
+
el.checked = el.defaultChecked;
|
|
49
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
document.querySelectorAll('form').forEach(f => f.reset());
|
|
53
|
+
})()`).catch(() => {});
|
|
54
|
+
}
|
|
55
|
+
if (config.scroll) {
|
|
56
|
+
await page.evaluate('window.scrollTo(0, 0)').catch(() => {});
|
|
57
|
+
}
|
|
58
|
+
if (config.storage) {
|
|
59
|
+
await page.evaluate('sessionStorage.clear()').catch(() => {});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
10
63
|
const test = base.extend<{
|
|
11
|
-
autoCleanup: CleanupConfig;
|
|
12
64
|
screenshotOnFailure: boolean;
|
|
13
|
-
_autoCleanup: void;
|
|
14
65
|
_autoScreenshot: void;
|
|
15
66
|
}>({
|
|
16
|
-
autoCleanup: [{}, { option: true }],
|
|
17
67
|
screenshotOnFailure: [false, { option: true }],
|
|
18
68
|
|
|
19
69
|
page: async ({ browser, storageState }, use) => {
|
|
@@ -26,46 +76,6 @@ const test = base.extend<{
|
|
|
26
76
|
await context.close();
|
|
27
77
|
},
|
|
28
78
|
|
|
29
|
-
// Auto-cleanup fixture: runs teardown after each test based on @cleanup:* tags
|
|
30
|
-
_autoCleanup: [async ({ page, autoCleanup }, use, testInfo) => {
|
|
31
|
-
await use();
|
|
32
|
-
|
|
33
|
-
// Only run cleanup when the test failed — avoids masking state for the next test
|
|
34
|
-
if (testInfo.status === testInfo.expectedStatus) return;
|
|
35
|
-
|
|
36
|
-
if (autoCleanup.overlay) {
|
|
37
|
-
await page.keyboard.press('Escape').catch(() => {});
|
|
38
|
-
await page.locator('body').click({ position: { x: 1, y: 1 }, force: true }).catch(() => {});
|
|
39
|
-
const hasOverlay = await page.evaluate(`(() => {
|
|
40
|
-
const el = document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2);
|
|
41
|
-
if (!el) return false;
|
|
42
|
-
let current = el;
|
|
43
|
-
while (current && current !== document.body) {
|
|
44
|
-
if (getComputedStyle(current).position === 'fixed') return true;
|
|
45
|
-
current = current.parentElement;
|
|
46
|
-
}
|
|
47
|
-
return false;
|
|
48
|
-
})()`).catch(() => false);
|
|
49
|
-
if (hasOverlay) {
|
|
50
|
-
await page.keyboard.press('Escape').catch(() => {});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (autoCleanup.forms) {
|
|
54
|
-
await page.evaluate(`(() => {
|
|
55
|
-
document.querySelectorAll('input:not([type=hidden]):not([type=submit])').forEach(el => { el.value = ''; });
|
|
56
|
-
document.querySelectorAll('textarea').forEach(el => { el.value = ''; });
|
|
57
|
-
document.querySelectorAll('select').forEach(el => { el.selectedIndex = 0; });
|
|
58
|
-
})()`).catch(() => {});
|
|
59
|
-
}
|
|
60
|
-
if (autoCleanup.scroll) {
|
|
61
|
-
await page.evaluate('window.scrollTo(0, 0)').catch(() => {});
|
|
62
|
-
}
|
|
63
|
-
if (autoCleanup.storage) {
|
|
64
|
-
await page.evaluate('sessionStorage.clear()').catch(() => {});
|
|
65
|
-
}
|
|
66
|
-
}, { auto: true }],
|
|
67
|
-
|
|
68
|
-
// Auto-screenshot fixture: captures screenshot on test failure
|
|
69
79
|
_autoScreenshot: [async ({ page, screenshotOnFailure }, use, testInfo) => {
|
|
70
80
|
await use();
|
|
71
81
|
|
|
@@ -78,4 +88,4 @@ const test = base.extend<{
|
|
|
78
88
|
}, { auto: true }],
|
|
79
89
|
});
|
|
80
90
|
|
|
81
|
-
export { test, expect };
|
|
91
|
+
export { test, expect, cleanupPage };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-test-data.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-test-data.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"specs-test-data.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-test-data.ts"],"names":[],"mappings":"AAKA,qBAAa,cAAc;IACzB,OAAO,CAAC,IAAI,CAAsB;IAElC,OAAO;IAIP;;;;;;;;OAQG;IACH,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc;IAqBpE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;CAczB"}
|
|
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.TestDataLoader = void 0;
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
|
+
const crypto = __importStar(require("crypto"));
|
|
42
43
|
const yaml_1 = __importDefault(require("yaml"));
|
|
43
44
|
class TestDataLoader {
|
|
44
45
|
constructor(data) {
|
|
@@ -68,6 +69,7 @@ class TestDataLoader {
|
|
|
68
69
|
if (envData)
|
|
69
70
|
data = deepMerge(data, envData);
|
|
70
71
|
}
|
|
72
|
+
data = resolveDynamicVars(data);
|
|
71
73
|
return new TestDataLoader(data);
|
|
72
74
|
}
|
|
73
75
|
get(key) {
|
|
@@ -105,4 +107,45 @@ function deepMerge(base, override) {
|
|
|
105
107
|
}
|
|
106
108
|
return result;
|
|
107
109
|
}
|
|
110
|
+
function resolveDynamicVars(data) {
|
|
111
|
+
const ts = String(Date.now());
|
|
112
|
+
const uid = crypto.randomUUID();
|
|
113
|
+
const now = new Date();
|
|
114
|
+
const date = now.toISOString().split('T')[0];
|
|
115
|
+
const datetime = now.toISOString();
|
|
116
|
+
function resolveValue(value) {
|
|
117
|
+
if (typeof value === 'string') {
|
|
118
|
+
return value.replace(/\{\{\$(\w+)(?::([^}]*))?\}\}/g, (match, name, args) => {
|
|
119
|
+
switch (name) {
|
|
120
|
+
case 'timestamp':
|
|
121
|
+
return ts;
|
|
122
|
+
case 'uuid':
|
|
123
|
+
return uid;
|
|
124
|
+
case 'random': {
|
|
125
|
+
const [min, max] = (args || '1:9999').split(':').map(Number);
|
|
126
|
+
return String(Math.floor(Math.random() * (max - min + 1)) + min);
|
|
127
|
+
}
|
|
128
|
+
case 'date':
|
|
129
|
+
return date;
|
|
130
|
+
case 'datetime':
|
|
131
|
+
return datetime;
|
|
132
|
+
default:
|
|
133
|
+
return match;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (Array.isArray(value)) {
|
|
138
|
+
return value.map(resolveValue);
|
|
139
|
+
}
|
|
140
|
+
if (value && typeof value === 'object') {
|
|
141
|
+
const resolved = {};
|
|
142
|
+
for (const [k, v] of Object.entries(value)) {
|
|
143
|
+
resolved[k] = resolveValue(v);
|
|
144
|
+
}
|
|
145
|
+
return resolved;
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
return resolveValue(data);
|
|
150
|
+
}
|
|
108
151
|
//# sourceMappingURL=specs-test-data.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-test-data.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-test-data.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAC7B,gDAAwB;AAExB,MAAa,cAAc;IAGzB,YAAoB,IAAyB;QAC3C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,IAAI,CAAC,UAAkB,EAAE,WAAmB;QACjD,IAAI,OAAe,CAAC;QACpB,IAAI,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QAC/E,CAAC;QACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;QAEnC,IAAI,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,WAAW,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAEzE,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,WAAW,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC;YAC/E,IAAI,OAAO;gBAAE,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QAED,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,OAAO,GAAQ,IAAI,CAAC,IAAI,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;gBACnD,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,gBAAgB,IAAI,IAAI,CAAC,CAAC;YAC3E,CAAC;YACD,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QACD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"specs-test-data.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-test-data.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAC7B,+CAAiC;AACjC,gDAAwB;AAExB,MAAa,cAAc;IAGzB,YAAoB,IAAyB;QAC3C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,IAAI,CAAC,UAAkB,EAAE,WAAmB;QACjD,IAAI,OAAe,CAAC;QACpB,IAAI,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QAC/E,CAAC;QACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;QAEnC,IAAI,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,WAAW,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAEzE,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,WAAW,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC;YAC/E,IAAI,OAAO;gBAAE,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAEhC,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,OAAO,GAAQ,IAAI,CAAC,IAAI,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;gBACnD,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,gBAAgB,IAAI,IAAI,CAAC,CAAC;YAC3E,CAAC;YACD,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QACD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;CACF;AAnDD,wCAmDC;AAED,SAAS,YAAY,CAAC,QAAgB;IACpC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,OAAO,cAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;AACrC,CAAC;AAED,SAAS,SAAS,CAAC,IAAyB,EAAE,QAA6B;IACzE,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAC3D,MAAM,CAAC,GAAG,CAAC,IAAI,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAClF,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAyB;IACnD,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAChC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAEnC,SAAS,YAAY,CAAC,KAAU;QAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,+BAA+B,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;gBAC1E,QAAQ,IAAI,EAAE,CAAC;oBACb,KAAK,WAAW;wBACd,OAAO,EAAE,CAAC;oBACZ,KAAK,MAAM;wBACT,OAAO,GAAG,CAAC;oBACb,KAAK,QAAQ,CAAC,CAAC,CAAC;wBACd,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;wBAC7D,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;oBACnE,CAAC;oBACD,KAAK,MAAM;wBACT,OAAO,IAAI,CAAC;oBACd,KAAK,UAAU;wBACb,OAAO,QAAQ,CAAC;oBAClB;wBACE,OAAO,KAAK,CAAC;gBACjB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAwB,EAAE,CAAC;YACzC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3C,QAAQ,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;YAChC,CAAC;YACD,OAAO,QAAQ,CAAC;QAClB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import * as crypto from 'crypto';
|
|
3
4
|
import yaml from 'yaml';
|
|
4
5
|
|
|
5
6
|
export class TestDataLoader {
|
|
@@ -34,6 +35,8 @@ export class TestDataLoader {
|
|
|
34
35
|
if (envData) data = deepMerge(data, envData);
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
data = resolveDynamicVars(data);
|
|
39
|
+
|
|
37
40
|
return new TestDataLoader(data);
|
|
38
41
|
}
|
|
39
42
|
|
|
@@ -71,3 +74,47 @@ function deepMerge(base: Record<string, any>, override: Record<string, any>): Re
|
|
|
71
74
|
}
|
|
72
75
|
return result;
|
|
73
76
|
}
|
|
77
|
+
|
|
78
|
+
function resolveDynamicVars(data: Record<string, any>): Record<string, any> {
|
|
79
|
+
const ts = String(Date.now());
|
|
80
|
+
const uid = crypto.randomUUID();
|
|
81
|
+
const now = new Date();
|
|
82
|
+
const date = now.toISOString().split('T')[0];
|
|
83
|
+
const datetime = now.toISOString();
|
|
84
|
+
|
|
85
|
+
function resolveValue(value: any): any {
|
|
86
|
+
if (typeof value === 'string') {
|
|
87
|
+
return value.replace(/\{\{\$(\w+)(?::([^}]*))?\}\}/g, (match, name, args) => {
|
|
88
|
+
switch (name) {
|
|
89
|
+
case 'timestamp':
|
|
90
|
+
return ts;
|
|
91
|
+
case 'uuid':
|
|
92
|
+
return uid;
|
|
93
|
+
case 'random': {
|
|
94
|
+
const [min, max] = (args || '1:9999').split(':').map(Number);
|
|
95
|
+
return String(Math.floor(Math.random() * (max - min + 1)) + min);
|
|
96
|
+
}
|
|
97
|
+
case 'date':
|
|
98
|
+
return date;
|
|
99
|
+
case 'datetime':
|
|
100
|
+
return datetime;
|
|
101
|
+
default:
|
|
102
|
+
return match;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
return value.map(resolveValue);
|
|
108
|
+
}
|
|
109
|
+
if (value && typeof value === 'object') {
|
|
110
|
+
const resolved: Record<string, any> = {};
|
|
111
|
+
for (const [k, v] of Object.entries(value)) {
|
|
112
|
+
resolved[k] = resolveValue(v);
|
|
113
|
+
}
|
|
114
|
+
return resolved;
|
|
115
|
+
}
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return resolveValue(data);
|
|
120
|
+
}
|
package/package.json
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -21,6 +21,9 @@ export interface TestFileData {
|
|
|
21
21
|
runtimeData?: boolean; // --runtime-data flag: testData.get() instead of hardcoded values
|
|
22
22
|
screenName?: string; // Screen name for TestDataLoader.load()
|
|
23
23
|
featureFileName?: string; // Feature file name for TestDataLoader.load()
|
|
24
|
+
isParallel?: boolean; // @parallel tag: fresh page per test (opt-out from serial default)
|
|
25
|
+
cleanup?: { overlay?: boolean; forms?: boolean; scroll?: boolean; storage?: boolean };
|
|
26
|
+
backgroundSteps?: Array<{ comment?: string; code: string }>; // Raw background steps for serial beforeAll
|
|
24
27
|
scenarios: string[];
|
|
25
28
|
authGroups?: AuthGroup[]; // Grouped by auth role for nested describes
|
|
26
29
|
singleAuthRole?: string; // Auth role when all scenarios share the same role
|
|
@@ -30,6 +33,8 @@ export interface ScenarioData {
|
|
|
30
33
|
scenarioName: string;
|
|
31
34
|
steps: Array<{ comment?: string; code: string }>;
|
|
32
35
|
authRole?: string; // Auth role for storage state
|
|
36
|
+
isParallel?: boolean; // @parallel: use fresh page from fixture
|
|
37
|
+
tags?: string; // Pass-through tags for Playwright { tag: [...] }, e.g. "'@smoke', '@critical'"
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
export interface StepTemplateData {
|
|
@@ -57,7 +62,7 @@ export interface TestGeneratorAdapter {
|
|
|
57
62
|
// Template rendering methods
|
|
58
63
|
renderTestFile(data: TestFileData): string;
|
|
59
64
|
renderScenario(data: ScenarioData): string;
|
|
60
|
-
renderImports(options?: { runtimeData?: boolean; basePath?: string }): string;
|
|
65
|
+
renderImports(options?: { runtimeData?: boolean; basePath?: string; isParallel?: boolean; needsCleanupImport?: boolean }): string;
|
|
61
66
|
renderBeforeEach(data: { steps: Array<{ comment?: string; code: string }> }): string;
|
|
62
67
|
renderBeforeAll(data: { steps: Array<{ comment?: string; code: string }> }): string;
|
|
63
68
|
renderAfterEach(data: { steps: Array<{ comment?: string; code: string }> }): string;
|
|
@@ -26,7 +26,7 @@ export class PlaywrightAdapter implements TestGeneratorAdapter {
|
|
|
26
26
|
return this.templateEngine.renderScenario(data);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
renderImports(options?: { runtimeData?: boolean; basePath?: string }): string {
|
|
29
|
+
renderImports(options?: { runtimeData?: boolean; basePath?: string; isParallel?: boolean; needsCleanupImport?: boolean }): string {
|
|
30
30
|
return this.templateEngine.renderImports(options);
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { test, expect } from '{{basePath}}/base';
|
|
1
|
+
import { test, expect{{#if needsCleanupImport}}, cleanupPage{{/if}} } from '{{basePath}}/base';
|
|
2
|
+
import { type Page, type BrowserContext } from '@playwright/test';
|
|
2
3
|
{{#if runtimeData}}
|
|
3
4
|
import { TestDataLoader } from '{{basePath}}/test-data';
|
|
4
5
|
{{/if}}
|
|
@@ -1,8 +1,27 @@
|
|
|
1
|
+
{{#if isParallel}}
|
|
2
|
+
{{#if tags}}
|
|
3
|
+
test('{{scenarioName}}', { tag: [{{{tags}}}] }, async ({ page }) => {
|
|
4
|
+
{{else}}
|
|
1
5
|
test('{{scenarioName}}', async ({ page }) => {
|
|
6
|
+
{{/if}}
|
|
7
|
+
{{#each steps}}
|
|
8
|
+
{{#if comment}}
|
|
9
|
+
// {{comment}}
|
|
10
|
+
{{/if}}
|
|
11
|
+
{{code}}
|
|
12
|
+
{{/each}}
|
|
13
|
+
});
|
|
14
|
+
{{else}}
|
|
15
|
+
{{#if tags}}
|
|
16
|
+
test('{{scenarioName}}', { tag: [{{{tags}}}] }, async () => {
|
|
17
|
+
{{else}}
|
|
18
|
+
test('{{scenarioName}}', async () => {
|
|
19
|
+
{{/if}}
|
|
2
20
|
{{#each steps}}
|
|
3
21
|
{{#if comment}}
|
|
4
22
|
// {{comment}}
|
|
5
23
|
{{/if}}
|
|
6
24
|
{{code}}
|
|
7
25
|
{{/each}}
|
|
8
|
-
});
|
|
26
|
+
});
|
|
27
|
+
{{/if}}
|
|
@@ -11,14 +11,11 @@ const testData = TestDataLoader.load('{{screenName}}', '{{featureFileName}}');
|
|
|
11
11
|
*/
|
|
12
12
|
{{/if}}
|
|
13
13
|
|
|
14
|
+
{{#if isParallel}}
|
|
14
15
|
test.describe('{{featureName}}', () => {
|
|
15
16
|
{{#if singleAuthRole}}
|
|
16
17
|
test.use({ storageState: 'specs/.auth/{{singleAuthRole}}.json' });
|
|
17
18
|
|
|
18
|
-
{{/if}}
|
|
19
|
-
{{#if cleanupConfig}}
|
|
20
|
-
test.use({ autoCleanup: { {{cleanupConfig}} } });
|
|
21
|
-
|
|
22
19
|
{{/if}}
|
|
23
20
|
{{#if screenshotOnFailure}}
|
|
24
21
|
test.use({ screenshotOnFailure: true });
|
|
@@ -66,3 +63,86 @@ test.describe('{{featureName}}', () => {
|
|
|
66
63
|
{{/each}}
|
|
67
64
|
{{/if}}
|
|
68
65
|
});
|
|
66
|
+
{{else}}
|
|
67
|
+
test.describe.serial('{{featureName}}', () => {
|
|
68
|
+
let page: Page;
|
|
69
|
+
let context: BrowserContext;
|
|
70
|
+
|
|
71
|
+
{{#if singleAuthRole}}
|
|
72
|
+
test.beforeAll(async ({ browser }) => {
|
|
73
|
+
context = await browser.newContext({ storageState: 'specs/.auth/{{singleAuthRole}}.json' });
|
|
74
|
+
page = await context.newPage();
|
|
75
|
+
{{#each backgroundSteps}}
|
|
76
|
+
{{#if comment}}
|
|
77
|
+
// {{comment}}
|
|
78
|
+
{{/if}}
|
|
79
|
+
{{code}}
|
|
80
|
+
{{/each}}
|
|
81
|
+
});
|
|
82
|
+
{{else}}
|
|
83
|
+
test.beforeAll(async ({ browser }) => {
|
|
84
|
+
context = await browser.newContext();
|
|
85
|
+
page = await context.newPage();
|
|
86
|
+
{{#each backgroundSteps}}
|
|
87
|
+
{{#if comment}}
|
|
88
|
+
// {{comment}}
|
|
89
|
+
{{/if}}
|
|
90
|
+
{{code}}
|
|
91
|
+
{{/each}}
|
|
92
|
+
});
|
|
93
|
+
{{/if}}
|
|
94
|
+
|
|
95
|
+
test.afterAll(async () => {
|
|
96
|
+
await page.close();
|
|
97
|
+
await context.close();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
{{#if cleanup}}
|
|
101
|
+
test.afterEach(async () => {
|
|
102
|
+
await cleanupPage(page, { {{cleanupConfig}} });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
{{/if}}
|
|
106
|
+
{{#if screenshotOnFailure}}
|
|
107
|
+
test.use({ screenshotOnFailure: true });
|
|
108
|
+
|
|
109
|
+
{{/if}}
|
|
110
|
+
{{#if beforeAll}}
|
|
111
|
+
{{beforeAll}}
|
|
112
|
+
|
|
113
|
+
{{/if}}
|
|
114
|
+
{{#if afterEach}}
|
|
115
|
+
{{afterEach}}
|
|
116
|
+
|
|
117
|
+
{{/if}}
|
|
118
|
+
{{#if afterAll}}
|
|
119
|
+
{{afterAll}}
|
|
120
|
+
|
|
121
|
+
{{/if}}
|
|
122
|
+
{{#if authGroups}}
|
|
123
|
+
{{#each authGroups}}
|
|
124
|
+
{{#if authRole}}
|
|
125
|
+
test.describe('{{authRole}}', () => {
|
|
126
|
+
test.use({ storageState: 'specs/.auth/{{authRole}}.json' });
|
|
127
|
+
|
|
128
|
+
{{#each scenarios}}
|
|
129
|
+
{{indent this 2}}
|
|
130
|
+
|
|
131
|
+
{{/each}}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
{{else}}
|
|
135
|
+
{{#each scenarios}}
|
|
136
|
+
{{this}}
|
|
137
|
+
|
|
138
|
+
{{/each}}
|
|
139
|
+
{{/if}}
|
|
140
|
+
{{/each}}
|
|
141
|
+
{{else}}
|
|
142
|
+
{{#each scenarios}}
|
|
143
|
+
{{this}}
|
|
144
|
+
|
|
145
|
+
{{/each}}
|
|
146
|
+
{{/if}}
|
|
147
|
+
});
|
|
148
|
+
{{/if}}
|