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 + + + + +
+ + + \ 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