234 lines
11 KiB
Markdown
234 lines
11 KiB
Markdown
|
|
# 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 |
|
||
|
|
```
|