@sun-asterisk/sungen 1.0.5 → 1.0.7

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 (85) hide show
  1. package/README.md +173 -1
  2. package/dist/cli/index.js +155 -4
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/core/validator/data-validator.d.ts +38 -0
  5. package/dist/core/validator/data-validator.d.ts.map +1 -0
  6. package/dist/core/validator/data-validator.js +212 -0
  7. package/dist/core/validator/data-validator.js.map +1 -0
  8. package/dist/core/validator/feature-validator.d.ts +27 -0
  9. package/dist/core/validator/feature-validator.d.ts.map +1 -0
  10. package/dist/core/validator/feature-validator.js +182 -0
  11. package/dist/core/validator/feature-validator.js.map +1 -0
  12. package/dist/core/validator/index.d.ts +46 -0
  13. package/dist/core/validator/index.d.ts.map +1 -0
  14. package/dist/core/validator/index.js +17 -0
  15. package/dist/core/validator/index.js.map +1 -0
  16. package/dist/core/validator/screen-validator.d.ts +35 -0
  17. package/dist/core/validator/screen-validator.d.ts.map +1 -0
  18. package/dist/core/validator/screen-validator.js +195 -0
  19. package/dist/core/validator/screen-validator.js.map +1 -0
  20. package/dist/core/validator/selector-validator.d.ts +35 -0
  21. package/dist/core/validator/selector-validator.d.ts.map +1 -0
  22. package/dist/core/validator/selector-validator.js +210 -0
  23. package/dist/core/validator/selector-validator.js.map +1 -0
  24. package/dist/generators/cli.js +1 -1
  25. package/dist/generators/gherkin-parser/index.d.ts +1 -0
  26. package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
  27. package/dist/generators/gherkin-parser/index.js +3 -0
  28. package/dist/generators/gherkin-parser/index.js.map +1 -1
  29. package/dist/generators/scaffold-generator/index.d.ts +25 -2
  30. package/dist/generators/scaffold-generator/index.d.ts.map +1 -1
  31. package/dist/generators/scaffold-generator/index.js +157 -14
  32. package/dist/generators/scaffold-generator/index.js.map +1 -1
  33. package/dist/generators/test-generator/adapters/adapter-interface.d.ts +2 -0
  34. package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
  35. package/dist/generators/test-generator/adapters/playwright/templates/scenario.hbs +4 -0
  36. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/radio-select-action.hbs +1 -0
  37. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -0
  38. package/dist/generators/test-generator/auth-setup-generator.d.ts +18 -0
  39. package/dist/generators/test-generator/auth-setup-generator.d.ts.map +1 -0
  40. package/dist/generators/test-generator/auth-setup-generator.js +82 -0
  41. package/dist/generators/test-generator/auth-setup-generator.js.map +1 -0
  42. package/dist/generators/test-generator/code-generator.d.ts +2 -0
  43. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  44. package/dist/generators/test-generator/code-generator.js +130 -5
  45. package/dist/generators/test-generator/code-generator.js.map +1 -1
  46. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
  47. package/dist/generators/test-generator/patterns/assertion-patterns.js +30 -0
  48. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
  49. package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +1 -1
  50. package/dist/generators/test-generator/patterns/form-patterns.js +25 -5
  51. package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -1
  52. package/dist/generators/test-generator/templates/auth-setup.ts.hbs +36 -0
  53. package/dist/input/cli-adapter.d.ts +8 -0
  54. package/dist/input/cli-adapter.d.ts.map +1 -1
  55. package/dist/input/cli-adapter.js +31 -1
  56. package/dist/input/cli-adapter.js.map +1 -1
  57. package/dist/orchestrator/pipeline.d.ts.map +1 -1
  58. package/dist/orchestrator/pipeline.js +31 -22
  59. package/dist/orchestrator/pipeline.js.map +1 -1
  60. package/dist/tools/auth-maker.d.ts +74 -0
  61. package/dist/tools/auth-maker.d.ts.map +1 -0
  62. package/dist/tools/auth-maker.js +420 -0
  63. package/dist/tools/auth-maker.js.map +1 -0
  64. package/package.json +2 -2
  65. package/src/cli/index.ts +166 -4
  66. package/src/core/validator/data-validator.ts +202 -0
  67. package/src/core/validator/feature-validator.ts +176 -0
  68. package/src/core/validator/index.ts +57 -0
  69. package/src/core/validator/screen-validator.ts +209 -0
  70. package/src/core/validator/selector-validator.ts +208 -0
  71. package/src/generators/cli.ts +1 -1
  72. package/src/generators/gherkin-parser/index.ts +5 -0
  73. package/src/generators/scaffold-generator/index.ts +179 -19
  74. package/src/generators/test-generator/adapters/adapter-interface.ts +2 -0
  75. package/src/generators/test-generator/adapters/playwright/templates/scenario.hbs +4 -0
  76. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/radio-select-action.hbs +1 -0
  77. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -0
  78. package/src/generators/test-generator/auth-setup-generator.ts +59 -0
  79. package/src/generators/test-generator/code-generator.ts +173 -6
  80. package/src/generators/test-generator/patterns/assertion-patterns.ts +34 -1
  81. package/src/generators/test-generator/patterns/form-patterns.ts +27 -8
  82. package/src/generators/test-generator/templates/auth-setup.ts.hbs +36 -0
  83. package/src/input/cli-adapter.ts +33 -1
  84. package/src/orchestrator/pipeline.ts +39 -24
  85. package/src/tools/auth-maker.ts +467 -0
