@bugzy-ai/bugzy 1.9.2 → 1.9.4
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/LICENSE +21 -21
- package/README.md +273 -273
- package/dist/cli/index.cjs +23 -50
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +22 -49
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +20 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +20 -46
- package/dist/index.js.map +1 -1
- package/dist/subagents/index.cjs.map +1 -1
- package/dist/subagents/index.js.map +1 -1
- package/dist/subagents/metadata.cjs.map +1 -1
- package/dist/subagents/metadata.js.map +1 -1
- package/dist/tasks/index.cjs +20 -9
- package/dist/tasks/index.cjs.map +1 -1
- package/dist/tasks/index.js +20 -9
- package/dist/tasks/index.js.map +1 -1
- package/dist/templates/init/.bugzy/runtime/knowledge-base.md +61 -0
- package/dist/templates/init/.bugzy/runtime/knowledge-maintenance-guide.md +97 -0
- package/dist/templates/init/.bugzy/runtime/project-context.md +35 -0
- package/dist/templates/init/.bugzy/runtime/subagent-memory-guide.md +87 -0
- package/dist/templates/init/.bugzy/runtime/templates/test-plan-template.md +50 -0
- package/dist/templates/init/.bugzy/runtime/templates/test-result-schema.md +498 -0
- package/dist/templates/init/.bugzy/runtime/test-execution-strategy.md +535 -0
- package/dist/templates/init/.bugzy/runtime/testing-best-practices.md +632 -0
- package/dist/templates/init/.gitignore-template +25 -0
- package/package.json +95 -95
- package/templates/init/.bugzy/runtime/knowledge-base.md +61 -61
- package/templates/init/.bugzy/runtime/knowledge-maintenance-guide.md +97 -97
- package/templates/init/.bugzy/runtime/project-context.md +35 -35
- package/templates/init/.bugzy/runtime/subagent-memory-guide.md +87 -87
- package/templates/init/.bugzy/runtime/templates/test-plan-template.md +50 -50
- package/templates/init/.bugzy/runtime/templates/test-result-schema.md +498 -498
- package/templates/init/.bugzy/runtime/test-execution-strategy.md +535 -535
- package/templates/init/.bugzy/runtime/testing-best-practices.md +724 -724
- package/templates/init/.env.testdata +18 -18
- package/templates/init/.gitignore-template +24 -24
- package/templates/init/AGENTS.md +155 -155
- package/templates/init/CLAUDE.md +157 -157
- package/templates/init/test-runs/README.md +45 -45
- package/templates/playwright/BasePage.template.ts +190 -190
- package/templates/playwright/auth.setup.template.ts +89 -89
- package/templates/playwright/dataGenerators.helper.template.ts +148 -148
- package/templates/playwright/dateUtils.helper.template.ts +96 -96
- package/templates/playwright/pages.fixture.template.ts +50 -50
- package/templates/playwright/playwright.config.template.ts +97 -97
- package/templates/playwright/reporters/bugzy-reporter.ts +454 -454
|
@@ -1,190 +1,190 @@
|
|
|
1
|
-
import { type Page, type Locator } from '@playwright/test';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Base Page Object Model
|
|
5
|
-
* All page objects should extend this class
|
|
6
|
-
*
|
|
7
|
-
* Provides common functionality and patterns for page interactions
|
|
8
|
-
*/
|
|
9
|
-
export class BasePage {
|
|
10
|
-
readonly page: Page;
|
|
11
|
-
|
|
12
|
-
constructor(page: Page) {
|
|
13
|
-
this.page = page;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Navigate to a specific path
|
|
18
|
-
* @param path - The path to navigate to (relative to baseURL)
|
|
19
|
-
*/
|
|
20
|
-
async navigate(path: string): Promise<void> {
|
|
21
|
-
await this.page.goto(path);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Wait for the page to be fully loaded
|
|
26
|
-
*/
|
|
27
|
-
async waitForPageLoad(): Promise<void> {
|
|
28
|
-
await this.page.waitForLoadState('networkidle');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Get the current URL
|
|
33
|
-
*/
|
|
34
|
-
getCurrentURL(): string {
|
|
35
|
-
return this.page.url();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Get the page title
|
|
40
|
-
*/
|
|
41
|
-
async getTitle(): Promise<string> {
|
|
42
|
-
return await this.page.title();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Wait for a locator to be visible
|
|
47
|
-
* @param locator - The locator to wait for
|
|
48
|
-
* @param timeout - Optional custom timeout
|
|
49
|
-
*/
|
|
50
|
-
async waitForVisible(locator: Locator, timeout?: number): Promise<void> {
|
|
51
|
-
await locator.waitFor({ state: 'visible', timeout });
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Wait for a locator to be hidden
|
|
56
|
-
* @param locator - The locator to wait for
|
|
57
|
-
* @param timeout - Optional custom timeout
|
|
58
|
-
*/
|
|
59
|
-
async waitForHidden(locator: Locator, timeout?: number): Promise<void> {
|
|
60
|
-
await locator.waitFor({ state: 'hidden', timeout });
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Click an element and wait for navigation
|
|
65
|
-
* @param locator - The element to click
|
|
66
|
-
*/
|
|
67
|
-
async clickAndNavigate(locator: Locator): Promise<void> {
|
|
68
|
-
await Promise.all([
|
|
69
|
-
this.page.waitForNavigation(),
|
|
70
|
-
locator.click()
|
|
71
|
-
]);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Fill a form field
|
|
76
|
-
* @param locator - The input field
|
|
77
|
-
* @param value - The value to fill
|
|
78
|
-
*/
|
|
79
|
-
async fillField(locator: Locator, value: string): Promise<void> {
|
|
80
|
-
await locator.clear();
|
|
81
|
-
await locator.fill(value);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Select an option from a dropdown
|
|
86
|
-
* @param locator - The select element
|
|
87
|
-
* @param value - The value to select
|
|
88
|
-
*/
|
|
89
|
-
async selectOption(locator: Locator, value: string): Promise<void> {
|
|
90
|
-
await locator.selectOption(value);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Check a checkbox
|
|
95
|
-
* @param locator - The checkbox element
|
|
96
|
-
*/
|
|
97
|
-
async check(locator: Locator): Promise<void> {
|
|
98
|
-
if (!(await locator.isChecked())) {
|
|
99
|
-
await locator.check();
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Uncheck a checkbox
|
|
105
|
-
* @param locator - The checkbox element
|
|
106
|
-
*/
|
|
107
|
-
async uncheck(locator: Locator): Promise<void> {
|
|
108
|
-
if (await locator.isChecked()) {
|
|
109
|
-
await locator.uncheck();
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Get text content from an element
|
|
115
|
-
* @param locator - The element to get text from
|
|
116
|
-
*/
|
|
117
|
-
async getText(locator: Locator): Promise<string> {
|
|
118
|
-
return (await locator.textContent()) || '';
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Check if element is visible
|
|
123
|
-
* @param locator - The element to check
|
|
124
|
-
*/
|
|
125
|
-
async isVisible(locator: Locator): Promise<boolean> {
|
|
126
|
-
return await locator.isVisible();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Check if element is enabled
|
|
131
|
-
* @param locator - The element to check
|
|
132
|
-
*/
|
|
133
|
-
async isEnabled(locator: Locator): Promise<boolean> {
|
|
134
|
-
return await locator.isEnabled();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Scroll element into view
|
|
139
|
-
* @param locator - The element to scroll to
|
|
140
|
-
*/
|
|
141
|
-
async scrollIntoView(locator: Locator): Promise<void> {
|
|
142
|
-
await locator.scrollIntoViewIfNeeded();
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Take a screenshot
|
|
147
|
-
* @param name - The screenshot file name
|
|
148
|
-
*/
|
|
149
|
-
async takeScreenshot(name: string): Promise<void> {
|
|
150
|
-
await this.page.screenshot({ path: `screenshots/${name}.png`, fullPage: true });
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Execute JavaScript in the browser context
|
|
155
|
-
* @param script - The script to execute
|
|
156
|
-
* @param args - Arguments to pass to the script
|
|
157
|
-
*/
|
|
158
|
-
async executeScript<T>(script: string | Function, ...args: any[]): Promise<T> {
|
|
159
|
-
return await this.page.evaluate(script, ...args);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Wait for API response
|
|
164
|
-
* @param urlPattern - The URL pattern to wait for
|
|
165
|
-
*/
|
|
166
|
-
async waitForResponse(urlPattern: string | RegExp): Promise<void> {
|
|
167
|
-
await this.page.waitForResponse(urlPattern);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Reload the current page
|
|
172
|
-
*/
|
|
173
|
-
async reload(): Promise<void> {
|
|
174
|
-
await this.page.reload();
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Go back in browser history
|
|
179
|
-
*/
|
|
180
|
-
async goBack(): Promise<void> {
|
|
181
|
-
await this.page.goBack();
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Go forward in browser history
|
|
186
|
-
*/
|
|
187
|
-
async goForward(): Promise<void> {
|
|
188
|
-
await this.page.goForward();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
1
|
+
import { type Page, type Locator } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base Page Object Model
|
|
5
|
+
* All page objects should extend this class
|
|
6
|
+
*
|
|
7
|
+
* Provides common functionality and patterns for page interactions
|
|
8
|
+
*/
|
|
9
|
+
export class BasePage {
|
|
10
|
+
readonly page: Page;
|
|
11
|
+
|
|
12
|
+
constructor(page: Page) {
|
|
13
|
+
this.page = page;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Navigate to a specific path
|
|
18
|
+
* @param path - The path to navigate to (relative to baseURL)
|
|
19
|
+
*/
|
|
20
|
+
async navigate(path: string): Promise<void> {
|
|
21
|
+
await this.page.goto(path);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Wait for the page to be fully loaded
|
|
26
|
+
*/
|
|
27
|
+
async waitForPageLoad(): Promise<void> {
|
|
28
|
+
await this.page.waitForLoadState('networkidle');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the current URL
|
|
33
|
+
*/
|
|
34
|
+
getCurrentURL(): string {
|
|
35
|
+
return this.page.url();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the page title
|
|
40
|
+
*/
|
|
41
|
+
async getTitle(): Promise<string> {
|
|
42
|
+
return await this.page.title();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Wait for a locator to be visible
|
|
47
|
+
* @param locator - The locator to wait for
|
|
48
|
+
* @param timeout - Optional custom timeout
|
|
49
|
+
*/
|
|
50
|
+
async waitForVisible(locator: Locator, timeout?: number): Promise<void> {
|
|
51
|
+
await locator.waitFor({ state: 'visible', timeout });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Wait for a locator to be hidden
|
|
56
|
+
* @param locator - The locator to wait for
|
|
57
|
+
* @param timeout - Optional custom timeout
|
|
58
|
+
*/
|
|
59
|
+
async waitForHidden(locator: Locator, timeout?: number): Promise<void> {
|
|
60
|
+
await locator.waitFor({ state: 'hidden', timeout });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Click an element and wait for navigation
|
|
65
|
+
* @param locator - The element to click
|
|
66
|
+
*/
|
|
67
|
+
async clickAndNavigate(locator: Locator): Promise<void> {
|
|
68
|
+
await Promise.all([
|
|
69
|
+
this.page.waitForNavigation(),
|
|
70
|
+
locator.click()
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Fill a form field
|
|
76
|
+
* @param locator - The input field
|
|
77
|
+
* @param value - The value to fill
|
|
78
|
+
*/
|
|
79
|
+
async fillField(locator: Locator, value: string): Promise<void> {
|
|
80
|
+
await locator.clear();
|
|
81
|
+
await locator.fill(value);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Select an option from a dropdown
|
|
86
|
+
* @param locator - The select element
|
|
87
|
+
* @param value - The value to select
|
|
88
|
+
*/
|
|
89
|
+
async selectOption(locator: Locator, value: string): Promise<void> {
|
|
90
|
+
await locator.selectOption(value);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check a checkbox
|
|
95
|
+
* @param locator - The checkbox element
|
|
96
|
+
*/
|
|
97
|
+
async check(locator: Locator): Promise<void> {
|
|
98
|
+
if (!(await locator.isChecked())) {
|
|
99
|
+
await locator.check();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Uncheck a checkbox
|
|
105
|
+
* @param locator - The checkbox element
|
|
106
|
+
*/
|
|
107
|
+
async uncheck(locator: Locator): Promise<void> {
|
|
108
|
+
if (await locator.isChecked()) {
|
|
109
|
+
await locator.uncheck();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get text content from an element
|
|
115
|
+
* @param locator - The element to get text from
|
|
116
|
+
*/
|
|
117
|
+
async getText(locator: Locator): Promise<string> {
|
|
118
|
+
return (await locator.textContent()) || '';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check if element is visible
|
|
123
|
+
* @param locator - The element to check
|
|
124
|
+
*/
|
|
125
|
+
async isVisible(locator: Locator): Promise<boolean> {
|
|
126
|
+
return await locator.isVisible();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Check if element is enabled
|
|
131
|
+
* @param locator - The element to check
|
|
132
|
+
*/
|
|
133
|
+
async isEnabled(locator: Locator): Promise<boolean> {
|
|
134
|
+
return await locator.isEnabled();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Scroll element into view
|
|
139
|
+
* @param locator - The element to scroll to
|
|
140
|
+
*/
|
|
141
|
+
async scrollIntoView(locator: Locator): Promise<void> {
|
|
142
|
+
await locator.scrollIntoViewIfNeeded();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Take a screenshot
|
|
147
|
+
* @param name - The screenshot file name
|
|
148
|
+
*/
|
|
149
|
+
async takeScreenshot(name: string): Promise<void> {
|
|
150
|
+
await this.page.screenshot({ path: `screenshots/${name}.png`, fullPage: true });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Execute JavaScript in the browser context
|
|
155
|
+
* @param script - The script to execute
|
|
156
|
+
* @param args - Arguments to pass to the script
|
|
157
|
+
*/
|
|
158
|
+
async executeScript<T>(script: string | Function, ...args: any[]): Promise<T> {
|
|
159
|
+
return await this.page.evaluate(script, ...args);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Wait for API response
|
|
164
|
+
* @param urlPattern - The URL pattern to wait for
|
|
165
|
+
*/
|
|
166
|
+
async waitForResponse(urlPattern: string | RegExp): Promise<void> {
|
|
167
|
+
await this.page.waitForResponse(urlPattern);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Reload the current page
|
|
172
|
+
*/
|
|
173
|
+
async reload(): Promise<void> {
|
|
174
|
+
await this.page.reload();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Go back in browser history
|
|
179
|
+
*/
|
|
180
|
+
async goBack(): Promise<void> {
|
|
181
|
+
await this.page.goBack();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Go forward in browser history
|
|
186
|
+
*/
|
|
187
|
+
async goForward(): Promise<void> {
|
|
188
|
+
await this.page.goForward();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import { test as setup, expect } from '@playwright/test';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { mkdir } from 'fs/promises';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Authentication Setup
|
|
7
|
-
* Generated by Bugzy - https://github.com/bugzy-ai/bugzy
|
|
8
|
-
*
|
|
9
|
-
* This file runs before all tests to authenticate and save the state
|
|
10
|
-
* Other tests can reuse this authenticated state for faster execution
|
|
11
|
-
*
|
|
12
|
-
* Benefits:
|
|
13
|
-
* - Login once, reuse for all tests
|
|
14
|
-
* - Faster test execution
|
|
15
|
-
* - More stable tests (no repeated login operations)
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const authFile = path.join(__dirname, '../.auth/user.json');
|
|
19
|
-
|
|
20
|
-
setup('authenticate', async ({ page }) => {
|
|
21
|
-
// Skip if no authentication is needed
|
|
22
|
-
// Remove this if you need authentication
|
|
23
|
-
if (!process.env.TEST_USER_EMAIL || !process.env.TEST_USER_PASSWORD) {
|
|
24
|
-
console.log('Skipping authentication setup - no credentials provided');
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Navigate to login page
|
|
29
|
-
// Adjust the URL based on your application
|
|
30
|
-
await page.goto('/login');
|
|
31
|
-
|
|
32
|
-
// Perform authentication steps
|
|
33
|
-
// Using role-based selectors for stability
|
|
34
|
-
|
|
35
|
-
// Fill in email
|
|
36
|
-
await page.getByLabel('Email').fill(process.env.TEST_USER_EMAIL);
|
|
37
|
-
|
|
38
|
-
// Fill in password
|
|
39
|
-
await page.getByLabel('Password').fill(process.env.TEST_USER_PASSWORD);
|
|
40
|
-
|
|
41
|
-
// Click the sign in button
|
|
42
|
-
// Adjust the button text based on your app
|
|
43
|
-
await page.getByRole('button', { name: /sign in|login/i }).click();
|
|
44
|
-
|
|
45
|
-
// Wait for redirect after successful login
|
|
46
|
-
// Adjust the URL pattern based on where users land after login
|
|
47
|
-
await page.waitForURL(/.*\/(dashboard|home)/);
|
|
48
|
-
|
|
49
|
-
// Verify authentication was successful
|
|
50
|
-
// Check for a user-specific element (e.g., user menu, profile icon)
|
|
51
|
-
// Adjust based on your application
|
|
52
|
-
// await expect(page.getByRole('button', { name: /profile|account/i })).toBeVisible();
|
|
53
|
-
|
|
54
|
-
// Create .auth directory if it doesn't exist
|
|
55
|
-
await mkdir(path.dirname(authFile), { recursive: true });
|
|
56
|
-
|
|
57
|
-
// Save authentication state to file
|
|
58
|
-
await page.context().storageState({ path: authFile });
|
|
59
|
-
|
|
60
|
-
console.log('✓ Authentication state saved successfully');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Example: Multiple User Roles
|
|
65
|
-
*
|
|
66
|
-
* If you need to test with different user roles (admin, user, etc.),
|
|
67
|
-
* create separate setup files:
|
|
68
|
-
*
|
|
69
|
-
* - auth.setup.admin.ts
|
|
70
|
-
* - auth.setup.user.ts
|
|
71
|
-
* - auth.setup.guest.ts
|
|
72
|
-
*
|
|
73
|
-
* Then configure them in playwright.config.ts:
|
|
74
|
-
*
|
|
75
|
-
* projects: [
|
|
76
|
-
* { name: 'setup-admin', testMatch: /.*\.setup\.admin\.ts/ },
|
|
77
|
-
* { name: 'setup-user', testMatch: /.*\.setup\.user\.ts/ },
|
|
78
|
-
* {
|
|
79
|
-
* name: 'admin-tests',
|
|
80
|
-
* use: { storageState: 'tests/.auth/admin.json' },
|
|
81
|
-
* dependencies: ['setup-admin'],
|
|
82
|
-
* },
|
|
83
|
-
* {
|
|
84
|
-
* name: 'user-tests',
|
|
85
|
-
* use: { storageState: 'tests/.auth/user.json' },
|
|
86
|
-
* dependencies: ['setup-user'],
|
|
87
|
-
* },
|
|
88
|
-
* ]
|
|
89
|
-
*/
|
|
1
|
+
import { test as setup, expect } from '@playwright/test';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { mkdir } from 'fs/promises';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Authentication Setup
|
|
7
|
+
* Generated by Bugzy - https://github.com/bugzy-ai/bugzy
|
|
8
|
+
*
|
|
9
|
+
* This file runs before all tests to authenticate and save the state
|
|
10
|
+
* Other tests can reuse this authenticated state for faster execution
|
|
11
|
+
*
|
|
12
|
+
* Benefits:
|
|
13
|
+
* - Login once, reuse for all tests
|
|
14
|
+
* - Faster test execution
|
|
15
|
+
* - More stable tests (no repeated login operations)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const authFile = path.join(__dirname, '../.auth/user.json');
|
|
19
|
+
|
|
20
|
+
setup('authenticate', async ({ page }) => {
|
|
21
|
+
// Skip if no authentication is needed
|
|
22
|
+
// Remove this if you need authentication
|
|
23
|
+
if (!process.env.TEST_USER_EMAIL || !process.env.TEST_USER_PASSWORD) {
|
|
24
|
+
console.log('Skipping authentication setup - no credentials provided');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Navigate to login page
|
|
29
|
+
// Adjust the URL based on your application
|
|
30
|
+
await page.goto('/login');
|
|
31
|
+
|
|
32
|
+
// Perform authentication steps
|
|
33
|
+
// Using role-based selectors for stability
|
|
34
|
+
|
|
35
|
+
// Fill in email
|
|
36
|
+
await page.getByLabel('Email').fill(process.env.TEST_USER_EMAIL);
|
|
37
|
+
|
|
38
|
+
// Fill in password
|
|
39
|
+
await page.getByLabel('Password').fill(process.env.TEST_USER_PASSWORD);
|
|
40
|
+
|
|
41
|
+
// Click the sign in button
|
|
42
|
+
// Adjust the button text based on your app
|
|
43
|
+
await page.getByRole('button', { name: /sign in|login/i }).click();
|
|
44
|
+
|
|
45
|
+
// Wait for redirect after successful login
|
|
46
|
+
// Adjust the URL pattern based on where users land after login
|
|
47
|
+
await page.waitForURL(/.*\/(dashboard|home)/);
|
|
48
|
+
|
|
49
|
+
// Verify authentication was successful
|
|
50
|
+
// Check for a user-specific element (e.g., user menu, profile icon)
|
|
51
|
+
// Adjust based on your application
|
|
52
|
+
// await expect(page.getByRole('button', { name: /profile|account/i })).toBeVisible();
|
|
53
|
+
|
|
54
|
+
// Create .auth directory if it doesn't exist
|
|
55
|
+
await mkdir(path.dirname(authFile), { recursive: true });
|
|
56
|
+
|
|
57
|
+
// Save authentication state to file
|
|
58
|
+
await page.context().storageState({ path: authFile });
|
|
59
|
+
|
|
60
|
+
console.log('✓ Authentication state saved successfully');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Example: Multiple User Roles
|
|
65
|
+
*
|
|
66
|
+
* If you need to test with different user roles (admin, user, etc.),
|
|
67
|
+
* create separate setup files:
|
|
68
|
+
*
|
|
69
|
+
* - auth.setup.admin.ts
|
|
70
|
+
* - auth.setup.user.ts
|
|
71
|
+
* - auth.setup.guest.ts
|
|
72
|
+
*
|
|
73
|
+
* Then configure them in playwright.config.ts:
|
|
74
|
+
*
|
|
75
|
+
* projects: [
|
|
76
|
+
* { name: 'setup-admin', testMatch: /.*\.setup\.admin\.ts/ },
|
|
77
|
+
* { name: 'setup-user', testMatch: /.*\.setup\.user\.ts/ },
|
|
78
|
+
* {
|
|
79
|
+
* name: 'admin-tests',
|
|
80
|
+
* use: { storageState: 'tests/.auth/admin.json' },
|
|
81
|
+
* dependencies: ['setup-admin'],
|
|
82
|
+
* },
|
|
83
|
+
* {
|
|
84
|
+
* name: 'user-tests',
|
|
85
|
+
* use: { storageState: 'tests/.auth/user.json' },
|
|
86
|
+
* dependencies: ['setup-user'],
|
|
87
|
+
* },
|
|
88
|
+
* ]
|
|
89
|
+
*/
|