@sun-asterisk/sungen 2.6.0 → 2.6.2

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.
Files changed (104) hide show
  1. package/dist/cli/commands/dashboard.d.ts +10 -0
  2. package/dist/cli/commands/dashboard.d.ts.map +1 -0
  3. package/dist/cli/commands/dashboard.js +171 -0
  4. package/dist/cli/commands/dashboard.js.map +1 -0
  5. package/dist/cli/index.js +4 -2
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/dashboard/history-store.d.ts +27 -0
  8. package/dist/dashboard/history-store.d.ts.map +1 -0
  9. package/dist/dashboard/history-store.js +112 -0
  10. package/dist/dashboard/history-store.js.map +1 -0
  11. package/dist/dashboard/html-renderer.d.ts +30 -0
  12. package/dist/dashboard/html-renderer.d.ts.map +1 -0
  13. package/dist/dashboard/html-renderer.js +111 -0
  14. package/dist/dashboard/html-renderer.js.map +1 -0
  15. package/dist/dashboard/snapshot-builder.d.ts +30 -0
  16. package/dist/dashboard/snapshot-builder.d.ts.map +1 -0
  17. package/dist/dashboard/snapshot-builder.js +263 -0
  18. package/dist/dashboard/snapshot-builder.js.map +1 -0
  19. package/dist/dashboard/templates/index.html +287 -0
  20. package/dist/dashboard/types.d.ts +122 -0
  21. package/dist/dashboard/types.d.ts.map +1 -0
  22. package/dist/dashboard/types.js +11 -0
  23. package/dist/dashboard/types.js.map +1 -0
  24. package/dist/exporters/json-exporter.d.ts +25 -0
  25. package/dist/exporters/json-exporter.d.ts.map +1 -0
  26. package/dist/exporters/json-exporter.js +135 -0
  27. package/dist/exporters/json-exporter.js.map +1 -0
  28. package/dist/exporters/playwright-report-parser.d.ts +2 -1
  29. package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
  30. package/dist/exporters/playwright-report-parser.js +12 -5
  31. package/dist/exporters/playwright-report-parser.js.map +1 -1
  32. package/dist/exporters/spec-parser.d.ts.map +1 -1
  33. package/dist/exporters/spec-parser.js +8 -3
  34. package/dist/exporters/spec-parser.js.map +1 -1
  35. package/dist/generators/test-generator/adapters/adapter-interface.d.ts +15 -0
  36. package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
  37. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +2 -0
  38. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -1
  39. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -1
  40. package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +2 -1
  41. package/dist/generators/test-generator/adapters/playwright/templates/scenario.hbs +20 -1
  42. package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +84 -4
  43. package/dist/generators/test-generator/code-generator.d.ts +1 -0
  44. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  45. package/dist/generators/test-generator/code-generator.js +76 -6
  46. package/dist/generators/test-generator/code-generator.js.map +1 -1
  47. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -1
  48. package/dist/generators/test-generator/patterns/interaction-patterns.js +22 -3
  49. package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -1
  50. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +1 -1
  51. package/dist/generators/test-generator/patterns/navigation-patterns.js +8 -3
  52. package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -1
  53. package/dist/generators/test-generator/template-engine.d.ts +13 -0
  54. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  55. package/dist/generators/test-generator/template-engine.js +1 -1
  56. package/dist/generators/test-generator/template-engine.js.map +1 -1
  57. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  58. package/dist/orchestrator/screen-manager.js +3 -1
  59. package/dist/orchestrator/screen-manager.js.map +1 -1
  60. package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +70 -10
  61. package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +23 -0
  62. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +70 -10
  63. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +23 -0
  64. package/dist/orchestrator/templates/playwright.config.d.ts.map +1 -1
  65. package/dist/orchestrator/templates/playwright.config.js +9 -1
  66. package/dist/orchestrator/templates/playwright.config.js.map +1 -1
  67. package/dist/orchestrator/templates/playwright.config.ts +11 -1
  68. package/dist/orchestrator/templates/specs-base.d.ts +3 -4
  69. package/dist/orchestrator/templates/specs-base.d.ts.map +1 -1
  70. package/dist/orchestrator/templates/specs-base.js +53 -39
  71. package/dist/orchestrator/templates/specs-base.js.map +1 -1
  72. package/dist/orchestrator/templates/specs-base.ts +55 -45
  73. package/dist/orchestrator/templates/specs-test-data.d.ts.map +1 -1
  74. package/dist/orchestrator/templates/specs-test-data.js +43 -0
  75. package/dist/orchestrator/templates/specs-test-data.js.map +1 -1
  76. package/dist/orchestrator/templates/specs-test-data.ts +47 -0
  77. package/package.json +4 -3
  78. package/src/cli/commands/dashboard.ts +158 -0
  79. package/src/cli/index.ts +4 -2
  80. package/src/dashboard/history-store.ts +86 -0
  81. package/src/dashboard/html-renderer.ts +90 -0
  82. package/src/dashboard/snapshot-builder.ts +273 -0
  83. package/src/dashboard/templates/index.html +287 -0
  84. package/src/dashboard/types.ts +148 -0
  85. package/src/exporters/json-exporter.ts +162 -0
  86. package/src/exporters/playwright-report-parser.ts +12 -5
  87. package/src/exporters/spec-parser.ts +8 -3
  88. package/src/generators/test-generator/adapters/adapter-interface.ts +6 -1
  89. package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +1 -1
  90. package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +2 -1
  91. package/src/generators/test-generator/adapters/playwright/templates/scenario.hbs +20 -1
  92. package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +84 -4
  93. package/src/generators/test-generator/code-generator.ts +88 -7
  94. package/src/generators/test-generator/patterns/interaction-patterns.ts +25 -3
  95. package/src/generators/test-generator/patterns/navigation-patterns.ts +8 -3
  96. package/src/generators/test-generator/template-engine.ts +5 -2
  97. package/src/orchestrator/screen-manager.ts +3 -1
  98. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +70 -10
  99. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +23 -0
  100. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +70 -10
  101. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +23 -0
  102. package/src/orchestrator/templates/playwright.config.ts +11 -1
  103. package/src/orchestrator/templates/specs-base.ts +55 -45
  104. package/src/orchestrator/templates/specs-test-data.ts +47 -0
@@ -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,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
+ }