@@ -0,0 +1,420 @@
1
+ "use strict";
2
+ /**
3
+ * Auth Maker - Generate authentication state for E2E tests
4
+ *
5
+ * Usage: sungen makeauth <name>
6
+ * Example: sungen makeauth admin -> generates specs/.auth/admin.json
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.AuthMaker = void 0;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const readline = __importStar(require("readline"));
46
+ const test_1 = require("@playwright/test");
47
+ /**
48
+ * Extract baseURL from playwright.config.ts
49
+ * Returns { url, source } or null
50
+ */
51
+ function getPlaywrightBaseURL(configPath) {
52
+ const possiblePaths = configPath
53
+ ? [configPath]
54
+ : [
55
+ path.join(process.cwd(), 'playwright.config.ts'),
56
+ path.join(process.cwd(), 'playwright.config.js'),
57
+ ];
58
+ for (const configFile of possiblePaths) {
59
+ if (fs.existsSync(configFile)) {
60
+ try {
61
+ const content = fs.readFileSync(configFile, 'utf-8');
62
+ // Match baseURL in use: { baseURL: '...' } or baseURL: '...'
63
+ const patterns = [
64
+ /baseURL:\s*['"]([^'"]+)['"]/,
65
+ /baseURL:\s*`([^`]+)`/,
66
+ ];
67
+ for (const pattern of patterns) {
68
+ const match = content.match(pattern);
69
+ if (match) {
70
+ return {
71
+ url: match[1],
72
+ source: path.basename(configFile),
73
+ };
74
+ }
75
+ }
76
+ }
77
+ catch {
78
+ // Ignore read errors
79
+ }
80
+ }
81
+ }
82
+ return null;
83
+ }
84
+ class AuthMaker {
85
+ constructor(config = {}) {
86
+ this.activeReadline = null;
87
+ this.baseURLSource = 'default';
88
+ // Priority: config > playwright.config.ts > env > default
89
+ const playwrightConfig = getPlaywrightBaseURL();
90
+ this.config = {
91
+ baseURL: config.baseURL || playwrightConfig?.url || process.env.APP_BASE_URL || 'http://localhost:3000',
92
+ loginPath: config.loginPath || '/login',
93
+ outputDir: config.outputDir || 'specs/.auth',
94
+ timeout: config.timeout || 180000, // 3 minutes for overall timeout
95
+ navigationTimeout: config.navigationTimeout || 180000, // 3 minutes for page navigation
96
+ stabilityWait: config.stabilityWait || 5000, // 5 seconds for URL stability check
97
+ successSelector: config.successSelector,
98
+ };
99
+ // Store source for display
100
+ if (!config.baseURL && playwrightConfig) {
101
+ this.baseURLSource = playwrightConfig.source;
102
+ }
103
+ else if (!config.baseURL && process.env.APP_BASE_URL) {
104
+ this.baseURLSource = 'APP_BASE_URL env';
105
+ }
106
+ else if (config.baseURL) {
107
+ this.baseURLSource = 'config';
108
+ }
109
+ else {
110
+ this.baseURLSource = 'default';
111
+ }
112
+ }
113
+ /**
114
+ * Generate auth state by opening browser for manual login
115
+ */
116
+ async makeAuth(options) {
117
+ const { name, headless = false } = options;
118
+ // Merge options with config
119
+ const baseURL = options.baseURL || this.config.baseURL;
120
+ const loginPath = options.loginPath || this.config.loginPath;
121
+ const outputDir = options.outputDir || this.config.outputDir;
122
+ const timeout = options.timeout || this.config.timeout;
123
+ const navigationTimeout = options.navigationTimeout || this.config.navigationTimeout;
124
+ const stabilityWait = options.stabilityWait || this.config.stabilityWait;
125
+ const successSelector = options.successSelector || this.config.successSelector;
126
+ const loginURL = `${baseURL}${loginPath}`;
127
+ const authFile = path.join(outputDir, `${name}.json`);
128
+ console.log('');
129
+ console.log('========================================');
130
+ console.log(` Auth Maker: ${name}`);
131
+ console.log('========================================');
132
+ console.log('');
133
+ console.log(` Base URL: ${baseURL} (from ${this.baseURLSource})`);
134
+ console.log(` Login URL: ${loginURL}`);
135
+ console.log(` Output: ${authFile}`);
136
+ console.log('');
137
+ // Create output directory
138
+ if (!fs.existsSync(outputDir)) {
139
+ fs.mkdirSync(outputDir, { recursive: true });
140
+ }
141
+ // Check if auth file already exists
142
+ if (fs.existsSync(authFile)) {
143
+ const overwrite = await this.askQuestion(`⚠️ File ${authFile} already exists. Overwrite? (y/N): `);
144
+ if (overwrite.toLowerCase() !== 'y') {
145
+ console.log('Cancelled.');
146
+ return authFile;
147
+ }
148
+ }
149
+ let browser = null;
150
+ let context = null;
151
+ try {
152
+ console.log('🚀 Opening browser...');
153
+ browser = await test_1.chromium.launch({
154
+ headless,
155
+ slowMo: 50,
156
+ args: ['--start-maximized'],
157
+ });
158
+ context = await browser.newContext({
159
+ viewport: { width: 1280, height: 800 },
160
+ });
161
+ const page = await context.newPage();
162
+ // Navigate to login page
163
+ console.log(`📍 Navigating to ${loginURL}...`);
164
+ await page.goto(loginURL, { timeout: navigationTimeout });
165
+ await page.waitForLoadState('domcontentloaded');
166
+ // Check if already logged in (URL changed)
167
+ const currentURL = page.url();
168
+ if (!currentURL.includes(loginPath)) {
169
+ console.log('');
170
+ console.log('✅ Already logged in!');
171
+ await this.saveAuthState(context, authFile);
172
+ return authFile;
173
+ }
174
+ console.log('');
175
+ console.log('========================================');
176
+ console.log(' MANUAL LOGIN REQUIRED');
177
+ console.log('========================================');
178
+ console.log('');
179
+ console.log(' 1. Login in the browser window');
180
+ console.log(' 2. Complete any 2FA if required');
181
+ console.log(' 3. Browser will auto-close when redirected back');
182
+ console.log(' (Or press ENTER to save manually)');
183
+ console.log('');
184
+ console.log(` Timeout: ${Math.round(timeout / 60000)} minutes`);
185
+ console.log('========================================');
186
+ console.log('');
187
+ // Wait for callback to baseURL OR user press Enter
188
+ const callbackPromise = this.waitForCallback(page, baseURL, loginPath, timeout, stabilityWait);
189
+ const enterPromise = this.waitForEnter();
190
+ await Promise.race([callbackPromise, enterPromise]);
191
+ // Cancel readline if callback was detected (so process can exit)
192
+ this.cancelWaitForEnter();
193
+ // Verify login was successful
194
+ const finalURL = page.url();
195
+ if (finalURL.includes(loginPath)) {
196
+ console.log('');
197
+ console.log('⚠️ Still on login page. Saving state anyway...');
198
+ }
199
+ // Save auth state
200
+ await this.saveAuthState(context, authFile);
201
+ return authFile;
202
+ }
203
+ catch (error) {
204
+ console.error('');
205
+ console.error('❌ Error:', error.message);
206
+ throw error;
207
+ }
208
+ finally {
209
+ if (context)
210
+ await context.close();
211
+ if (browser)
212
+ await browser.close();
213
+ }
214
+ }
215
+ /**
216
+ * Detect SSO provider from URL
217
+ */
218
+ detectSSOProvider(url) {
219
+ const providers = [
220
+ { pattern: /accounts\.google\.com|googleapis\.com/i, name: 'Google', icon: '🔵' },
221
+ { pattern: /facebook\.com|fb\.com/i, name: 'Facebook', icon: '🔷' },
222
+ { pattern: /github\.com/i, name: 'GitHub', icon: '⚫' },
223
+ { pattern: /login\.microsoftonline\.com|login\.live\.com|microsoft\.com/i, name: 'Microsoft', icon: '🟦' },
224
+ { pattern: /appleid\.apple\.com/i, name: 'Apple', icon: '🍎' },
225
+ { pattern: /twitter\.com|x\.com/i, name: 'X (Twitter)', icon: '🐦' },
226
+ { pattern: /linkedin\.com/i, name: 'LinkedIn', icon: '🔗' },
227
+ { pattern: /\.okta\.com/i, name: 'Okta', icon: '🔐' },
228
+ { pattern: /\.auth0\.com/i, name: 'Auth0', icon: '🔑' },
229
+ { pattern: /gitlab\.com/i, name: 'GitLab', icon: '🦊' },
230
+ { pattern: /bitbucket\.org|atlassian\.com/i, name: 'Atlassian', icon: '🔹' },
231
+ { pattern: /slack\.com/i, name: 'Slack', icon: '💬' },
232
+ { pattern: /discord\.com/i, name: 'Discord', icon: '🎮' },
233
+ { pattern: /amazon\.com|amazoncognito\.com/i, name: 'Amazon', icon: '📦' },
234
+ { pattern: /yahoo\.com/i, name: 'Yahoo', icon: '🟣' },
235
+ ];
236
+ for (const provider of providers) {
237
+ if (provider.pattern.test(url)) {
238
+ return `${provider.icon} ${provider.name}`;
239
+ }
240
+ }
241
+ // Try to extract domain name for unknown providers
242
+ try {
243
+ const domain = new URL(url).hostname;
244
+ return `🔐 ${domain}`;
245
+ }
246
+ catch {
247
+ return '🔐 Unknown SSO';
248
+ }
249
+ }
250
+ /**
251
+ * Wait for callback to app URL after SSO login
252
+ * Flow: Wait for user to leave baseURL (click SSO) -> Wait for return to baseURL (not login page)
253
+ */
254
+ async waitForCallback(page, baseURL, loginPath, timeout = 180000, stabilityWait = 5000) {
255
+ const startTime = Date.now();
256
+ const checkInterval = 1000; // Check every 1 second
257
+ let hasLeftBaseURL = false;
258
+ let detectedProvider = '';
259
+ let callbackDetectedAt = 0;
260
+ let lastCallbackURL = '';
261
+ console.log('⏳ Waiting for SSO redirect...');
262
+ console.log(' Supported: Google, Facebook, GitHub, Microsoft, Apple, Twitter, LinkedIn, Okta, Auth0, GitLab, Slack, Discord, and more...');
263
+ while (Date.now() - startTime < timeout) {
264
+ const currentURL = page.url();
265
+ // Phase 1: Wait for user to click SSO and leave baseURL
266
+ if (!hasLeftBaseURL) {
267
+ if (!currentURL.startsWith(baseURL)) {
268
+ hasLeftBaseURL = true;
269
+ detectedProvider = this.detectSSOProvider(currentURL);
270
+ console.log(`🔄 ${detectedProvider} SSO detected, waiting for callback...`);
271
+ }
272
+ await page.waitForTimeout(checkInterval);
273
+ continue;
274
+ }
275
+ // Phase 2: Wait for callback to baseURL (not login page)
276
+ const isCallback = currentURL.startsWith(baseURL) && !currentURL.includes(loginPath);
277
+ if (isCallback) {
278
+ // First time detecting callback - start stability timer
279
+ if (callbackDetectedAt === 0 || lastCallbackURL !== currentURL) {
280
+ callbackDetectedAt = Date.now();
281
+ lastCallbackURL = currentURL;
282
+ console.log('🔍 Callback detected, verifying login...');
283
+ }
284
+ // Check if URL has been stable for stabilityWait duration
285
+ if (Date.now() - callbackDetectedAt >= stabilityWait) {
286
+ console.log('');
287
+ console.log(`✅ ${detectedProvider} login successful!`);
288
+ return;
289
+ }
290
+ }
291
+ else {
292
+ // Reset if URL changed back (e.g., redirects during auth)
293
+ if (callbackDetectedAt > 0) {
294
+ console.log('🔄 Redirect detected, continuing to wait...');
295
+ }
296
+ callbackDetectedAt = 0;
297
+ lastCallbackURL = '';
298
+ }
299
+ await page.waitForTimeout(checkInterval);
300
+ }
301
+ throw new Error('Timeout waiting for login callback');
302
+ }
303
+ /**
304
+ * Wait for user to press Enter (cancellable)
305
+ */
306
+ waitForEnter() {
307
+ return new Promise((resolve) => {
308
+ this.activeReadline = readline.createInterface({
309
+ input: process.stdin,
310
+ output: process.stdout,
311
+ });
312
+ this.activeReadline.question('', () => {
313
+ this.activeReadline?.close();
314
+ this.activeReadline = null;
315
+ resolve();
316
+ });
317
+ });
318
+ }
319
+ /**
320
+ * Cancel waiting for Enter (close readline interface)
321
+ */
322
+ cancelWaitForEnter() {
323
+ if (this.activeReadline) {
324
+ this.activeReadline.close();
325
+ this.activeReadline = null;
326
+ }
327
+ }
328
+ /**
329
+ * Save browser auth state to file
330
+ */
331
+ async saveAuthState(context, authFile) {
332
+ console.log('💾 Saving auth state...');
333
+ await context.storageState({ path: authFile });
334
+ console.log('');
335
+ console.log('========================================');
336
+ console.log(' ✅ AUTH STATE SAVED!');
337
+ console.log('========================================');
338
+ console.log('');
339
+ console.log(` File: ${authFile}`);
340
+ console.log('');
341
+ console.log(' Usage in Playwright:');
342
+ console.log(` storageState: '${authFile}'`);
343
+ console.log('');
344
+ }
345
+ /**
346
+ * Ask user a question
347
+ */
348
+ askQuestion(question) {
349
+ return new Promise((resolve) => {
350
+ const rl = readline.createInterface({
351
+ input: process.stdin,
352
+ output: process.stdout,
353
+ });
354
+ rl.question(question, (answer) => {
355
+ rl.close();
356
+ resolve(answer);
357
+ });
358
+ });
359
+ }
360
+ /**
361
+ * Verify auth state is valid
362
+ */
363
+ async verifyAuth(name) {
364
+ const authFile = path.join(this.config.outputDir, `${name}.json`);
365
+ if (!fs.existsSync(authFile)) {
366
+ console.log(`❌ Auth file not found: ${authFile}`);
367
+ return false;
368
+ }
369
+ console.log(`🔍 Verifying auth state: ${name}...`);
370
+ const browser = await test_1.chromium.launch({ headless: true });
371
+ try {
372
+ const context = await browser.newContext({
373
+ storageState: authFile,
374
+ });
375
+ const page = await context.newPage();
376
+ await page.goto(this.config.baseURL);
377
+ await page.waitForLoadState('networkidle');
378
+ const currentURL = page.url();
379
+ const isLoggedIn = !currentURL.includes(this.config.loginPath);
380
+ if (isLoggedIn) {
381
+ console.log('✅ Auth state is valid');
382
+ }
383
+ else {
384
+ console.log('❌ Auth state expired or invalid');
385
+ }
386
+ await context.close();
387
+ return isLoggedIn;
388
+ }
389
+ finally {
390
+ await browser.close();
391
+ }
392
+ }
393
+ /**
394
+ * List all auth states
395
+ */
396
+ listAuth() {
397
+ const outputDir = this.config.outputDir;
398
+ if (!fs.existsSync(outputDir)) {
399
+ return [];
400
+ }
401
+ return fs
402
+ .readdirSync(outputDir)
403
+ .filter((f) => f.endsWith('.json'))
404
+ .map((f) => f.replace('.json', ''));
405
+ }
406
+ /**
407
+ * Export auth state as base64 (for CI)
408
+ */
409
+ exportAuth(name) {
410
+ const authFile = path.join(this.config.outputDir, `${name}.json`);
411
+ if (!fs.existsSync(authFile)) {
412
+ console.log(`❌ Auth file not found: ${authFile}`);
413
+ return null;
414
+ }
415
+ const authJson = fs.readFileSync(authFile, 'utf-8');
416
+ return Buffer.from(authJson).toString('base64');
417
+ }
418
+ }
419
+ exports.AuthMaker = AuthMaker;
420
+ //# sourceMappingURL=auth-maker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-maker.js","sourceRoot":"","sources":["../../src/tools/auth-maker.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "AI-Native E2E Test Generator - Generate Playwright tests from Gherkin features using AI",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "build": "tsc && npm run copy-templates",
12
- "copy-templates": "mkdir -p dist/generators/test-generator/adapters/playwright/templates/steps && cp -r src/generators/test-generator/adapters/playwright/templates/*.hbs dist/generators/test-generator/adapters/playwright/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/playwright/templates/steps dist/generators/test-generator/adapters/playwright/templates/",
12
+ "copy-templates": "mkdir -p dist/generators/test-generator/adapters/playwright/templates/steps && mkdir -p dist/generators/test-generator/templates && cp -r src/generators/test-generator/adapters/playwright/templates/*.hbs dist/generators/test-generator/adapters/playwright/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/playwright/templates/steps dist/generators/test-generator/adapters/playwright/templates/ && cp src/generators/test-generator/templates/*.hbs dist/generators/test-generator/templates/ 2>/dev/null || true",
13
13
  "dev": "tsx src/cli/index.ts",
14
14
  "test": "echo \"Error: no test specified\" && exit 1",
15
15
  "prepublishOnly": "npm run build"
package/src/cli/index.ts CHANGED
@@ -8,6 +8,56 @@ import { CLIAdapter } from '../input/cli-adapter';
8
8
  import { ConfigLoader } from '../config/config-loader';
9
9
  import { Pipeline } from '../orchestrator/pipeline';
10
10
  import { CacheManager } from '../orchestrator/cache-manager';
11
+ import { ValidationResult } from '../core/validator';
12
+
13
+ /**
14
+ * Print validation result to console
15
+ */
16
+ function printValidationResult(result: ValidationResult, verbose: boolean = false): void {
17
+ for (const fileResult of result.results) {
18
+ if (fileResult.valid) {
19
+ if (verbose) {
20
+ console.log(`✓ ${fileResult.file}`);
21
+ console.log(` ✓ Gherkin syntax valid`);
22
+ if (fileResult.warnings.length === 0) {
23
+ console.log(` ✓ All checks passed`);
24
+ }
25
+ } else {
26
+ console.log(`✓ ${fileResult.file}`);
27
+ }
28
+ } else {
29
+ console.log(`✗ ${fileResult.file}`);
30
+ }
31
+
32
+ // Print warnings
33
+ for (const warning of fileResult.warnings) {
34
+ const lineInfo = warning.line ? `:${warning.line}` : '';
35
+ console.log(` ⊘ ${warning.message}${lineInfo}`);
36
+ }
37
+
38
+ // Print errors
39
+ for (const error of fileResult.errors) {
40
+ const lineInfo = error.line ? `:${error.line}` : '';
41
+ console.log(` ✗ ${error.message}${lineInfo}`);
42
+ }
43
+
44
+ if (!fileResult.valid || (verbose && fileResult.warnings.length > 0)) {
45
+ console.log('');
46
+ }
47
+ }
48
+
49
+ // Print summary
50
+ console.log('\nSummary:');
51
+ console.log(` Files: ${result.summary.totalFiles} total, ${result.summary.passedFiles} passed, ${result.summary.failedFiles} failed`);
52
+ console.log(` Errors: ${result.summary.totalErrors}`);
53
+ console.log(` Warnings: ${result.summary.totalWarnings}`);
54
+
55
+ if (result.valid) {
56
+ console.log('\n✅ Validation passed');
57
+ } else {
58
+ console.log('\n❌ Validation failed');
59
+ }
60
+ }
11
61
 
12
62
  async function main() {
13
63
  const adapter = new CLIAdapter();
@@ -83,12 +133,21 @@ async function main() {
83
133
  console.log(`Next step: sungen generate --screen ${options.screen}`);
84
134
  console.log(' This will create the test specs based on the mapped selectors and test data.');
85
135
  } else if (options.file) {
86
- // Process single feature file (legacy mode)
87
- const outputDir = options.output || path.join(config.paths.selectorsDir, 'screens');
136
+ // Process single feature file
137
+ // Derive output from feature file location: features/ -> selectors/screens/
138
+ const deriveOutputDir = (featureFilePath: string): string => {
139
+ const featureDir = path.dirname(featureFilePath);
140
+ if (featureDir.includes('features')) {
141
+ return featureDir.replace(/features\/?$/, 'selectors/screens');
142
+ }
143
+ return path.join(featureDir, '..', 'selectors', 'screens');
144
+ };
145
+
146
+ const outputDir = options.output || deriveOutputDir(options.file);
88
147
  const forceOverwrite = cliOptions.force || false;
89
- console.log(`🗺️ Mapping from file: ${options.file}`);
148
+ console.log(`Mapping from file: ${options.file}\n`);
90
149
  const result = generator.processFeatureFile(options.file, outputDir, forceOverwrite);
91
- console.log(`✅ Generated: ${result.outputPath} (${result.elementCount} elements)`);
150
+ console.log(`✓ Generated: ${result.outputPath} (${result.elementCount} elements)`);
92
151
  } else {
93
152
  console.error('❌ Error: Please specify --screen <name> or --file <path>');
94
153
  process.exit(1);
@@ -195,6 +254,109 @@ async function main() {
195
254
  }
196
255
  });
197
256
 
257
+ const validateCmd = adapter.addValidateCommand(program);
258
+ validateCmd.action(async (options) => {
259
+ try {
260
+ const { ScreenValidator } = require('../core/validator');
261
+
262
+ const validator = new ScreenValidator({
263
+ verbose: options.verbose,
264
+ });
265
+
266
+ let result: ValidationResult;
267
+ if (options.screen) {
268
+ console.log(`Validating screen: ${options.screen}\n`);
269
+ result = validator.validateScreen(options.screen);
270
+ } else {
271
+ console.log('Validating all screens...\n');
272
+ result = validator.validateAll();
273
+ }
274
+
275
+ // Output results
276
+ if (options.json) {
277
+ console.log(JSON.stringify(result, null, 2));
278
+ } else {
279
+ printValidationResult(result, options.verbose);
280
+ }
281
+
282
+ // Exit with error code if validation failed
283
+ if (!result.valid) {
284
+ process.exit(1);
285
+ }
286
+ } catch (error) {
287
+ console.error('❌ Validation failed:', error);
288
+ process.exit(1);
289
+ }
290
+ });
291
+
292
+ const makeAuthCmd = adapter.addMakeAuthCommand(program);
293
+ makeAuthCmd.action(async (name: string, options: any) => {
294
+ try {
295
+ const { AuthMaker } = require('../tools/auth-maker');
296
+
297
+ // Only pass baseURL if explicitly provided via --url
298
+ // Otherwise let AuthMaker detect from playwright.config.ts
299
+ const authMaker = new AuthMaker({
300
+ baseURL: options.url,
301
+ loginPath: options.path || '/login',
302
+ outputDir: options.output || 'specs/.auth',
303
+ timeout: options.timeout ? parseInt(options.timeout, 300) : undefined,
304
+ navigationTimeout: options.navTimeout ? parseInt(options.navTimeout, 300) : undefined,
305
+ stabilityWait: options.stabilityWait ? parseInt(options.stabilityWait, 300) : undefined,
306
+ });
307
+
308
+ // Handle --list flag
309
+ if (options.list) {
310
+ const authStates = authMaker.listAuth();
311
+ if (authStates.length === 0) {
312
+ console.log('No auth states found.');
313
+ } else {
314
+ console.log('Available auth states:');
315
+ for (const state of authStates) {
316
+ console.log(` - ${state}`);
317
+ }
318
+ }
319
+ return;
320
+ }
321
+
322
+ // Handle --verify flag
323
+ if (options.verify) {
324
+ const isValid = await authMaker.verifyAuth(name);
325
+ process.exit(isValid ? 0 : 1);
326
+ }
327
+
328
+ // Handle --export flag
329
+ if (options.export) {
330
+ const base64 = authMaker.exportAuth(name);
331
+ if (base64) {
332
+ console.log('');
333
+ console.log('Base64 Auth State (for CI):');
334
+ console.log('========================================');
335
+ console.log(base64);
336
+ console.log('========================================');
337
+ console.log('');
338
+ console.log('Save this to CI secret: AUTH_STATE_' + name.toUpperCase());
339
+ }
340
+ return;
341
+ }
342
+
343
+ // Default: create new auth state
344
+ await authMaker.makeAuth({
345
+ name,
346
+ baseURL: options.url,
347
+ loginPath: options.path,
348
+ outputDir: options.output,
349
+ timeout: options.timeout ? parseInt(options.timeout, 10) : undefined,
350
+ navigationTimeout: options.navTimeout ? parseInt(options.navTimeout, 10) : undefined,
351
+ stabilityWait: options.stabilityWait ? parseInt(options.stabilityWait, 10) : undefined,
352
+ headless: options.headless,
353
+ });
354
+ } catch (error) {
355
+ console.error('❌ Auth generation failed:', error);
356
+ process.exit(1);
357
+ }
358
+ });
359
+
198
360
  // Parse arguments
199
361
  await program.parseAsync(process.argv);
200
362
  }