diff --git a/package-lock.json b/package-lock.json
index 65c4b20..7d690f3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -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",
diff --git a/package.json b/package.json
index 5a7566a..7d9e6eb 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/playwright-report/data/1ca060e370aad7bc0e52f15b13ddb034abf38a0e.png b/playwright-report/data/1ca060e370aad7bc0e52f15b13ddb034abf38a0e.png
new file mode 100644
index 0000000..698e25d
Binary files /dev/null and b/playwright-report/data/1ca060e370aad7bc0e52f15b13ddb034abf38a0e.png differ
diff --git a/playwright-report/data/3e9b6efe236ab01e61071710192aac95626226d6.md b/playwright-report/data/3e9b6efe236ab01e61071710192aac95626226d6.md
new file mode 100644
index 0000000..e221b2e
--- /dev/null
+++ b/playwright-report/data/3e9b6efe236ab01e61071710192aac95626226d6.md
@@ -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();
+```
\ No newline at end of file
diff --git a/playwright-report/data/bd62accf4ab42e1b0f900b0ec24e0d66aed608d2.webm b/playwright-report/data/bd62accf4ab42e1b0f900b0ec24e0d66aed608d2.webm
new file mode 100644
index 0000000..e19393f
Binary files /dev/null and b/playwright-report/data/bd62accf4ab42e1b0f900b0ec24e0d66aed608d2.webm differ
diff --git a/playwright-report/data/c7afdb8f544d92e6a92ae8173ff4a2f1063f1b50.webm b/playwright-report/data/c7afdb8f544d92e6a92ae8173ff4a2f1063f1b50.webm
new file mode 100644
index 0000000..268e083
Binary files /dev/null and b/playwright-report/data/c7afdb8f544d92e6a92ae8173ff4a2f1063f1b50.webm differ
diff --git a/playwright-report/data/eb141894a40c3f575f0c6be15623e32583ee15c4.png b/playwright-report/data/eb141894a40c3f575f0c6be15623e32583ee15c4.png
new file mode 100644
index 0000000..fe7367e
Binary files /dev/null and b/playwright-report/data/eb141894a40c3f575f0c6be15623e32583ee15c4.png differ
diff --git a/playwright-report/data/ec8f708b217156b22e7ad5ff6020879501e7f6fe.md b/playwright-report/data/ec8f708b217156b22e7ad5ff6020879501e7f6fe.md
new file mode 100644
index 0000000..a197f86
--- /dev/null
+++ b/playwright-report/data/ec8f708b217156b22e7ad5ff6020879501e7f6fe.md
@@ -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 |
+```
\ No newline at end of file
diff --git a/playwright-report/index.html b/playwright-report/index.html
new file mode 100644
index 0000000..325243c
--- /dev/null
+++ b/playwright-report/index.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+ Playwright Test Report
+
+
+
+
+
+
+
+data:application/zip;base64,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
\ No newline at end of file
diff --git a/playwright.config.ts b/playwright.config.ts
new file mode 100644
index 0000000..ae2f27e
--- /dev/null
+++ b/playwright.config.ts
@@ -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'] },
+ },
+ ],
+});
diff --git a/playwright/e2e/onboarding.spec.ts b/playwright/e2e/onboarding.spec.ts
new file mode 100644
index 0000000..3c8b1f6
--- /dev/null
+++ b/playwright/e2e/onboarding.spec.ts
@@ -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();
+ });
+});
diff --git a/test-results/.last-run.json b/test-results/.last-run.json
new file mode 100644
index 0000000..d5dc2ea
--- /dev/null
+++ b/test-results/.last-run.json
@@ -0,0 +1,7 @@
+{
+ "status": "failed",
+ "failedTests": [
+ "fe534f0825407f213faa-eae28aa470cee3dd351a",
+ "fe534f0825407f213faa-178b974dec5b4368ccdd"
+ ]
+}
\ No newline at end of file
diff --git a/test-results/onboarding-Supplier-Portal-e01b6--rules-negative-test-cases--chromium/error-context.md b/test-results/onboarding-Supplier-Portal-e01b6--rules-negative-test-cases--chromium/error-context.md
new file mode 100644
index 0000000..a197f86
--- /dev/null
+++ b/test-results/onboarding-Supplier-Portal-e01b6--rules-negative-test-cases--chromium/error-context.md
@@ -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 |
+```
\ No newline at end of file
diff --git a/test-results/onboarding-Supplier-Portal-e01b6--rules-negative-test-cases--chromium/test-failed-1.png b/test-results/onboarding-Supplier-Portal-e01b6--rules-negative-test-cases--chromium/test-failed-1.png
new file mode 100644
index 0000000..fe7367e
Binary files /dev/null and b/test-results/onboarding-Supplier-Portal-e01b6--rules-negative-test-cases--chromium/test-failed-1.png differ
diff --git a/test-results/onboarding-Supplier-Portal-e01b6--rules-negative-test-cases--chromium/video.webm b/test-results/onboarding-Supplier-Portal-e01b6--rules-negative-test-cases--chromium/video.webm
new file mode 100644
index 0000000..e19393f
Binary files /dev/null and b/test-results/onboarding-Supplier-Portal-e01b6--rules-negative-test-cases--chromium/video.webm differ
diff --git a/test-results/onboarding-Supplier-Portal-e143f-ance-and-barcode-generation-chromium/error-context.md b/test-results/onboarding-Supplier-Portal-e143f-ance-and-barcode-generation-chromium/error-context.md
new file mode 100644
index 0000000..e221b2e
--- /dev/null
+++ b/test-results/onboarding-Supplier-Portal-e143f-ance-and-barcode-generation-chromium/error-context.md
@@ -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();
+```
\ No newline at end of file
diff --git a/test-results/onboarding-Supplier-Portal-e143f-ance-and-barcode-generation-chromium/test-failed-1.png b/test-results/onboarding-Supplier-Portal-e143f-ance-and-barcode-generation-chromium/test-failed-1.png
new file mode 100644
index 0000000..698e25d
Binary files /dev/null and b/test-results/onboarding-Supplier-Portal-e143f-ance-and-barcode-generation-chromium/test-failed-1.png differ
diff --git a/test-results/onboarding-Supplier-Portal-e143f-ance-and-barcode-generation-chromium/video.webm b/test-results/onboarding-Supplier-Portal-e143f-ance-and-barcode-generation-chromium/video.webm
new file mode 100644
index 0000000..268e083
Binary files /dev/null and b/test-results/onboarding-Supplier-Portal-e143f-ance-and-barcode-generation-chromium/video.webm differ