@sridharkikkeri/playwright-common 1.0.47 ā 1.0.48
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/create-healthedge-tests.js +40 -34
- package/package.json +2 -1
- package/templates/DashboardPage.ts +22 -0
- package/templates/LoginPage.ts +34 -0
- package/templates/UserBuilder.ts +43 -0
- package/templates/fixtures.ts +73 -0
- package/templates/sample.spec.ts +39 -0
|
@@ -165,41 +165,47 @@ Object.entries(envConfigs).forEach(([env, config]) => {
|
|
|
165
165
|
);
|
|
166
166
|
});
|
|
167
167
|
|
|
168
|
-
//
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
168
|
+
// Copy sample files from templates
|
|
169
|
+
const templatesDir = path.join(__dirname, 'templates');
|
|
170
|
+
if (fs.existsSync(templatesDir)) {
|
|
171
|
+
console.log('\nš Copying sample files...\n');
|
|
172
|
+
|
|
173
|
+
// Copy fixtures
|
|
174
|
+
const fixturesSrc = path.join(templatesDir, 'fixtures.ts');
|
|
175
|
+
if (fs.existsSync(fixturesSrc)) {
|
|
176
|
+
fs.copyFileSync(fixturesSrc, path.join(projectPath, 'src/main/fixtures/fixtures.ts'));
|
|
177
|
+
console.log(' ā Copied fixtures.ts');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Copy pages
|
|
181
|
+
const loginPageSrc = path.join(templatesDir, 'LoginPage.ts');
|
|
182
|
+
if (fs.existsSync(loginPageSrc)) {
|
|
183
|
+
fs.copyFileSync(loginPageSrc, path.join(projectPath, 'src/main/pages/LoginPage.ts'));
|
|
184
|
+
console.log(' ā Copied LoginPage.ts');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const dashboardPageSrc = path.join(templatesDir, 'DashboardPage.ts');
|
|
188
|
+
if (fs.existsSync(dashboardPageSrc)) {
|
|
189
|
+
fs.copyFileSync(dashboardPageSrc, path.join(projectPath, 'src/main/pages/DashboardPage.ts'));
|
|
190
|
+
console.log(' ā Copied DashboardPage.ts');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Copy builder
|
|
194
|
+
const builderSrc = path.join(templatesDir, 'UserBuilder.ts');
|
|
195
|
+
if (fs.existsSync(builderSrc)) {
|
|
196
|
+
fs.copyFileSync(builderSrc, path.join(projectPath, 'src/main/builders/UserBuilder.ts'));
|
|
197
|
+
console.log(' ā Copied UserBuilder.ts');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Copy test
|
|
201
|
+
const testSrc = path.join(templatesDir, 'sample.spec.ts');
|
|
202
|
+
if (fs.existsSync(testSrc)) {
|
|
203
|
+
fs.copyFileSync(testSrc, path.join(projectPath, 'src/tests/specs/sample.spec.ts'));
|
|
204
|
+
console.log(' ā Copied sample.spec.ts');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
console.log('\nā
Sample files copied');
|
|
184
208
|
}
|
|
185
|
-
`;
|
|
186
|
-
|
|
187
|
-
fs.writeFileSync(path.join(projectPath, 'src/main/pages/HomePage.ts'), samplePage);
|
|
188
|
-
|
|
189
|
-
// Sample Test
|
|
190
|
-
const sampleTest = `import { test } from '../../../main/fixtures/fixtures';
|
|
191
|
-
import { HomePage } from '../../main/pages/HomePage';
|
|
192
|
-
|
|
193
|
-
test.describe('Sample Tests', () => {
|
|
194
|
-
test('Search functionality', async ({ page }) => {
|
|
195
|
-
const homePage = new HomePage(page);
|
|
196
|
-
await page.goto('/');
|
|
197
|
-
await homePage.search('playwright');
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
`;
|
|
201
|
-
|
|
202
|
-
fs.writeFileSync(path.join(projectPath, 'src/tests/specs/sample.spec.ts'), sampleTest);
|
|
203
209
|
|
|
204
210
|
// Test data
|
|
205
211
|
const testData = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sridharkikkeri/playwright-common",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.48",
|
|
4
4
|
"description": "Production-grade Playwright framework with AI-powered self-healing, visual regression, and enterprise features",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"dist",
|
|
15
15
|
"src",
|
|
16
|
+
"templates",
|
|
16
17
|
"api-docs",
|
|
17
18
|
"create-healthedge-tests.js",
|
|
18
19
|
"README.md",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Page } from '@playwright/test';
|
|
2
|
+
import { BasePage } from '../main/pages/BasePage';
|
|
3
|
+
import { ElementWrapper } from '../main/wrappers/ElementWrapper';
|
|
4
|
+
|
|
5
|
+
export class DashboardPage extends BasePage {
|
|
6
|
+
readonly welcomeMessage: ElementWrapper;
|
|
7
|
+
readonly logoutButton: ElementWrapper;
|
|
8
|
+
|
|
9
|
+
constructor(page: Page) {
|
|
10
|
+
super(page, { pageName: 'DashboardPage' });
|
|
11
|
+
this.welcomeMessage = this.getByTestId('welcome-message');
|
|
12
|
+
this.logoutButton = this.getByRole('button', { name: 'Logout' });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async getWelcomeText(): Promise<string> {
|
|
16
|
+
return await this.welcomeMessage.textContent();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async logout(): Promise<void> {
|
|
20
|
+
await this.logoutButton.click('Click logout button');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Page } from '@playwright/test';
|
|
2
|
+
import { BasePage } from '../main/pages/BasePage';
|
|
3
|
+
import { ElementWrapper } from '../main/wrappers/ElementWrapper';
|
|
4
|
+
|
|
5
|
+
export class LoginPage extends BasePage {
|
|
6
|
+
readonly emailInput: ElementWrapper;
|
|
7
|
+
readonly passwordInput: ElementWrapper;
|
|
8
|
+
readonly loginButton: ElementWrapper;
|
|
9
|
+
|
|
10
|
+
constructor(page: Page) {
|
|
11
|
+
super(page, { pageName: 'LoginPage' });
|
|
12
|
+
this.emailInput = this.getByLabel('Email');
|
|
13
|
+
this.passwordInput = this.getByLabel('Password');
|
|
14
|
+
this.loginButton = this.getByRole('button', { name: 'Login' });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async enterEmail(email: string): Promise<void> {
|
|
18
|
+
await this.emailInput.fill(email, 'Enter email address');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async enterPassword(password: string): Promise<void> {
|
|
22
|
+
await this.passwordInput.fill(password, 'Enter password');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async clickLogin(): Promise<void> {
|
|
26
|
+
await this.loginButton.click('Click login button');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async login(email: string, password: string): Promise<void> {
|
|
30
|
+
await this.enterEmail(email);
|
|
31
|
+
await this.enterPassword(password);
|
|
32
|
+
await this.clickLogin();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface UserData {
|
|
2
|
+
firstName: string;
|
|
3
|
+
lastName: string;
|
|
4
|
+
email?: string;
|
|
5
|
+
phone?: string;
|
|
6
|
+
role?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class UserBuilder {
|
|
10
|
+
private data: UserData = {
|
|
11
|
+
firstName: 'John',
|
|
12
|
+
lastName: 'Doe'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
setFirstName(name: string): this {
|
|
16
|
+
this.data.firstName = name;
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
setLastName(name: string): this {
|
|
21
|
+
this.data.lastName = name;
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setEmail(email: string): this {
|
|
26
|
+
this.data.email = email;
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setPhone(phone: string): this {
|
|
31
|
+
this.data.phone = phone;
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
setRole(role: string): this {
|
|
36
|
+
this.data.role = role;
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
build(): UserData {
|
|
41
|
+
return { ...this.data };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { test as base } from '@playwright/test';
|
|
2
|
+
import { ActionOrchestrator } from '../main/selfhealing/ActionOrchestrator';
|
|
3
|
+
import { ApiClient } from '../main/api/ApiClient';
|
|
4
|
+
import { AllureUtil } from '../main/reporting/AllureUtil';
|
|
5
|
+
import { LoginPage } from '../main/pages/LoginPage';
|
|
6
|
+
import { DashboardPage } from '../main/pages/DashboardPage';
|
|
7
|
+
|
|
8
|
+
export type FrameworkFixtures = {
|
|
9
|
+
orchestrator: ActionOrchestrator;
|
|
10
|
+
apiClient: ApiClient;
|
|
11
|
+
loginPage: LoginPage;
|
|
12
|
+
dashboardPage: DashboardPage;
|
|
13
|
+
locale: string;
|
|
14
|
+
retryInfo: { current: number; max: number };
|
|
15
|
+
_reportingHook: void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const test = base.extend<FrameworkFixtures>({
|
|
19
|
+
orchestrator: async ({ page }, use, testInfo) => {
|
|
20
|
+
const orchestrator = new ActionOrchestrator(page, {
|
|
21
|
+
pageName: testInfo.title
|
|
22
|
+
});
|
|
23
|
+
await use(orchestrator);
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
apiClient: async ({ }, use) => {
|
|
27
|
+
const client = new ApiClient(process.env.API_BASE_URL || '');
|
|
28
|
+
await client.init();
|
|
29
|
+
await use(client);
|
|
30
|
+
await client.dispose();
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
loginPage: async ({ page }, use) => {
|
|
34
|
+
await use(new LoginPage(page));
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
dashboardPage: async ({ page }, use) => {
|
|
38
|
+
await use(new DashboardPage(page));
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
locale: async ({ }, use) => {
|
|
42
|
+
const locale = process.env.LOCALE || 'en';
|
|
43
|
+
await use(locale);
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
retryInfo: async ({ }, use, testInfo) => {
|
|
47
|
+
await use({
|
|
48
|
+
current: testInfo.retry,
|
|
49
|
+
max: testInfo.project.retries
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
_reportingHook: [async ({ retryInfo }, use, testInfo) => {
|
|
54
|
+
await use();
|
|
55
|
+
|
|
56
|
+
if (testInfo.status !== testInfo.expectedStatus) {
|
|
57
|
+
AllureUtil.attachText(
|
|
58
|
+
'Test Failure Details',
|
|
59
|
+
`Error: ${testInfo.error?.message || 'Unknown'}\nStack: ${testInfo.error?.stack || 'N/A'}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (retryInfo.current > 0) {
|
|
64
|
+
AllureUtil.attachText(
|
|
65
|
+
'Retry Telemetry',
|
|
66
|
+
`This test is being retried. Attempt: ${retryInfo.current} / ${retryInfo.max}`
|
|
67
|
+
);
|
|
68
|
+
console.warn(`ā ļø Flaky test detected: "${testInfo.title}" (Retry ${retryInfo.current})`);
|
|
69
|
+
}
|
|
70
|
+
}, { auto: true }]
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export { expect } from '@playwright/test';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { test, expect } from '../main/fixtures/fixtures';
|
|
2
|
+
import { UserBuilder } from '../main/builders/UserBuilder';
|
|
3
|
+
import { ConfigManager } from '../main/config/ConfigManager';
|
|
4
|
+
|
|
5
|
+
test.describe('Sample Test Suite @smoke', () => {
|
|
6
|
+
test('Login with valid credentials', async ({ page, loginPage, dashboardPage }) => {
|
|
7
|
+
const config = ConfigManager.getConfig();
|
|
8
|
+
await page.goto(config.baseUrl!);
|
|
9
|
+
await page.waitForLoadState('networkidle');
|
|
10
|
+
|
|
11
|
+
await loginPage.login('test@example.com', 'password123');
|
|
12
|
+
await page.waitForLoadState('networkidle');
|
|
13
|
+
|
|
14
|
+
const welcomeText = await dashboardPage.getWelcomeText();
|
|
15
|
+
expect(welcomeText).toContain('Welcome');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('Create user with builder pattern', async ({ page, loginPage }) => {
|
|
19
|
+
const timestamp = Date.now();
|
|
20
|
+
const user = new UserBuilder()
|
|
21
|
+
.setFirstName('Test')
|
|
22
|
+
.setLastName(`User-${timestamp}`)
|
|
23
|
+
.setEmail(`test-${timestamp}@example.com`)
|
|
24
|
+
.setRole('admin')
|
|
25
|
+
.build();
|
|
26
|
+
|
|
27
|
+
console.log('Created user:', user);
|
|
28
|
+
|
|
29
|
+
const config = ConfigManager.getConfig();
|
|
30
|
+
await page.goto(config.baseUrl!);
|
|
31
|
+
await loginPage.login(user.email!, 'password123');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('API integration example', async ({ apiClient }) => {
|
|
35
|
+
const response = await apiClient.get('/api/users/1');
|
|
36
|
+
expect(response.status).toBe(200);
|
|
37
|
+
expect(response.data).toHaveProperty('id');
|
|
38
|
+
});
|
|
39
|
+
});
|