fixing tha playwright
This commit is contained in:
parent
3cd4ed1641
commit
d4f495fef7
18 changed files with 1219 additions and 1 deletions
64
package-lock.json
generated
64
package-lock.json
generated
|
|
@ -12,6 +12,7 @@
|
|||
"react-dom": "^19.2.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.49.0",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^7.0.0",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
|
|
@ -697,6 +698,22 @@
|
|||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.62.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz",
|
||||
"integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.62.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@polka/url": {
|
||||
"version": "1.0.0-next.29",
|
||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
|
||||
|
|
@ -3453,6 +3470,53 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.62.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz",
|
||||
"integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.62.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.62.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz",
|
||||
"integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright/node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.26",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
|
||||
|
|
|
|||
|
|
@ -10,13 +10,15 @@
|
|||
"test": "vitest run",
|
||||
"preview": "vite preview",
|
||||
"cypress:run": "cypress run --headed",
|
||||
"cypress:open": "cypress open"
|
||||
"cypress:open": "cypress open",
|
||||
"test:playwright": "playwright test --project=chromium"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.49.0",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^7.0.0",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
|
|
@ -0,0 +1,209 @@
|
|||
# Instructions
|
||||
|
||||
- Following Playwright test failed.
|
||||
- Explain why, be concise, respect Playwright best practices.
|
||||
- Provide a snippet of code with the fix, if possible.
|
||||
|
||||
# Test info
|
||||
|
||||
- Name: onboarding.spec.ts >> Supplier Portal Onboarding & Feature E2E Flow >> completes the registration, contact verification, document upload, location pinning, welcome tour, order acceptance, and barcode generation
|
||||
- Location: playwright/e2e/onboarding.spec.ts:4:3
|
||||
|
||||
# Error details
|
||||
|
||||
```
|
||||
Error: expect(locator).toBeVisible() failed
|
||||
|
||||
Locator: getByText('Step 1 of 3: Contact Verification')
|
||||
Expected: visible
|
||||
Timeout: 5000ms
|
||||
Error: element(s) not found
|
||||
|
||||
Call log:
|
||||
- Expect "toBeVisible" with timeout 5000ms
|
||||
- waiting for getByText('Step 1 of 3: Contact Verification')
|
||||
|
||||
```
|
||||
|
||||
```yaml
|
||||
- banner:
|
||||
- text: A Global Artisans Hub
|
||||
- navigation:
|
||||
- button "Platform"
|
||||
- button "Pricing"
|
||||
- button "Success Stories"
|
||||
- button "Support"
|
||||
- button "Login"
|
||||
- button "Get Started"
|
||||
- main:
|
||||
- button "Login"
|
||||
- button "Register"
|
||||
- heading "Create Your Supplier Account" [level=2]
|
||||
- text: Business Email *
|
||||
- textbox "Business Email *":
|
||||
- /placeholder: Enter email address
|
||||
- text: test_seller_1786700927358@example.com
|
||||
- text: Mobile Number *
|
||||
- textbox "Mobile Number *":
|
||||
- /placeholder: Enter 10-digit number
|
||||
- text: "9118192284"
|
||||
- text: Create Password *
|
||||
- textbox "Create Password *":
|
||||
- /placeholder: Minimum 8 characters
|
||||
- text: Super_secure_pass_123!
|
||||
- button "🙈"
|
||||
- text: Confirm Password *
|
||||
- textbox "Confirm Password *":
|
||||
- /placeholder: Re-enter password
|
||||
- text: Super_secure_pass_123!
|
||||
- button "🙈"
|
||||
- checkbox "I accept the Terms of Service and Privacy Policy *" [checked]
|
||||
- text: I accept the
|
||||
- link "Terms of Service":
|
||||
- /url: "#terms"
|
||||
- text: and
|
||||
- link "Privacy Policy":
|
||||
- /url: "#privacy"
|
||||
- text: "*"
|
||||
- button "Register Business"
|
||||
- contentinfo:
|
||||
- text: Global Artisans Hub
|
||||
- link "About Us":
|
||||
- /url: "#about"
|
||||
- link "Careers":
|
||||
- /url: "#careers"
|
||||
- link "Press":
|
||||
- /url: "#press"
|
||||
- link "Terms":
|
||||
- /url: "#terms"
|
||||
- link "Privacy":
|
||||
- /url: "#privacy"
|
||||
- text: © 2026 Global Artisans Hub. All rights reserved.
|
||||
```
|
||||
|
||||
# Test source
|
||||
|
||||
```ts
|
||||
1 | import { test, expect } from '@playwright/test';
|
||||
2 |
|
||||
3 | test.describe('Supplier Portal Onboarding & Feature E2E Flow', () => {
|
||||
4 | test('completes the registration, contact verification, document upload, location pinning, welcome tour, order acceptance, and barcode generation', async ({ page }) => {
|
||||
5 | // 1. Visit Landing Page
|
||||
6 | await page.goto('/');
|
||||
7 | await expect(page.getByText('Sell Globally.')).toBeVisible();
|
||||
8 |
|
||||
9 | // 2. Registration Page
|
||||
10 | await page.locator('button, a').filter({ hasText: 'Get Started Today' }).first().click();
|
||||
11 | const randUser = `test_seller_${Date.now()}@example.com`;
|
||||
12 | const randPhone = `9${Math.floor(100000000 + Math.random() * 900000000)}`;
|
||||
13 | await page.locator('input#reg-email').fill(randUser);
|
||||
14 | await page.locator('input#reg-phone').fill(randPhone);
|
||||
15 | await page.locator('input#reg-pass').fill('Super_secure_pass_123!');
|
||||
16 | await page.locator('input#reg-confirm-pass').fill('Super_secure_pass_123!');
|
||||
17 | await page.locator('input#reg-policy').check();
|
||||
18 | await page.locator('button').filter({ hasText: 'Register Business' }).click();
|
||||
19 |
|
||||
20 | // 3. Step 1: Contact Verification
|
||||
> 21 | await expect(page.getByText('Step 1 of 3: Contact Verification')).toBeVisible();
|
||||
| ^ Error: expect(locator).toBeVisible() failed
|
||||
22 | await page.locator('button').filter({ hasText: 'Send WhatsApp OTP' }).click();
|
||||
23 | await page.locator('input[placeholder="Enter 123456"]').first().fill('123456');
|
||||
24 | await page.locator('button').filter({ hasText: 'Verify Code' }).first().click();
|
||||
25 | await expect(page.getByText('✓ Mobile & WhatsApp Verified')).toBeVisible();
|
||||
26 |
|
||||
27 | await page.locator('button').filter({ hasText: 'Send Email OTP' }).click();
|
||||
28 | await page.locator('input[placeholder="Enter 123456"]').last().fill('123456');
|
||||
29 | await page.locator('button').filter({ hasText: 'Verify Code' }).last().click();
|
||||
30 | await expect(page.getByText('✓ Email Verified')).toBeVisible();
|
||||
31 | await page.locator('button').filter({ hasText: 'Next Step: Identity Verification' }).click();
|
||||
32 |
|
||||
33 | // 4. Step 2: Tax & Identity Verification
|
||||
34 | await expect(page.getByText('Step 2 of 3: Tax & Identity Verification')).toBeVisible();
|
||||
35 | await page.locator('input#verify-gst').clear();
|
||||
36 | await page.locator('input#verify-gst').fill('29AAAAA1111A1Z1');
|
||||
37 | await page.locator('button').filter({ hasText: 'Submit GSTIN' }).click();
|
||||
38 | await expect(page.getByText('verification will be completed within next 24 hrs')).toBeVisible();
|
||||
39 |
|
||||
40 | // Upload mock Aadhaar & PAN files
|
||||
41 | await page.setInputFiles('input#aadhar-upload', {
|
||||
42 | name: 'aadhar_card.pdf',
|
||||
43 | mimeType: 'application/pdf',
|
||||
44 | buffer: Buffer.from('mock aadhaar pdf'),
|
||||
45 | });
|
||||
46 | await expect(page.getByText('Selected: aadhar_card.pdf')).toBeVisible();
|
||||
47 |
|
||||
48 | await page.setInputFiles('input#pan-upload', {
|
||||
49 | name: 'pan_card.pdf',
|
||||
50 | mimeType: 'application/pdf',
|
||||
51 | buffer: Buffer.from('mock pan pdf'),
|
||||
52 | });
|
||||
53 | await expect(page.getByText('Selected: pan_card.pdf')).toBeVisible();
|
||||
54 |
|
||||
55 | await page.locator('button').filter({ hasText: 'Next Step: Store & Location' }).click();
|
||||
56 |
|
||||
57 | // 5. Step 3: Store & Pickup Location Setup
|
||||
58 | await expect(page.getByText('Step 3 of 3: Store & Pickup Location')).toBeVisible();
|
||||
59 | await page.locator('input#store-name').clear();
|
||||
60 | await page.locator('input#store-name').fill('Teak Wood Craft Store');
|
||||
61 | await page.locator('textarea#store-bio').clear();
|
||||
62 | await page.locator('textarea#store-bio').fill('Teak wood hand-carved home decor elements.');
|
||||
63 |
|
||||
64 | // Test interactive map coordinate pinning
|
||||
65 | await expect(page.getByText('Store Location on Map *')).toBeVisible();
|
||||
66 | const mapGridParent = page.locator('span').filter({ hasText: 'Click anywhere on the map grid to pin location' }).locator('..');
|
||||
67 | await mapGridParent.click({ position: { x: 100, y: 80 } });
|
||||
68 | await expect(page.getByText('Coordinates: Lat')).toBeVisible();
|
||||
69 |
|
||||
70 | await expect(page.locator('input#location-text')).not.toHaveValue('');
|
||||
71 | await page.locator('input#loc-city').clear();
|
||||
72 | await page.locator('input#loc-city').fill('Bangalore');
|
||||
73 | await page.locator('input#loc-pincode').clear();
|
||||
74 | await page.locator('input#loc-pincode').fill('560001');
|
||||
75 | await page.locator('button').filter({ hasText: 'Submit Supplier Profile' }).click();
|
||||
76 |
|
||||
77 | // 6. Setup Welcome Tour Stepper
|
||||
78 | await expect(page.getByText('Supplier Account Under Review ⏳')).toBeVisible();
|
||||
79 | await page.locator('button').filter({ hasText: 'Start Guided Tour 🎬' }).click();
|
||||
80 | await expect(page.getByText('📦 Products & Inventory Management')).toBeVisible();
|
||||
81 | await page.locator('button').filter({ hasText: 'Next: Order Management' }).click();
|
||||
82 | await expect(page.getByText('🚚 Order Acceptance Control')).toBeVisible();
|
||||
83 | await page.locator('button').filter({ hasText: 'Next: Barcode Generation' }).click();
|
||||
84 | await expect(page.getByText('🏷️ Barcode Identification & Tracking')).toBeVisible();
|
||||
85 | await page.locator('button').filter({ hasText: 'Next: Earnings & Wallet' }).click();
|
||||
86 | await expect(page.getByText('💼 Wallet & Payout Withdrawals')).toBeVisible();
|
||||
87 | await page.locator('button').filter({ hasText: 'Explore Dashboard 🚀' }).click();
|
||||
88 |
|
||||
89 | // 7. Check Active Dashboard Tab
|
||||
90 | await expect(page.getByText('Performance Analytics')).toBeVisible();
|
||||
91 |
|
||||
92 | // 8. Manage Products tab & Bulk template download
|
||||
93 | await page.locator('button, a').filter({ hasText: 'Manage Products' }).click();
|
||||
94 | await expect(page.getByText('Active Inventory')).toBeVisible();
|
||||
95 |
|
||||
96 | const downloadPromise = page.waitForEvent('download');
|
||||
97 | await page.locator('button, a').filter({ hasText: 'Download Sample Excel Template' }).click();
|
||||
98 | const download = await downloadPromise;
|
||||
99 | expect(download.suggestedFilename()).toBeDefined();
|
||||
100 |
|
||||
101 | // 9. Orders Acceptance Flow
|
||||
102 | await page.locator('button, a').filter({ hasText: 'Orders & Transit' }).click();
|
||||
103 | await expect(page.getByText('Active Orders & Payout status')).toBeVisible();
|
||||
104 | await expect(page.getByText('Pending Acceptance')).toBeVisible();
|
||||
105 | await page.locator('button').filter({ hasText: 'Accept' }).first().click();
|
||||
106 | await expect(page.getByText('Ready to Ship')).toBeVisible();
|
||||
107 |
|
||||
108 | // 10. Barcode Generator
|
||||
109 | await page.locator('button, a').filter({ hasText: 'Barcode Generator' }).click();
|
||||
110 | await expect(page.getByText('Product Barcode Generator')).toBeVisible();
|
||||
111 | await page.locator('select#barcode-product').selectOption({ index: 1 });
|
||||
112 | await page.locator('button').filter({ hasText: 'Generate Barcode Graphic' }).click();
|
||||
113 | await expect(page.getByText('Generated Barcode Preview')).toBeVisible();
|
||||
114 | await expect(page.getByText('Print Label')).toBeVisible();
|
||||
115 | await expect(page.getByText('Download SVG')).toBeVisible();
|
||||
116 | });
|
||||
117 |
|
||||
118 | test('enforces security constraints and validation rules (negative test cases)', async ({ page }) => {
|
||||
119 | // 1. Visit Home and verify dashboard components are not in DOM (route guarding)
|
||||
120 | await page.goto('/');
|
||||
121 | await expect(page.locator('.dashboard-container')).toBeHidden();
|
||||
```
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
|
|
@ -0,0 +1,234 @@
|
|||
# Instructions
|
||||
|
||||
- Following Playwright test failed.
|
||||
- Explain why, be concise, respect Playwright best practices.
|
||||
- Provide a snippet of code with the fix, if possible.
|
||||
|
||||
# Test info
|
||||
|
||||
- Name: onboarding.spec.ts >> Supplier Portal Onboarding & Feature E2E Flow >> enforces security constraints and validation rules (negative test cases)
|
||||
- Location: playwright/e2e/onboarding.spec.ts:118:3
|
||||
|
||||
# Error details
|
||||
|
||||
```
|
||||
Error: expect(locator).toBeVisible() failed
|
||||
|
||||
Locator: getByText('Step 1 of 3: Contact Verification')
|
||||
Expected: visible
|
||||
Timeout: 5000ms
|
||||
Error: element(s) not found
|
||||
|
||||
Call log:
|
||||
- Expect "toBeVisible" with timeout 5000ms
|
||||
- waiting for getByText('Step 1 of 3: Contact Verification')
|
||||
|
||||
```
|
||||
|
||||
```yaml
|
||||
- banner:
|
||||
- text: A Global Artisans Hub
|
||||
- navigation:
|
||||
- button "Platform"
|
||||
- button "Pricing"
|
||||
- button "Success Stories"
|
||||
- button "Support"
|
||||
- button "Login"
|
||||
- button "Get Started"
|
||||
- main:
|
||||
- button "Login"
|
||||
- button "Register"
|
||||
- heading "Create Your Supplier Account" [level=2]
|
||||
- text: Business Email *
|
||||
- textbox "Business Email *":
|
||||
- /placeholder: Enter email address
|
||||
- text: security_test_1786700927306@example.com
|
||||
- text: Mobile Number *
|
||||
- textbox "Mobile Number *":
|
||||
- /placeholder: Enter 10-digit number
|
||||
- text: "9358500184"
|
||||
- text: Create Password *
|
||||
- textbox "Create Password *":
|
||||
- /placeholder: Minimum 8 characters
|
||||
- text: Password123!
|
||||
- button "🙈"
|
||||
- text: Confirm Password *
|
||||
- textbox "Confirm Password *":
|
||||
- /placeholder: Re-enter password
|
||||
- text: Password123!
|
||||
- button "🙈"
|
||||
- checkbox "I accept the Terms of Service and Privacy Policy *" [checked]
|
||||
- text: I accept the
|
||||
- link "Terms of Service":
|
||||
- /url: "#terms"
|
||||
- text: and
|
||||
- link "Privacy Policy":
|
||||
- /url: "#privacy"
|
||||
- text: "*"
|
||||
- button "Register Business"
|
||||
- contentinfo:
|
||||
- text: Global Artisans Hub
|
||||
- link "About Us":
|
||||
- /url: "#about"
|
||||
- link "Careers":
|
||||
- /url: "#careers"
|
||||
- link "Press":
|
||||
- /url: "#press"
|
||||
- link "Terms":
|
||||
- /url: "#terms"
|
||||
- link "Privacy":
|
||||
- /url: "#privacy"
|
||||
- text: © 2026 Global Artisans Hub. All rights reserved.
|
||||
```
|
||||
|
||||
# Test source
|
||||
|
||||
```ts
|
||||
36 | await page.locator('input#verify-gst').fill('29AAAAA1111A1Z1');
|
||||
37 | await page.locator('button').filter({ hasText: 'Submit GSTIN' }).click();
|
||||
38 | await expect(page.getByText('verification will be completed within next 24 hrs')).toBeVisible();
|
||||
39 |
|
||||
40 | // Upload mock Aadhaar & PAN files
|
||||
41 | await page.setInputFiles('input#aadhar-upload', {
|
||||
42 | name: 'aadhar_card.pdf',
|
||||
43 | mimeType: 'application/pdf',
|
||||
44 | buffer: Buffer.from('mock aadhaar pdf'),
|
||||
45 | });
|
||||
46 | await expect(page.getByText('Selected: aadhar_card.pdf')).toBeVisible();
|
||||
47 |
|
||||
48 | await page.setInputFiles('input#pan-upload', {
|
||||
49 | name: 'pan_card.pdf',
|
||||
50 | mimeType: 'application/pdf',
|
||||
51 | buffer: Buffer.from('mock pan pdf'),
|
||||
52 | });
|
||||
53 | await expect(page.getByText('Selected: pan_card.pdf')).toBeVisible();
|
||||
54 |
|
||||
55 | await page.locator('button').filter({ hasText: 'Next Step: Store & Location' }).click();
|
||||
56 |
|
||||
57 | // 5. Step 3: Store & Pickup Location Setup
|
||||
58 | await expect(page.getByText('Step 3 of 3: Store & Pickup Location')).toBeVisible();
|
||||
59 | await page.locator('input#store-name').clear();
|
||||
60 | await page.locator('input#store-name').fill('Teak Wood Craft Store');
|
||||
61 | await page.locator('textarea#store-bio').clear();
|
||||
62 | await page.locator('textarea#store-bio').fill('Teak wood hand-carved home decor elements.');
|
||||
63 |
|
||||
64 | // Test interactive map coordinate pinning
|
||||
65 | await expect(page.getByText('Store Location on Map *')).toBeVisible();
|
||||
66 | const mapGridParent = page.locator('span').filter({ hasText: 'Click anywhere on the map grid to pin location' }).locator('..');
|
||||
67 | await mapGridParent.click({ position: { x: 100, y: 80 } });
|
||||
68 | await expect(page.getByText('Coordinates: Lat')).toBeVisible();
|
||||
69 |
|
||||
70 | await expect(page.locator('input#location-text')).not.toHaveValue('');
|
||||
71 | await page.locator('input#loc-city').clear();
|
||||
72 | await page.locator('input#loc-city').fill('Bangalore');
|
||||
73 | await page.locator('input#loc-pincode').clear();
|
||||
74 | await page.locator('input#loc-pincode').fill('560001');
|
||||
75 | await page.locator('button').filter({ hasText: 'Submit Supplier Profile' }).click();
|
||||
76 |
|
||||
77 | // 6. Setup Welcome Tour Stepper
|
||||
78 | await expect(page.getByText('Supplier Account Under Review ⏳')).toBeVisible();
|
||||
79 | await page.locator('button').filter({ hasText: 'Start Guided Tour 🎬' }).click();
|
||||
80 | await expect(page.getByText('📦 Products & Inventory Management')).toBeVisible();
|
||||
81 | await page.locator('button').filter({ hasText: 'Next: Order Management' }).click();
|
||||
82 | await expect(page.getByText('🚚 Order Acceptance Control')).toBeVisible();
|
||||
83 | await page.locator('button').filter({ hasText: 'Next: Barcode Generation' }).click();
|
||||
84 | await expect(page.getByText('🏷️ Barcode Identification & Tracking')).toBeVisible();
|
||||
85 | await page.locator('button').filter({ hasText: 'Next: Earnings & Wallet' }).click();
|
||||
86 | await expect(page.getByText('💼 Wallet & Payout Withdrawals')).toBeVisible();
|
||||
87 | await page.locator('button').filter({ hasText: 'Explore Dashboard 🚀' }).click();
|
||||
88 |
|
||||
89 | // 7. Check Active Dashboard Tab
|
||||
90 | await expect(page.getByText('Performance Analytics')).toBeVisible();
|
||||
91 |
|
||||
92 | // 8. Manage Products tab & Bulk template download
|
||||
93 | await page.locator('button, a').filter({ hasText: 'Manage Products' }).click();
|
||||
94 | await expect(page.getByText('Active Inventory')).toBeVisible();
|
||||
95 |
|
||||
96 | const downloadPromise = page.waitForEvent('download');
|
||||
97 | await page.locator('button, a').filter({ hasText: 'Download Sample Excel Template' }).click();
|
||||
98 | const download = await downloadPromise;
|
||||
99 | expect(download.suggestedFilename()).toBeDefined();
|
||||
100 |
|
||||
101 | // 9. Orders Acceptance Flow
|
||||
102 | await page.locator('button, a').filter({ hasText: 'Orders & Transit' }).click();
|
||||
103 | await expect(page.getByText('Active Orders & Payout status')).toBeVisible();
|
||||
104 | await expect(page.getByText('Pending Acceptance')).toBeVisible();
|
||||
105 | await page.locator('button').filter({ hasText: 'Accept' }).first().click();
|
||||
106 | await expect(page.getByText('Ready to Ship')).toBeVisible();
|
||||
107 |
|
||||
108 | // 10. Barcode Generator
|
||||
109 | await page.locator('button, a').filter({ hasText: 'Barcode Generator' }).click();
|
||||
110 | await expect(page.getByText('Product Barcode Generator')).toBeVisible();
|
||||
111 | await page.locator('select#barcode-product').selectOption({ index: 1 });
|
||||
112 | await page.locator('button').filter({ hasText: 'Generate Barcode Graphic' }).click();
|
||||
113 | await expect(page.getByText('Generated Barcode Preview')).toBeVisible();
|
||||
114 | await expect(page.getByText('Print Label')).toBeVisible();
|
||||
115 | await expect(page.getByText('Download SVG')).toBeVisible();
|
||||
116 | });
|
||||
117 |
|
||||
118 | test('enforces security constraints and validation rules (negative test cases)', async ({ page }) => {
|
||||
119 | // 1. Visit Home and verify dashboard components are not in DOM (route guarding)
|
||||
120 | await page.goto('/');
|
||||
121 | await expect(page.locator('.dashboard-container')).toBeHidden();
|
||||
122 | await expect(page.locator('.sidebar-menu')).toBeHidden();
|
||||
123 |
|
||||
124 | // 2. Go to Registration
|
||||
125 | await page.locator('button, a').filter({ hasText: 'Get Started Today' }).first().click();
|
||||
126 | const randSecurityUser = `security_test_${Date.now()}@example.com`;
|
||||
127 | const randSecurityPhone = `9${Math.floor(100000000 + Math.random() * 900000000)}`;
|
||||
128 | await page.locator('input#reg-email').fill(randSecurityUser);
|
||||
129 | await page.locator('input#reg-phone').fill(randSecurityPhone);
|
||||
130 | await page.locator('input#reg-pass').fill('Password123!');
|
||||
131 | await page.locator('input#reg-confirm-pass').fill('Password123!');
|
||||
132 | await page.locator('input#reg-policy').check();
|
||||
133 | await page.locator('button').filter({ hasText: 'Register Business' }).click();
|
||||
134 |
|
||||
135 | // 3. Step 1: Negative OTP validations
|
||||
> 136 | await expect(page.getByText('Step 1 of 3: Contact Verification')).toBeVisible();
|
||||
| ^ Error: expect(locator).toBeVisible() failed
|
||||
137 |
|
||||
138 | // Proceed button must be disabled initially
|
||||
139 | await expect(page.locator('button').filter({ hasText: 'Next Step: Identity Verification' })).toBeDisabled();
|
||||
140 |
|
||||
141 | // Submit invalid WhatsApp OTP
|
||||
142 | await page.locator('button').filter({ hasText: 'Send WhatsApp OTP' }).click();
|
||||
143 | await page.locator('input[placeholder="Enter 123456"]').first().fill('000000');
|
||||
144 |
|
||||
145 | // Capture dialog
|
||||
146 | let alertMessage = '';
|
||||
147 | page.once('dialog', async dialog => {
|
||||
148 | alertMessage = dialog.message();
|
||||
149 | await dialog.accept();
|
||||
150 | });
|
||||
151 |
|
||||
152 | await page.locator('button').filter({ hasText: 'Verify Code' }).first().click();
|
||||
153 |
|
||||
154 | // Give brief time/event loop ticks to ensure dialog handler fires
|
||||
155 | await page.waitForTimeout(500);
|
||||
156 | expect(alertMessage).toBe('Incorrect code. Enter 123456');
|
||||
157 | await expect(page.getByText('✓ Mobile & WhatsApp Verified')).toBeHidden();
|
||||
158 |
|
||||
159 | // Correct the code to enable proceed
|
||||
160 | await page.locator('input[placeholder="Enter 123456"]').first().clear();
|
||||
161 | await page.locator('input[placeholder="Enter 123456"]').first().fill('123456');
|
||||
162 | await page.locator('button').filter({ hasText: 'Verify Code' }).first().click();
|
||||
163 | await expect(page.getByText('✓ Mobile & WhatsApp Verified')).toBeVisible();
|
||||
164 |
|
||||
165 | // Submit invalid Email OTP
|
||||
166 | await page.locator('button').filter({ hasText: 'Send Email OTP' }).click();
|
||||
167 | await page.locator('input[placeholder="Enter 123456"]').last().fill('999999');
|
||||
168 |
|
||||
169 | page.once('dialog', async dialog => {
|
||||
170 | alertMessage = dialog.message();
|
||||
171 | await dialog.accept();
|
||||
172 | });
|
||||
173 |
|
||||
174 | await page.locator('button').filter({ hasText: 'Verify Code' }).last().click();
|
||||
175 | await page.waitForTimeout(500);
|
||||
176 | expect(alertMessage).toBe('Incorrect code. Enter 123456');
|
||||
177 | await expect(page.getByText('✓ Email Verified')).toBeHidden();
|
||||
178 | await expect(page.locator('button').filter({ hasText: 'Next Step: Identity Verification' })).toBeDisabled();
|
||||
179 | });
|
||||
180 | });
|
||||
181 |
|
||||
```
|
||||
49
playwright-report/index.html
Normal file
49
playwright-report/index.html
Normal file
File diff suppressed because one or more lines are too long
30
playwright.config.ts
Normal file
30
playwright.config.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './playwright/e2e',
|
||||
fullyParallel: true,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
reporter: 'html',
|
||||
use: {
|
||||
baseURL: 'https://betasuppliers.tipro.in/',
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
video: 'retain-on-failure',
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
{
|
||||
name: 'firefox',
|
||||
use: { ...devices['Desktop Firefox'] },
|
||||
},
|
||||
{
|
||||
name: 'webkit',
|
||||
use: { ...devices['Desktop Safari'] },
|
||||
},
|
||||
],
|
||||
});
|
||||
180
playwright/e2e/onboarding.spec.ts
Normal file
180
playwright/e2e/onboarding.spec.ts
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Supplier Portal Onboarding & Feature E2E Flow', () => {
|
||||
test('completes the registration, contact verification, document upload, location pinning, welcome tour, order acceptance, and barcode generation', async ({ page }) => {
|
||||
// 1. Visit Landing Page
|
||||
await page.goto('/');
|
||||
await expect(page.getByText('Sell Globally.')).toBeVisible();
|
||||
|
||||
// 2. Registration Page
|
||||
await page.locator('button, a').filter({ hasText: 'Get Started Today' }).first().click();
|
||||
const randUser = `test_seller_${Date.now()}@example.com`;
|
||||
const randPhone = `9${Math.floor(100000000 + Math.random() * 900000000)}`;
|
||||
await page.locator('input#reg-email').fill(randUser);
|
||||
await page.locator('input#reg-phone').fill(randPhone);
|
||||
await page.locator('input#reg-pass').fill('Super_secure_pass_123!');
|
||||
await page.locator('input#reg-confirm-pass').fill('Super_secure_pass_123!');
|
||||
await page.locator('input#reg-policy').check();
|
||||
await page.locator('button').filter({ hasText: 'Register Business' }).click();
|
||||
|
||||
// 3. Step 1: Contact Verification
|
||||
await expect(page.getByText('Step 1 of 3: Contact Verification')).toBeVisible();
|
||||
await page.locator('button').filter({ hasText: 'Send WhatsApp OTP' }).click();
|
||||
await page.locator('input[placeholder="Enter 123456"]').first().fill('123456');
|
||||
await page.locator('button').filter({ hasText: 'Verify Code' }).first().click();
|
||||
await expect(page.getByText('✓ Mobile & WhatsApp Verified')).toBeVisible();
|
||||
|
||||
await page.locator('button').filter({ hasText: 'Send Email OTP' }).click();
|
||||
await page.locator('input[placeholder="Enter 123456"]').last().fill('123456');
|
||||
await page.locator('button').filter({ hasText: 'Verify Code' }).last().click();
|
||||
await expect(page.getByText('✓ Email Verified')).toBeVisible();
|
||||
await page.locator('button').filter({ hasText: 'Next Step: Identity Verification' }).click();
|
||||
|
||||
// 4. Step 2: Tax & Identity Verification
|
||||
await expect(page.getByText('Step 2 of 3: Tax & Identity Verification')).toBeVisible();
|
||||
await page.locator('input#verify-gst').clear();
|
||||
await page.locator('input#verify-gst').fill('29AAAAA1111A1Z1');
|
||||
await page.locator('button').filter({ hasText: 'Submit GSTIN' }).click();
|
||||
await expect(page.getByText('verification will be completed within next 24 hrs')).toBeVisible();
|
||||
|
||||
// Upload mock Aadhaar & PAN files
|
||||
await page.setInputFiles('input#aadhar-upload', {
|
||||
name: 'aadhar_card.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
buffer: Buffer.from('mock aadhaar pdf'),
|
||||
});
|
||||
await expect(page.getByText('Selected: aadhar_card.pdf')).toBeVisible();
|
||||
|
||||
await page.setInputFiles('input#pan-upload', {
|
||||
name: 'pan_card.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
buffer: Buffer.from('mock pan pdf'),
|
||||
});
|
||||
await expect(page.getByText('Selected: pan_card.pdf')).toBeVisible();
|
||||
|
||||
await page.locator('button').filter({ hasText: 'Next Step: Store & Location' }).click();
|
||||
|
||||
// 5. Step 3: Store & Pickup Location Setup
|
||||
await expect(page.getByText('Step 3 of 3: Store & Pickup Location')).toBeVisible();
|
||||
await page.locator('input#store-name').clear();
|
||||
await page.locator('input#store-name').fill('Teak Wood Craft Store');
|
||||
await page.locator('textarea#store-bio').clear();
|
||||
await page.locator('textarea#store-bio').fill('Teak wood hand-carved home decor elements.');
|
||||
|
||||
// Test interactive map coordinate pinning
|
||||
await expect(page.getByText('Store Location on Map *')).toBeVisible();
|
||||
const mapGridParent = page.locator('span').filter({ hasText: 'Click anywhere on the map grid to pin location' }).locator('..');
|
||||
await mapGridParent.click({ position: { x: 100, y: 80 } });
|
||||
await expect(page.getByText('Coordinates: Lat')).toBeVisible();
|
||||
|
||||
await expect(page.locator('input#location-text')).not.toHaveValue('');
|
||||
await page.locator('input#loc-city').clear();
|
||||
await page.locator('input#loc-city').fill('Bangalore');
|
||||
await page.locator('input#loc-pincode').clear();
|
||||
await page.locator('input#loc-pincode').fill('560001');
|
||||
await page.locator('button').filter({ hasText: 'Submit Supplier Profile' }).click();
|
||||
|
||||
// 6. Setup Welcome Tour Stepper
|
||||
await expect(page.getByText('Supplier Account Under Review ⏳')).toBeVisible();
|
||||
await page.locator('button').filter({ hasText: 'Start Guided Tour 🎬' }).click();
|
||||
await expect(page.getByText('📦 Products & Inventory Management')).toBeVisible();
|
||||
await page.locator('button').filter({ hasText: 'Next: Order Management' }).click();
|
||||
await expect(page.getByText('🚚 Order Acceptance Control')).toBeVisible();
|
||||
await page.locator('button').filter({ hasText: 'Next: Barcode Generation' }).click();
|
||||
await expect(page.getByText('🏷️ Barcode Identification & Tracking')).toBeVisible();
|
||||
await page.locator('button').filter({ hasText: 'Next: Earnings & Wallet' }).click();
|
||||
await expect(page.getByText('💼 Wallet & Payout Withdrawals')).toBeVisible();
|
||||
await page.locator('button').filter({ hasText: 'Explore Dashboard 🚀' }).click();
|
||||
|
||||
// 7. Check Active Dashboard Tab
|
||||
await expect(page.getByText('Performance Analytics')).toBeVisible();
|
||||
|
||||
// 8. Manage Products tab & Bulk template download
|
||||
await page.locator('button, a').filter({ hasText: 'Manage Products' }).click();
|
||||
await expect(page.getByText('Active Inventory')).toBeVisible();
|
||||
|
||||
const downloadPromise = page.waitForEvent('download');
|
||||
await page.locator('button, a').filter({ hasText: 'Download Sample Excel Template' }).click();
|
||||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toBeDefined();
|
||||
|
||||
// 9. Orders Acceptance Flow
|
||||
await page.locator('button, a').filter({ hasText: 'Orders & Transit' }).click();
|
||||
await expect(page.getByText('Active Orders & Payout status')).toBeVisible();
|
||||
await expect(page.getByText('Pending Acceptance')).toBeVisible();
|
||||
await page.locator('button').filter({ hasText: 'Accept' }).first().click();
|
||||
await expect(page.getByText('Ready to Ship')).toBeVisible();
|
||||
|
||||
// 10. Barcode Generator
|
||||
await page.locator('button, a').filter({ hasText: 'Barcode Generator' }).click();
|
||||
await expect(page.getByText('Product Barcode Generator')).toBeVisible();
|
||||
await page.locator('select#barcode-product').selectOption({ index: 1 });
|
||||
await page.locator('button').filter({ hasText: 'Generate Barcode Graphic' }).click();
|
||||
await expect(page.getByText('Generated Barcode Preview')).toBeVisible();
|
||||
await expect(page.getByText('Print Label')).toBeVisible();
|
||||
await expect(page.getByText('Download SVG')).toBeVisible();
|
||||
});
|
||||
|
||||
test('enforces security constraints and validation rules (negative test cases)', async ({ page }) => {
|
||||
// 1. Visit Home and verify dashboard components are not in DOM (route guarding)
|
||||
await page.goto('/');
|
||||
await expect(page.locator('.dashboard-container')).toBeHidden();
|
||||
await expect(page.locator('.sidebar-menu')).toBeHidden();
|
||||
|
||||
// 2. Go to Registration
|
||||
await page.locator('button, a').filter({ hasText: 'Get Started Today' }).first().click();
|
||||
const randSecurityUser = `security_test_${Date.now()}@example.com`;
|
||||
const randSecurityPhone = `9${Math.floor(100000000 + Math.random() * 900000000)}`;
|
||||
await page.locator('input#reg-email').fill(randSecurityUser);
|
||||
await page.locator('input#reg-phone').fill(randSecurityPhone);
|
||||
await page.locator('input#reg-pass').fill('Password123!');
|
||||
await page.locator('input#reg-confirm-pass').fill('Password123!');
|
||||
await page.locator('input#reg-policy').check();
|
||||
await page.locator('button').filter({ hasText: 'Register Business' }).click();
|
||||
|
||||
// 3. Step 1: Negative OTP validations
|
||||
await expect(page.getByText('Step 1 of 3: Contact Verification')).toBeVisible();
|
||||
|
||||
// Proceed button must be disabled initially
|
||||
await expect(page.locator('button').filter({ hasText: 'Next Step: Identity Verification' })).toBeDisabled();
|
||||
|
||||
// Submit invalid WhatsApp OTP
|
||||
await page.locator('button').filter({ hasText: 'Send WhatsApp OTP' }).click();
|
||||
await page.locator('input[placeholder="Enter 123456"]').first().fill('000000');
|
||||
|
||||
// Capture dialog
|
||||
let alertMessage = '';
|
||||
page.once('dialog', async dialog => {
|
||||
alertMessage = dialog.message();
|
||||
await dialog.accept();
|
||||
});
|
||||
|
||||
await page.locator('button').filter({ hasText: 'Verify Code' }).first().click();
|
||||
|
||||
// Give brief time/event loop ticks to ensure dialog handler fires
|
||||
await page.waitForTimeout(500);
|
||||
expect(alertMessage).toBe('Incorrect code. Enter 123456');
|
||||
await expect(page.getByText('✓ Mobile & WhatsApp Verified')).toBeHidden();
|
||||
|
||||
// Correct the code to enable proceed
|
||||
await page.locator('input[placeholder="Enter 123456"]').first().clear();
|
||||
await page.locator('input[placeholder="Enter 123456"]').first().fill('123456');
|
||||
await page.locator('button').filter({ hasText: 'Verify Code' }).first().click();
|
||||
await expect(page.getByText('✓ Mobile & WhatsApp Verified')).toBeVisible();
|
||||
|
||||
// Submit invalid Email OTP
|
||||
await page.locator('button').filter({ hasText: 'Send Email OTP' }).click();
|
||||
await page.locator('input[placeholder="Enter 123456"]').last().fill('999999');
|
||||
|
||||
page.once('dialog', async dialog => {
|
||||
alertMessage = dialog.message();
|
||||
await dialog.accept();
|
||||
});
|
||||
|
||||
await page.locator('button').filter({ hasText: 'Verify Code' }).last().click();
|
||||
await page.waitForTimeout(500);
|
||||
expect(alertMessage).toBe('Incorrect code. Enter 123456');
|
||||
await expect(page.getByText('✓ Email Verified')).toBeHidden();
|
||||
await expect(page.locator('button').filter({ hasText: 'Next Step: Identity Verification' })).toBeDisabled();
|
||||
});
|
||||
});
|
||||
7
test-results/.last-run.json
Normal file
7
test-results/.last-run.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"status": "failed",
|
||||
"failedTests": [
|
||||
"fe534f0825407f213faa-eae28aa470cee3dd351a",
|
||||
"fe534f0825407f213faa-178b974dec5b4368ccdd"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
# Instructions
|
||||
|
||||
- Following Playwright test failed.
|
||||
- Explain why, be concise, respect Playwright best practices.
|
||||
- Provide a snippet of code with the fix, if possible.
|
||||
|
||||
# Test info
|
||||
|
||||
- Name: onboarding.spec.ts >> Supplier Portal Onboarding & Feature E2E Flow >> enforces security constraints and validation rules (negative test cases)
|
||||
- Location: playwright/e2e/onboarding.spec.ts:118:3
|
||||
|
||||
# Error details
|
||||
|
||||
```
|
||||
Error: expect(locator).toBeVisible() failed
|
||||
|
||||
Locator: getByText('Step 1 of 3: Contact Verification')
|
||||
Expected: visible
|
||||
Timeout: 5000ms
|
||||
Error: element(s) not found
|
||||
|
||||
Call log:
|
||||
- Expect "toBeVisible" with timeout 5000ms
|
||||
- waiting for getByText('Step 1 of 3: Contact Verification')
|
||||
|
||||
```
|
||||
|
||||
```yaml
|
||||
- banner:
|
||||
- text: A Global Artisans Hub
|
||||
- navigation:
|
||||
- button "Platform"
|
||||
- button "Pricing"
|
||||
- button "Success Stories"
|
||||
- button "Support"
|
||||
- button "Login"
|
||||
- button "Get Started"
|
||||
- main:
|
||||
- button "Login"
|
||||
- button "Register"
|
||||
- heading "Create Your Supplier Account" [level=2]
|
||||
- text: Business Email *
|
||||
- textbox "Business Email *":
|
||||
- /placeholder: Enter email address
|
||||
- text: security_test_1786700927306@example.com
|
||||
- text: Mobile Number *
|
||||
- textbox "Mobile Number *":
|
||||
- /placeholder: Enter 10-digit number
|
||||
- text: "9358500184"
|
||||
- text: Create Password *
|
||||
- textbox "Create Password *":
|
||||
- /placeholder: Minimum 8 characters
|
||||
- text: Password123!
|
||||
- button "🙈"
|
||||
- text: Confirm Password *
|
||||
- textbox "Confirm Password *":
|
||||
- /placeholder: Re-enter password
|
||||
- text: Password123!
|
||||
- button "🙈"
|
||||
- checkbox "I accept the Terms of Service and Privacy Policy *" [checked]
|
||||
- text: I accept the
|
||||
- link "Terms of Service":
|
||||
- /url: "#terms"
|
||||
- text: and
|
||||
- link "Privacy Policy":
|
||||
- /url: "#privacy"
|
||||
- text: "*"
|
||||
- button "Register Business"
|
||||
- contentinfo:
|
||||
- text: Global Artisans Hub
|
||||
- link "About Us":
|
||||
- /url: "#about"
|
||||
- link "Careers":
|
||||
- /url: "#careers"
|
||||
- link "Press":
|
||||
- /url: "#press"
|
||||
- link "Terms":
|
||||
- /url: "#terms"
|
||||
- link "Privacy":
|
||||
- /url: "#privacy"
|
||||
- text: © 2026 Global Artisans Hub. All rights reserved.
|
||||
```
|
||||
|
||||
# Test source
|
||||
|
||||
```ts
|
||||
36 | await page.locator('input#verify-gst').fill('29AAAAA1111A1Z1');
|
||||
37 | await page.locator('button').filter({ hasText: 'Submit GSTIN' }).click();
|
||||
38 | await expect(page.getByText('verification will be completed within next 24 hrs')).toBeVisible();
|
||||
39 |
|
||||
40 | // Upload mock Aadhaar & PAN files
|
||||
41 | await page.setInputFiles('input#aadhar-upload', {
|
||||
42 | name: 'aadhar_card.pdf',
|
||||
43 | mimeType: 'application/pdf',
|
||||
44 | buffer: Buffer.from('mock aadhaar pdf'),
|
||||
45 | });
|
||||
46 | await expect(page.getByText('Selected: aadhar_card.pdf')).toBeVisible();
|
||||
47 |
|
||||
48 | await page.setInputFiles('input#pan-upload', {
|
||||
49 | name: 'pan_card.pdf',
|
||||
50 | mimeType: 'application/pdf',
|
||||
51 | buffer: Buffer.from('mock pan pdf'),
|
||||
52 | });
|
||||
53 | await expect(page.getByText('Selected: pan_card.pdf')).toBeVisible();
|
||||
54 |
|
||||
55 | await page.locator('button').filter({ hasText: 'Next Step: Store & Location' }).click();
|
||||
56 |
|
||||
57 | // 5. Step 3: Store & Pickup Location Setup
|
||||
58 | await expect(page.getByText('Step 3 of 3: Store & Pickup Location')).toBeVisible();
|
||||
59 | await page.locator('input#store-name').clear();
|
||||
60 | await page.locator('input#store-name').fill('Teak Wood Craft Store');
|
||||
61 | await page.locator('textarea#store-bio').clear();
|
||||
62 | await page.locator('textarea#store-bio').fill('Teak wood hand-carved home decor elements.');
|
||||
63 |
|
||||
64 | // Test interactive map coordinate pinning
|
||||
65 | await expect(page.getByText('Store Location on Map *')).toBeVisible();
|
||||
66 | const mapGridParent = page.locator('span').filter({ hasText: 'Click anywhere on the map grid to pin location' }).locator('..');
|
||||
67 | await mapGridParent.click({ position: { x: 100, y: 80 } });
|
||||
68 | await expect(page.getByText('Coordinates: Lat')).toBeVisible();
|
||||
69 |
|
||||
70 | await expect(page.locator('input#location-text')).not.toHaveValue('');
|
||||
71 | await page.locator('input#loc-city').clear();
|
||||
72 | await page.locator('input#loc-city').fill('Bangalore');
|
||||
73 | await page.locator('input#loc-pincode').clear();
|
||||
74 | await page.locator('input#loc-pincode').fill('560001');
|
||||
75 | await page.locator('button').filter({ hasText: 'Submit Supplier Profile' }).click();
|
||||
76 |
|
||||
77 | // 6. Setup Welcome Tour Stepper
|
||||
78 | await expect(page.getByText('Supplier Account Under Review ⏳')).toBeVisible();
|
||||
79 | await page.locator('button').filter({ hasText: 'Start Guided Tour 🎬' }).click();
|
||||
80 | await expect(page.getByText('📦 Products & Inventory Management')).toBeVisible();
|
||||
81 | await page.locator('button').filter({ hasText: 'Next: Order Management' }).click();
|
||||
82 | await expect(page.getByText('🚚 Order Acceptance Control')).toBeVisible();
|
||||
83 | await page.locator('button').filter({ hasText: 'Next: Barcode Generation' }).click();
|
||||
84 | await expect(page.getByText('🏷️ Barcode Identification & Tracking')).toBeVisible();
|
||||
85 | await page.locator('button').filter({ hasText: 'Next: Earnings & Wallet' }).click();
|
||||
86 | await expect(page.getByText('💼 Wallet & Payout Withdrawals')).toBeVisible();
|
||||
87 | await page.locator('button').filter({ hasText: 'Explore Dashboard 🚀' }).click();
|
||||
88 |
|
||||
89 | // 7. Check Active Dashboard Tab
|
||||
90 | await expect(page.getByText('Performance Analytics')).toBeVisible();
|
||||
91 |
|
||||
92 | // 8. Manage Products tab & Bulk template download
|
||||
93 | await page.locator('button, a').filter({ hasText: 'Manage Products' }).click();
|
||||
94 | await expect(page.getByText('Active Inventory')).toBeVisible();
|
||||
95 |
|
||||
96 | const downloadPromise = page.waitForEvent('download');
|
||||
97 | await page.locator('button, a').filter({ hasText: 'Download Sample Excel Template' }).click();
|
||||
98 | const download = await downloadPromise;
|
||||
99 | expect(download.suggestedFilename()).toBeDefined();
|
||||
100 |
|
||||
101 | // 9. Orders Acceptance Flow
|
||||
102 | await page.locator('button, a').filter({ hasText: 'Orders & Transit' }).click();
|
||||
103 | await expect(page.getByText('Active Orders & Payout status')).toBeVisible();
|
||||
104 | await expect(page.getByText('Pending Acceptance')).toBeVisible();
|
||||
105 | await page.locator('button').filter({ hasText: 'Accept' }).first().click();
|
||||
106 | await expect(page.getByText('Ready to Ship')).toBeVisible();
|
||||
107 |
|
||||
108 | // 10. Barcode Generator
|
||||
109 | await page.locator('button, a').filter({ hasText: 'Barcode Generator' }).click();
|
||||
110 | await expect(page.getByText('Product Barcode Generator')).toBeVisible();
|
||||
111 | await page.locator('select#barcode-product').selectOption({ index: 1 });
|
||||
112 | await page.locator('button').filter({ hasText: 'Generate Barcode Graphic' }).click();
|
||||
113 | await expect(page.getByText('Generated Barcode Preview')).toBeVisible();
|
||||
114 | await expect(page.getByText('Print Label')).toBeVisible();
|
||||
115 | await expect(page.getByText('Download SVG')).toBeVisible();
|
||||
116 | });
|
||||
117 |
|
||||
118 | test('enforces security constraints and validation rules (negative test cases)', async ({ page }) => {
|
||||
119 | // 1. Visit Home and verify dashboard components are not in DOM (route guarding)
|
||||
120 | await page.goto('/');
|
||||
121 | await expect(page.locator('.dashboard-container')).toBeHidden();
|
||||
122 | await expect(page.locator('.sidebar-menu')).toBeHidden();
|
||||
123 |
|
||||
124 | // 2. Go to Registration
|
||||
125 | await page.locator('button, a').filter({ hasText: 'Get Started Today' }).first().click();
|
||||
126 | const randSecurityUser = `security_test_${Date.now()}@example.com`;
|
||||
127 | const randSecurityPhone = `9${Math.floor(100000000 + Math.random() * 900000000)}`;
|
||||
128 | await page.locator('input#reg-email').fill(randSecurityUser);
|
||||
129 | await page.locator('input#reg-phone').fill(randSecurityPhone);
|
||||
130 | await page.locator('input#reg-pass').fill('Password123!');
|
||||
131 | await page.locator('input#reg-confirm-pass').fill('Password123!');
|
||||
132 | await page.locator('input#reg-policy').check();
|
||||
133 | await page.locator('button').filter({ hasText: 'Register Business' }).click();
|
||||
134 |
|
||||
135 | // 3. Step 1: Negative OTP validations
|
||||
> 136 | await expect(page.getByText('Step 1 of 3: Contact Verification')).toBeVisible();
|
||||
| ^ Error: expect(locator).toBeVisible() failed
|
||||
137 |
|
||||
138 | // Proceed button must be disabled initially
|
||||
139 | await expect(page.locator('button').filter({ hasText: 'Next Step: Identity Verification' })).toBeDisabled();
|
||||
140 |
|
||||
141 | // Submit invalid WhatsApp OTP
|
||||
142 | await page.locator('button').filter({ hasText: 'Send WhatsApp OTP' }).click();
|
||||
143 | await page.locator('input[placeholder="Enter 123456"]').first().fill('000000');
|
||||
144 |
|
||||
145 | // Capture dialog
|
||||
146 | let alertMessage = '';
|
||||
147 | page.once('dialog', async dialog => {
|
||||
148 | alertMessage = dialog.message();
|
||||
149 | await dialog.accept();
|
||||
150 | });
|
||||
151 |
|
||||
152 | await page.locator('button').filter({ hasText: 'Verify Code' }).first().click();
|
||||
153 |
|
||||
154 | // Give brief time/event loop ticks to ensure dialog handler fires
|
||||
155 | await page.waitForTimeout(500);
|
||||
156 | expect(alertMessage).toBe('Incorrect code. Enter 123456');
|
||||
157 | await expect(page.getByText('✓ Mobile & WhatsApp Verified')).toBeHidden();
|
||||
158 |
|
||||
159 | // Correct the code to enable proceed
|
||||
160 | await page.locator('input[placeholder="Enter 123456"]').first().clear();
|
||||
161 | await page.locator('input[placeholder="Enter 123456"]').first().fill('123456');
|
||||
162 | await page.locator('button').filter({ hasText: 'Verify Code' }).first().click();
|
||||
163 | await expect(page.getByText('✓ Mobile & WhatsApp Verified')).toBeVisible();
|
||||
164 |
|
||||
165 | // Submit invalid Email OTP
|
||||
166 | await page.locator('button').filter({ hasText: 'Send Email OTP' }).click();
|
||||
167 | await page.locator('input[placeholder="Enter 123456"]').last().fill('999999');
|
||||
168 |
|
||||
169 | page.once('dialog', async dialog => {
|
||||
170 | alertMessage = dialog.message();
|
||||
171 | await dialog.accept();
|
||||
172 | });
|
||||
173 |
|
||||
174 | await page.locator('button').filter({ hasText: 'Verify Code' }).last().click();
|
||||
175 | await page.waitForTimeout(500);
|
||||
176 | expect(alertMessage).toBe('Incorrect code. Enter 123456');
|
||||
177 | await expect(page.getByText('✓ Email Verified')).toBeHidden();
|
||||
178 | await expect(page.locator('button').filter({ hasText: 'Next Step: Identity Verification' })).toBeDisabled();
|
||||
179 | });
|
||||
180 | });
|
||||
181 |
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
|
|
@ -0,0 +1,209 @@
|
|||
# Instructions
|
||||
|
||||
- Following Playwright test failed.
|
||||
- Explain why, be concise, respect Playwright best practices.
|
||||
- Provide a snippet of code with the fix, if possible.
|
||||
|
||||
# Test info
|
||||
|
||||
- Name: onboarding.spec.ts >> Supplier Portal Onboarding & Feature E2E Flow >> completes the registration, contact verification, document upload, location pinning, welcome tour, order acceptance, and barcode generation
|
||||
- Location: playwright/e2e/onboarding.spec.ts:4:3
|
||||
|
||||
# Error details
|
||||
|
||||
```
|
||||
Error: expect(locator).toBeVisible() failed
|
||||
|
||||
Locator: getByText('Step 1 of 3: Contact Verification')
|
||||
Expected: visible
|
||||
Timeout: 5000ms
|
||||
Error: element(s) not found
|
||||
|
||||
Call log:
|
||||
- Expect "toBeVisible" with timeout 5000ms
|
||||
- waiting for getByText('Step 1 of 3: Contact Verification')
|
||||
|
||||
```
|
||||
|
||||
```yaml
|
||||
- banner:
|
||||
- text: A Global Artisans Hub
|
||||
- navigation:
|
||||
- button "Platform"
|
||||
- button "Pricing"
|
||||
- button "Success Stories"
|
||||
- button "Support"
|
||||
- button "Login"
|
||||
- button "Get Started"
|
||||
- main:
|
||||
- button "Login"
|
||||
- button "Register"
|
||||
- heading "Create Your Supplier Account" [level=2]
|
||||
- text: Business Email *
|
||||
- textbox "Business Email *":
|
||||
- /placeholder: Enter email address
|
||||
- text: test_seller_1786700927358@example.com
|
||||
- text: Mobile Number *
|
||||
- textbox "Mobile Number *":
|
||||
- /placeholder: Enter 10-digit number
|
||||
- text: "9118192284"
|
||||
- text: Create Password *
|
||||
- textbox "Create Password *":
|
||||
- /placeholder: Minimum 8 characters
|
||||
- text: Super_secure_pass_123!
|
||||
- button "🙈"
|
||||
- text: Confirm Password *
|
||||
- textbox "Confirm Password *":
|
||||
- /placeholder: Re-enter password
|
||||
- text: Super_secure_pass_123!
|
||||
- button "🙈"
|
||||
- checkbox "I accept the Terms of Service and Privacy Policy *" [checked]
|
||||
- text: I accept the
|
||||
- link "Terms of Service":
|
||||
- /url: "#terms"
|
||||
- text: and
|
||||
- link "Privacy Policy":
|
||||
- /url: "#privacy"
|
||||
- text: "*"
|
||||
- button "Register Business"
|
||||
- contentinfo:
|
||||
- text: Global Artisans Hub
|
||||
- link "About Us":
|
||||
- /url: "#about"
|
||||
- link "Careers":
|
||||
- /url: "#careers"
|
||||
- link "Press":
|
||||
- /url: "#press"
|
||||
- link "Terms":
|
||||
- /url: "#terms"
|
||||
- link "Privacy":
|
||||
- /url: "#privacy"
|
||||
- text: © 2026 Global Artisans Hub. All rights reserved.
|
||||
```
|
||||
|
||||
# Test source
|
||||
|
||||
```ts
|
||||
1 | import { test, expect } from '@playwright/test';
|
||||
2 |
|
||||
3 | test.describe('Supplier Portal Onboarding & Feature E2E Flow', () => {
|
||||
4 | test('completes the registration, contact verification, document upload, location pinning, welcome tour, order acceptance, and barcode generation', async ({ page }) => {
|
||||
5 | // 1. Visit Landing Page
|
||||
6 | await page.goto('/');
|
||||
7 | await expect(page.getByText('Sell Globally.')).toBeVisible();
|
||||
8 |
|
||||
9 | // 2. Registration Page
|
||||
10 | await page.locator('button, a').filter({ hasText: 'Get Started Today' }).first().click();
|
||||
11 | const randUser = `test_seller_${Date.now()}@example.com`;
|
||||
12 | const randPhone = `9${Math.floor(100000000 + Math.random() * 900000000)}`;
|
||||
13 | await page.locator('input#reg-email').fill(randUser);
|
||||
14 | await page.locator('input#reg-phone').fill(randPhone);
|
||||
15 | await page.locator('input#reg-pass').fill('Super_secure_pass_123!');
|
||||
16 | await page.locator('input#reg-confirm-pass').fill('Super_secure_pass_123!');
|
||||
17 | await page.locator('input#reg-policy').check();
|
||||
18 | await page.locator('button').filter({ hasText: 'Register Business' }).click();
|
||||
19 |
|
||||
20 | // 3. Step 1: Contact Verification
|
||||
> 21 | await expect(page.getByText('Step 1 of 3: Contact Verification')).toBeVisible();
|
||||
| ^ Error: expect(locator).toBeVisible() failed
|
||||
22 | await page.locator('button').filter({ hasText: 'Send WhatsApp OTP' }).click();
|
||||
23 | await page.locator('input[placeholder="Enter 123456"]').first().fill('123456');
|
||||
24 | await page.locator('button').filter({ hasText: 'Verify Code' }).first().click();
|
||||
25 | await expect(page.getByText('✓ Mobile & WhatsApp Verified')).toBeVisible();
|
||||
26 |
|
||||
27 | await page.locator('button').filter({ hasText: 'Send Email OTP' }).click();
|
||||
28 | await page.locator('input[placeholder="Enter 123456"]').last().fill('123456');
|
||||
29 | await page.locator('button').filter({ hasText: 'Verify Code' }).last().click();
|
||||
30 | await expect(page.getByText('✓ Email Verified')).toBeVisible();
|
||||
31 | await page.locator('button').filter({ hasText: 'Next Step: Identity Verification' }).click();
|
||||
32 |
|
||||
33 | // 4. Step 2: Tax & Identity Verification
|
||||
34 | await expect(page.getByText('Step 2 of 3: Tax & Identity Verification')).toBeVisible();
|
||||
35 | await page.locator('input#verify-gst').clear();
|
||||
36 | await page.locator('input#verify-gst').fill('29AAAAA1111A1Z1');
|
||||
37 | await page.locator('button').filter({ hasText: 'Submit GSTIN' }).click();
|
||||
38 | await expect(page.getByText('verification will be completed within next 24 hrs')).toBeVisible();
|
||||
39 |
|
||||
40 | // Upload mock Aadhaar & PAN files
|
||||
41 | await page.setInputFiles('input#aadhar-upload', {
|
||||
42 | name: 'aadhar_card.pdf',
|
||||
43 | mimeType: 'application/pdf',
|
||||
44 | buffer: Buffer.from('mock aadhaar pdf'),
|
||||
45 | });
|
||||
46 | await expect(page.getByText('Selected: aadhar_card.pdf')).toBeVisible();
|
||||
47 |
|
||||
48 | await page.setInputFiles('input#pan-upload', {
|
||||
49 | name: 'pan_card.pdf',
|
||||
50 | mimeType: 'application/pdf',
|
||||
51 | buffer: Buffer.from('mock pan pdf'),
|
||||
52 | });
|
||||
53 | await expect(page.getByText('Selected: pan_card.pdf')).toBeVisible();
|
||||
54 |
|
||||
55 | await page.locator('button').filter({ hasText: 'Next Step: Store & Location' }).click();
|
||||
56 |
|
||||
57 | // 5. Step 3: Store & Pickup Location Setup
|
||||
58 | await expect(page.getByText('Step 3 of 3: Store & Pickup Location')).toBeVisible();
|
||||
59 | await page.locator('input#store-name').clear();
|
||||
60 | await page.locator('input#store-name').fill('Teak Wood Craft Store');
|
||||
61 | await page.locator('textarea#store-bio').clear();
|
||||
62 | await page.locator('textarea#store-bio').fill('Teak wood hand-carved home decor elements.');
|
||||
63 |
|
||||
64 | // Test interactive map coordinate pinning
|
||||
65 | await expect(page.getByText('Store Location on Map *')).toBeVisible();
|
||||
66 | const mapGridParent = page.locator('span').filter({ hasText: 'Click anywhere on the map grid to pin location' }).locator('..');
|
||||
67 | await mapGridParent.click({ position: { x: 100, y: 80 } });
|
||||
68 | await expect(page.getByText('Coordinates: Lat')).toBeVisible();
|
||||
69 |
|
||||
70 | await expect(page.locator('input#location-text')).not.toHaveValue('');
|
||||
71 | await page.locator('input#loc-city').clear();
|
||||
72 | await page.locator('input#loc-city').fill('Bangalore');
|
||||
73 | await page.locator('input#loc-pincode').clear();
|
||||
74 | await page.locator('input#loc-pincode').fill('560001');
|
||||
75 | await page.locator('button').filter({ hasText: 'Submit Supplier Profile' }).click();
|
||||
76 |
|
||||
77 | // 6. Setup Welcome Tour Stepper
|
||||
78 | await expect(page.getByText('Supplier Account Under Review ⏳')).toBeVisible();
|
||||
79 | await page.locator('button').filter({ hasText: 'Start Guided Tour 🎬' }).click();
|
||||
80 | await expect(page.getByText('📦 Products & Inventory Management')).toBeVisible();
|
||||
81 | await page.locator('button').filter({ hasText: 'Next: Order Management' }).click();
|
||||
82 | await expect(page.getByText('🚚 Order Acceptance Control')).toBeVisible();
|
||||
83 | await page.locator('button').filter({ hasText: 'Next: Barcode Generation' }).click();
|
||||
84 | await expect(page.getByText('🏷️ Barcode Identification & Tracking')).toBeVisible();
|
||||
85 | await page.locator('button').filter({ hasText: 'Next: Earnings & Wallet' }).click();
|
||||
86 | await expect(page.getByText('💼 Wallet & Payout Withdrawals')).toBeVisible();
|
||||
87 | await page.locator('button').filter({ hasText: 'Explore Dashboard 🚀' }).click();
|
||||
88 |
|
||||
89 | // 7. Check Active Dashboard Tab
|
||||
90 | await expect(page.getByText('Performance Analytics')).toBeVisible();
|
||||
91 |
|
||||
92 | // 8. Manage Products tab & Bulk template download
|
||||
93 | await page.locator('button, a').filter({ hasText: 'Manage Products' }).click();
|
||||
94 | await expect(page.getByText('Active Inventory')).toBeVisible();
|
||||
95 |
|
||||
96 | const downloadPromise = page.waitForEvent('download');
|
||||
97 | await page.locator('button, a').filter({ hasText: 'Download Sample Excel Template' }).click();
|
||||
98 | const download = await downloadPromise;
|
||||
99 | expect(download.suggestedFilename()).toBeDefined();
|
||||
100 |
|
||||
101 | // 9. Orders Acceptance Flow
|
||||
102 | await page.locator('button, a').filter({ hasText: 'Orders & Transit' }).click();
|
||||
103 | await expect(page.getByText('Active Orders & Payout status')).toBeVisible();
|
||||
104 | await expect(page.getByText('Pending Acceptance')).toBeVisible();
|
||||
105 | await page.locator('button').filter({ hasText: 'Accept' }).first().click();
|
||||
106 | await expect(page.getByText('Ready to Ship')).toBeVisible();
|
||||
107 |
|
||||
108 | // 10. Barcode Generator
|
||||
109 | await page.locator('button, a').filter({ hasText: 'Barcode Generator' }).click();
|
||||
110 | await expect(page.getByText('Product Barcode Generator')).toBeVisible();
|
||||
111 | await page.locator('select#barcode-product').selectOption({ index: 1 });
|
||||
112 | await page.locator('button').filter({ hasText: 'Generate Barcode Graphic' }).click();
|
||||
113 | await expect(page.getByText('Generated Barcode Preview')).toBeVisible();
|
||||
114 | await expect(page.getByText('Print Label')).toBeVisible();
|
||||
115 | await expect(page.getByText('Download SVG')).toBeVisible();
|
||||
116 | });
|
||||
117 |
|
||||
118 | test('enforces security constraints and validation rules (negative test cases)', async ({ page }) => {
|
||||
119 | // 1. Visit Home and verify dashboard components are not in DOM (route guarding)
|
||||
120 | await page.goto('/');
|
||||
121 | await expect(page.locator('.dashboard-container')).toBeHidden();
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
Loading…
Reference in a new issue