@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.
- package/README.md +173 -1
- package/dist/cli/index.js +155 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/core/validator/data-validator.d.ts +38 -0
- package/dist/core/validator/data-validator.d.ts.map +1 -0
- package/dist/core/validator/data-validator.js +212 -0
- package/dist/core/validator/data-validator.js.map +1 -0
- package/dist/core/validator/feature-validator.d.ts +27 -0
- package/dist/core/validator/feature-validator.d.ts.map +1 -0
- package/dist/core/validator/feature-validator.js +182 -0
- package/dist/core/validator/feature-validator.js.map +1 -0
- package/dist/core/validator/index.d.ts +46 -0
- package/dist/core/validator/index.d.ts.map +1 -0
- package/dist/core/validator/index.js +17 -0
- package/dist/core/validator/index.js.map +1 -0
- package/dist/core/validator/screen-validator.d.ts +35 -0
- package/dist/core/validator/screen-validator.d.ts.map +1 -0
- package/dist/core/validator/screen-validator.js +195 -0
- package/dist/core/validator/screen-validator.js.map +1 -0
- package/dist/core/validator/selector-validator.d.ts +35 -0
- package/dist/core/validator/selector-validator.d.ts.map +1 -0
- package/dist/core/validator/selector-validator.js +210 -0
- package/dist/core/validator/selector-validator.js.map +1 -0
- package/dist/generators/cli.js +1 -1
- 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/scaffold-generator/index.d.ts +25 -2
- package/dist/generators/scaffold-generator/index.d.ts.map +1 -1
- package/dist/generators/scaffold-generator/index.js +157 -14
- package/dist/generators/scaffold-generator/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts +2 -0
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/scenario.hbs +4 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/radio-select-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -0
- package/dist/generators/test-generator/auth-setup-generator.d.ts +18 -0
- package/dist/generators/test-generator/auth-setup-generator.d.ts.map +1 -0
- package/dist/generators/test-generator/auth-setup-generator.js +82 -0
- package/dist/generators/test-generator/auth-setup-generator.js.map +1 -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 +130 -5
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/assertion-patterns.js +30 -0
- package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -1
- package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/form-patterns.js +25 -5
- package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -1
- package/dist/generators/test-generator/templates/auth-setup.ts.hbs +36 -0
- package/dist/input/cli-adapter.d.ts +8 -0
- package/dist/input/cli-adapter.d.ts.map +1 -1
- package/dist/input/cli-adapter.js +31 -1
- package/dist/input/cli-adapter.js.map +1 -1
- package/dist/orchestrator/pipeline.d.ts.map +1 -1
- package/dist/orchestrator/pipeline.js +31 -22
- package/dist/orchestrator/pipeline.js.map +1 -1
- package/dist/tools/auth-maker.d.ts +74 -0
- package/dist/tools/auth-maker.d.ts.map +1 -0
- package/dist/tools/auth-maker.js +420 -0
- package/dist/tools/auth-maker.js.map +1 -0
- package/package.json +2 -2
- package/src/cli/index.ts +166 -4
- package/src/core/validator/data-validator.ts +202 -0
- package/src/core/validator/feature-validator.ts +176 -0
- package/src/core/validator/index.ts +57 -0
- package/src/core/validator/screen-validator.ts +209 -0
- package/src/core/validator/selector-validator.ts +208 -0
- package/src/generators/cli.ts +1 -1
- package/src/generators/gherkin-parser/index.ts +5 -0
- package/src/generators/scaffold-generator/index.ts +179 -19
- package/src/generators/test-generator/adapters/adapter-interface.ts +2 -0
- package/src/generators/test-generator/adapters/playwright/templates/scenario.hbs +4 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/radio-select-action.hbs +1 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -0
- package/src/generators/test-generator/auth-setup-generator.ts +59 -0
- package/src/generators/test-generator/code-generator.ts +173 -6
- package/src/generators/test-generator/patterns/assertion-patterns.ts +34 -1
- package/src/generators/test-generator/patterns/form-patterns.ts +27 -8
- package/src/generators/test-generator/templates/auth-setup.ts.hbs +36 -0
- package/src/input/cli-adapter.ts +33 -1
- package/src/orchestrator/pipeline.ts +39 -24
- 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.
|
|
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
|
|
87
|
-
|
|
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(
|
|
148
|
+
console.log(`Mapping from file: ${options.file}\n`);
|
|
90
149
|
const result = generator.processFeatureFile(options.file, outputDir, forceOverwrite);
|
|
91
|
-
console.log(
|
|
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
|
}
|