diff --git a/.gitignore b/.gitignore index fd88a74..4c0406b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ storybook-static coverage test-results playwright-report +playwright-reports .vitest diff --git a/playwright-reports/html/data/06e44c615840f735bba73424e927e0954d8ffe9f.md b/playwright-reports/html/data/06e44c615840f735bba73424e927e0954d8ffe9f.md deleted file mode 100644 index 847fb91..0000000 --- a/playwright-reports/html/data/06e44c615840f735bba73424e927e0954d8ffe9f.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Rate Limiting >> AI endpoints rate limit after daily quota exceeded -- Location: tests/e2e/security.spec.ts:108:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 65 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/073b3a84e67a48c170161e0a7ee0aae27a123df8.md b/playwright-reports/html/data/073b3a84e67a48c170161e0a7ee0aae27a123df8.md deleted file mode 100644 index 8f865c6..0000000 --- a/playwright-reports/html/data/073b3a84e67a48c170161e0a7ee0aae27a123df8.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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: security.spec.ts >> Security - CORS Headers >> CORS headers are present on API responses -- Location: tests/e2e/security.spec.ts:191:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/nonexistent-route - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/0e5a355480ad6428e83382d4468d07963b681082.md b/playwright-reports/html/data/0e5a355480ad6428e83382d4468d07963b681082.md deleted file mode 100644 index f2e3b69..0000000 --- a/playwright-reports/html/data/0e5a355480ad6428e83382d4468d07963b681082.md +++ /dev/null @@ -1,23 +0,0 @@ -# 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: api.spec.ts >> Gateway API >> Gateway routes /api/ai/* to users service -- Location: tests/e2e/api.spec.ts:240:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - Authorization: Bearer dummy - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/11ca00cf695dab257487e5bebac2e2c7a3169933.md b/playwright-reports/html/data/11ca00cf695dab257487e5bebac2e2c7a3169933.md deleted file mode 100644 index 4ccea51..0000000 --- a/playwright-reports/html/data/11ca00cf695dab257487e5bebac2e2c7a3169933.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Input Validation >> XSS attempt in login email is handled safely -- Location: tests/e2e/security.spec.ts:163:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 75 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/12598e1fbd51e19c41565b90e5b1515b3b5a9dcc.md b/playwright-reports/html/data/12598e1fbd51e19c41565b90e5b1515b3b5a9dcc.md deleted file mode 100644 index 7238c0e..0000000 --- a/playwright-reports/html/data/12598e1fbd51e19c41565b90e5b1515b3b5a9dcc.md +++ /dev/null @@ -1,23 +0,0 @@ -# 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: security.spec.ts >> Security - Token Handling >> Token with invalid signature is rejected -- Location: tests/e2e/security.spec.ts:274:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxOTk5OTk5OTk5OX0.wronngsignature - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/1800f3ea9cfe96cf643841bb85b0e393d9bc4a72.md b/playwright-reports/html/data/1800f3ea9cfe96cf643841bb85b0e393d9bc4a72.md deleted file mode 100644 index 23c6299..0000000 --- a/playwright-reports/html/data/1800f3ea9cfe96cf643841bb85b0e393d9bc4a72.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> AI API Endpoints >> AI Usage Endpoint >> GET /ai/usage returns usage stats for company -- Location: tests/e2e/api.spec.ts:144:5 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 65 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/1fcbd74fe24dee6388b780541426c639edc14c88.webm b/playwright-reports/html/data/1fcbd74fe24dee6388b780541426c639edc14c88.webm deleted file mode 100644 index 49e0345..0000000 Binary files a/playwright-reports/html/data/1fcbd74fe24dee6388b780541426c639edc14c88.webm and /dev/null differ diff --git a/playwright-reports/html/data/203efa55ac7aeeb02acb4df84d4a8a641f0b23aa.md b/playwright-reports/html/data/203efa55ac7aeeb02acb4df84d4a8a641f0b23aa.md deleted file mode 100644 index 7b0f05c..0000000 --- a/playwright-reports/html/data/203efa55ac7aeeb02acb4df84d4a8a641f0b23aa.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Authorization >> User cannot access admin endpoints with regular user token -- Location: tests/e2e/security.spec.ts:200:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 61 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/216d7d99c1dba7a3f5a89ff2c97220a1ca0a513e.md b/playwright-reports/html/data/216d7d99c1dba7a3f5a89ff2c97220a1ca0a513e.md deleted file mode 100644 index 2591469..0000000 --- a/playwright-reports/html/data/216d7d99c1dba7a3f5a89ff2c97220a1ca0a513e.md +++ /dev/null @@ -1,23 +0,0 @@ -# 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: security.spec.ts >> Security - Token Handling >> Token with invalid signature is rejected -- Location: tests/e2e/security.spec.ts:274:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxOTk5OTk5OTk5OX0.wronngsignature - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/24d5a86915194471726ebb9579c0ce4bdf75f353.webm b/playwright-reports/html/data/24d5a86915194471726ebb9579c0ce4bdf75f353.webm deleted file mode 100644 index 28169d5..0000000 Binary files a/playwright-reports/html/data/24d5a86915194471726ebb9579c0ce4bdf75f353.webm and /dev/null differ diff --git a/playwright-reports/html/data/27a75f235c7c6cff89e69a1724e1a0d6a4032019.md b/playwright-reports/html/data/27a75f235c7c6cff89e69a1724e1a0d6a4032019.md deleted file mode 100644 index ea20617..0000000 --- a/playwright-reports/html/data/27a75f235c7c6cff89e69a1724e1a0d6a4032019.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> Protected endpoint rejects request without token -- Location: tests/e2e/security.spec.ts:42:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/2bb9e70b5df9cda14db7bccb36404f908bdfe2d7.md b/playwright-reports/html/data/2bb9e70b5df9cda14db7bccb36404f908bdfe2d7.md deleted file mode 100644 index 2678973..0000000 --- a/playwright-reports/html/data/2bb9e70b5df9cda14db7bccb36404f908bdfe2d7.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Rate Limiting >> Login rate limits after multiple failed attempts -- Location: tests/e2e/security.spec.ts:73:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 76 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/30dc3bdd2b699405346a2b8d6d0953ddfed88280.md b/playwright-reports/html/data/30dc3bdd2b699405346a2b8d6d0953ddfed88280.md deleted file mode 100644 index 72858cb..0000000 --- a/playwright-reports/html/data/30dc3bdd2b699405346a2b8d6d0953ddfed88280.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Response Headers >> API does not leak sensitive information in error responses -- Location: tests/e2e/security.spec.ts:230:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 55 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/349260bce55b970764d34c52e3aadd9ed06e6bb4.md b/playwright-reports/html/data/349260bce55b970764d34c52e3aadd9ed06e6bb4.md deleted file mode 100644 index 7ec8e33..0000000 --- a/playwright-reports/html/data/349260bce55b970764d34c52e3aadd9ed06e6bb4.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> JWT token is not returned for invalid credentials -- Location: tests/e2e/security.spec.ts:6:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 62 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/3bafe09d2a425af60248fc3d45d05a5551e1fa10.md b/playwright-reports/html/data/3bafe09d2a425af60248fc3d45d05a5551e1fa10.md deleted file mode 100644 index 97aebaf..0000000 --- a/playwright-reports/html/data/3bafe09d2a425af60248fc3d45d05a5551e1fa10.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> Auth API Endpoints >> POST /auth/login returns token for valid credentials -- Location: tests/e2e/api.spec.ts:182:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 61 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/3fff103345fe4e6ae2d7112dded59a7464f68f4a.md b/playwright-reports/html/data/3fff103345fe4e6ae2d7112dded59a7464f68f4a.md deleted file mode 100644 index 1d51598..0000000 --- a/playwright-reports/html/data/3fff103345fe4e6ae2d7112dded59a7464f68f4a.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> AI API Endpoints >> Company AI - Generate Job Field >> POST /ai/generate-job-field returns generated content -- Location: tests/e2e/api.spec.ts:41:5 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 65 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/4329687e0f6e6c05cb74888c9498fa97da221e19.md b/playwright-reports/html/data/4329687e0f6e6c05cb74888c9498fa97da221e19.md deleted file mode 100644 index a93e890..0000000 --- a/playwright-reports/html/data/4329687e0f6e6c05cb74888c9498fa97da221e19.md +++ /dev/null @@ -1,23 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> Protected endpoint rejects request with malformed token -- Location: tests/e2e/security.spec.ts:49:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - Authorization: Bearer invalid.malformed.token - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/433304ff69d838aa70f6274e6bc4b9f4ac95ee02.md b/playwright-reports/html/data/433304ff69d838aa70f6274e6bc4b9f4ac95ee02.md deleted file mode 100644 index 3ceff5a..0000000 --- a/playwright-reports/html/data/433304ff69d838aa70f6274e6bc4b9f4ac95ee02.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> Login without email returns proper error -- Location: tests/e2e/security.spec.ts:20:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 27 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/433ec107ae011c65a5d8e0b230619139e033c5ad.md b/playwright-reports/html/data/433ec107ae011c65a5d8e0b230619139e033c5ad.md deleted file mode 100644 index 7a1cc3c..0000000 --- a/playwright-reports/html/data/433ec107ae011c65a5d8e0b230619139e033c5ad.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Rate Limiting >> Login rate limits after multiple failed attempts -- Location: tests/e2e/security.spec.ts:73:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 76 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/454d893103d2da92da2414583ee4d96021d18eae.md b/playwright-reports/html/data/454d893103d2da92da2414583ee4d96021d18eae.md deleted file mode 100644 index 613e69f..0000000 --- a/playwright-reports/html/data/454d893103d2da92da2414583ee4d96021d18eae.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> Login without password returns proper error -- Location: tests/e2e/security.spec.ts:31:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 28 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/4bb4548304ad4b7e3bc6c05d099ee4faf1b4ecd2.md b/playwright-reports/html/data/4bb4548304ad4b7e3bc6c05d099ee4faf1b4ecd2.md deleted file mode 100644 index 0ac57bf..0000000 --- a/playwright-reports/html/data/4bb4548304ad4b7e3bc6c05d099ee4faf1b4ecd2.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Input Validation >> XSS attempt in login email is handled safely -- Location: tests/e2e/security.spec.ts:163:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 75 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/5a0d4d04689ba72eabcd73cfe2e48aeef318df71.md b/playwright-reports/html/data/5a0d4d04689ba72eabcd73cfe2e48aeef318df71.md deleted file mode 100644 index 8147c1a..0000000 --- a/playwright-reports/html/data/5a0d4d04689ba72eabcd73cfe2e48aeef318df71.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Response Headers >> API does not leak sensitive information in error responses -- Location: tests/e2e/security.spec.ts:230:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 55 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/5cc9d9d62cbff7d489c9b1c25cfeae1d4c4d3abf.md b/playwright-reports/html/data/5cc9d9d62cbff7d489c9b1c25cfeae1d4c4d3abf.md deleted file mode 100644 index 3b97740..0000000 --- a/playwright-reports/html/data/5cc9d9d62cbff7d489c9b1c25cfeae1d4c4d3abf.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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: security.spec.ts >> Security - CORS Headers >> CORS headers are present on API responses -- Location: tests/e2e/security.spec.ts:191:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/nonexistent-route - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/61580f75bfe0e42a119ef7795079c96b58160034.md b/playwright-reports/html/data/61580f75bfe0e42a119ef7795079c96b58160034.md deleted file mode 100644 index 60e7948..0000000 --- a/playwright-reports/html/data/61580f75bfe0e42a119ef7795079c96b58160034.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Authorization >> User cannot access admin endpoints with regular user token -- Location: tests/e2e/security.spec.ts:200:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 61 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/6b5a98b22612f377b15ec8fa3a0877a588b8e7d0.md b/playwright-reports/html/data/6b5a98b22612f377b15ec8fa3a0877a588b8e7d0.md deleted file mode 100644 index ce74ec6..0000000 --- a/playwright-reports/html/data/6b5a98b22612f377b15ec8fa3a0877a588b8e7d0.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> Login without password returns proper error -- Location: tests/e2e/security.spec.ts:31:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 28 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/70f0445f0ccf67a917ab8b0a36f15e738ee88abe.md b/playwright-reports/html/data/70f0445f0ccf67a917ab8b0a36f15e738ee88abe.md deleted file mode 100644 index e6f673d..0000000 --- a/playwright-reports/html/data/70f0445f0ccf67a917ab8b0a36f15e738ee88abe.md +++ /dev/null @@ -1,23 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> Protected endpoint rejects request with malformed token -- Location: tests/e2e/security.spec.ts:49:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - Authorization: Bearer invalid.malformed.token - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/76dd4ddcee69aa44313eaab9e8507d1aa33ef783.md b/playwright-reports/html/data/76dd4ddcee69aa44313eaab9e8507d1aa33ef783.md deleted file mode 100644 index a667bde..0000000 --- a/playwright-reports/html/data/76dd4ddcee69aa44313eaab9e8507d1aa33ef783.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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: api.spec.ts >> Gateway API >> Gateway returns 404 for unknown routes -- Location: tests/e2e/api.spec.ts:255:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/nonexistent-route - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/7dc3bad8e0544ace88c7d46975f6ef75f42313e6.md b/playwright-reports/html/data/7dc3bad8e0544ace88c7d46975f6ef75f42313e6.md deleted file mode 100644 index ea60b80..0000000 --- a/playwright-reports/html/data/7dc3bad8e0544ace88c7d46975f6ef75f42313e6.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> Auth API Endpoints >> POST /auth/login returns 401 for invalid credentials -- Location: tests/e2e/api.spec.ts:202:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 58 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/83970aa84141905481132a9341730c3d93412bfd.md b/playwright-reports/html/data/83970aa84141905481132a9341730c3d93412bfd.md deleted file mode 100644 index f8c43aa..0000000 --- a/playwright-reports/html/data/83970aa84141905481132a9341730c3d93412bfd.md +++ /dev/null @@ -1,433 +0,0 @@ -# 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: accessibility.spec.ts >> Public Frontend Accessibility >> homepage should have no accessibility violations -- Location: tests/e2e/accessibility.spec.ts:5:3 - -# Error details - -``` -Error: expect(received).toEqual(expected) // deep equality - -- Expected - 1 -+ Received + 58 - -- Array [] -+ Array [ -+ Object { -+ "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds", -+ "help": "Elements must meet minimum color contrast ratio thresholds", -+ "helpUrl": "https://dequeuniversity.com/rules/axe/4.11/color-contrast?application=playwright", -+ "id": "color-contrast", -+ "impact": "serious", -+ "nodes": Array [ -+ Object { -+ "all": Array [], -+ "any": Array [ -+ Object { -+ "data": Object { -+ "bgColor": "#fd6116", -+ "contrastRatio": 3.04, -+ "expectedContrastRatio": "4.5:1", -+ "fgColor": "#ffffff", -+ "fontSize": "10.5pt (14px)", -+ "fontWeight": "normal", -+ "messageKey": null, -+ }, -+ "id": "color-contrast", -+ "impact": "serious", -+ "message": "Element has insufficient color contrast of 3.04 (foreground color: #ffffff, background color: #fd6116, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1", -+ "relatedNodes": Array [ -+ Object { -+ "html": "", -+ "target": Array [ -+ "button[data-hk=\"00000001000001000000000a1080\"]", -+ ], -+ }, -+ ], -+ }, -+ ], -+ "failureSummary": "Fix any of the following: -+ Element has insufficient color contrast of 3.04 (foreground color: #ffffff, background color: #fd6116, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1", -+ "html": "", -+ "impact": "serious", -+ "none": Array [], -+ "target": Array [ -+ "button[data-hk=\"00000001000001000000000a1080\"]", -+ ], -+ }, -+ ], -+ "tags": Array [ -+ "cat.color", -+ "wcag2aa", -+ "wcag143", -+ "TTv5", -+ "TT13.c", -+ "EN-301-549", -+ "EN-9.1.4.3", -+ "ACT", -+ "RGAAv4", -+ "RGAA-3.2.1", -+ ], -+ }, -+ ] -``` - -# Page snapshot - -```yaml -- generic [ref=e2]: - - main [ref=e3]: - - generic: - - generic: - - generic: - - img - - generic: - - img - - generic: - - img - - generic: - - img - - generic: - - img - - generic [ref=e4]: - - navigation [ref=e6]: - - link "Nxtgauge home" [ref=e7] [cursor=pointer]: - - /url: / - - img "NXTGAUGE" [ref=e8] - - button "Open navigation menu" [ref=e9]: - - generic [ref=e10]: Menu - - generic [ref=e16]: - - generic [ref=e17]: - - heading "Trusted hiring and opportunities, verified." [level=1] [ref=e18] - - paragraph [ref=e19]: Hire trusted professionals, post verified jobs, and apply faster in one platform. - - generic [ref=e20]: - - link "Get Started" [ref=e21] [cursor=pointer]: - - /url: /signup/customer - - link "How It Works" [ref=e22] [cursor=pointer]: - - /url: /#how-it-works - - paragraph [ref=e23]: Most profile and listing reviews are completed within 24-48 hours. - - region "Nxtgauge opportunity graph preview" [ref=e25]: - - generic [ref=e26]: - - generic [ref=e27]: - - img [ref=e28] - - generic [ref=e45]: Developer - - generic [ref=e46]: Tutor - - generic [ref=e47]: Photographer - - generic [ref=e48]: - - generic [ref=e49]: - - generic [ref=e50]: ✓ - - generic [ref=e51]: Everything from Nxtgauge - - paragraph [ref=e52]: Opportunity Workspace - - generic [ref=e53]: - - strong [ref=e54]: Verified Profiles - - generic [ref=e55]: Trust layer ready - - generic [ref=e56]: - - strong [ref=e57]: Matched Opportunities - - generic [ref=e58]: Priority queue - - generic [ref=e59]: - - strong [ref=e60]: Responses - - generic [ref=e61]: Tracked in one flow - - generic [ref=e62]: - - strong [ref=e63]: Updates - - generic [ref=e64]: Live status signals - - paragraph [ref=e66]: Opportunities start scattered - - generic [ref=e68]: - - generic [ref=e70]: - - heading "Who Nxtgauge is for" [level=2] [ref=e71] - - paragraph [ref=e72]: Start with one account, then activate the journey you need. - - generic [ref=e73]: - - article [ref=e74]: - - img "Post a Job" [ref=e76] - - generic [ref=e78]: - - generic [ref=e79]: - - img [ref=e81] - - generic [ref=e85]: - - img [ref=e86] - - text: Company - - heading "Post a Job" [level=3] [ref=e89] - - paragraph [ref=e90]: Create verified job openings and find the right talent faster. - - link "Explore Company" [ref=e91] [cursor=pointer]: - - /url: /roles/company - - article [ref=e92]: - - img "Apply for Jobs" [ref=e94] - - generic [ref=e96]: - - generic [ref=e97]: - - img [ref=e99] - - generic [ref=e102]: - - img [ref=e103] - - text: Job Seeker - - heading "Apply for Jobs" [level=3] [ref=e106] - - paragraph [ref=e107]: Build your profile and apply to approved opportunities quickly. - - link "Explore Job Seeker" [ref=e108] [cursor=pointer]: - - /url: /roles/job-seeker - - article [ref=e109]: - - img "Hire a Professional" [ref=e111] - - generic [ref=e113]: - - generic [ref=e114]: - - img [ref=e116] - - generic [ref=e121]: - - img [ref=e122] - - text: Customer - - heading "Hire a Professional" [level=3] [ref=e125] - - paragraph [ref=e126]: Post your requirement and discover trusted specialists. - - link "Explore Customer" [ref=e127] [cursor=pointer]: - - /url: /roles/customer - - article [ref=e128]: - - img "Join as Professional" [ref=e130] - - generic [ref=e132]: - - generic [ref=e133]: - - img [ref=e135] - - generic [ref=e139]: - - img [ref=e140] - - text: Professional - - heading "Join as Professional" [level=3] [ref=e143] - - paragraph [ref=e144]: Create a trusted profile and grow through verified demand. - - link "Explore Professionals" [ref=e145] [cursor=pointer]: - - /url: /professionals - - generic [ref=e147]: - - generic [ref=e148]: - - heading "Explore professional categories" [level=2] [ref=e150] - - link "View all professionals" [ref=e151] [cursor=pointer]: - - /url: /professionals - - generic [ref=e152]: - - button "All" [ref=e153] [cursor=pointer] - - button "Creative" [ref=e154] [cursor=pointer] - - button "Tech" [ref=e155] [cursor=pointer] - - button "Education" [ref=e156] [cursor=pointer] - - button "Wellness" [ref=e157] [cursor=pointer] - - button "Events" [ref=e158] [cursor=pointer] - - button "Marketing" [ref=e159] [cursor=pointer] - - generic [ref=e160]: - - article [ref=e161]: - - img "Developer" [ref=e163] - - generic [ref=e165]: - - generic [ref=e167]: tech - - heading "Developer" [level=3] [ref=e168] - - paragraph [ref=e169]: Join Nxtgauge as a Developer and connect with customers and companies through a trust-first workflow. - - generic [ref=e170]: - - link "Explore Developer" [ref=e171] [cursor=pointer]: - - /url: /professionals/developer - - link "Join Developer" [ref=e172] [cursor=pointer]: - - /url: /signup?intent=professional&role=DEVELOPER - - article [ref=e173]: - - img "Photographer" [ref=e175] - - generic [ref=e177]: - - generic [ref=e179]: creative - - heading "Photographer" [level=3] [ref=e180] - - paragraph [ref=e181]: Join as a Photographer and receive opportunities where trust and profile quality matter. - - generic [ref=e182]: - - link "Explore Photographer" [ref=e183] [cursor=pointer]: - - /url: /professionals/photographer - - link "Join Photographer" [ref=e184] [cursor=pointer]: - - /url: /signup?intent=professional&role=PHOTOGRAPHER - - article [ref=e185]: - - img "Makeup Artist" [ref=e187] - - generic [ref=e189]: - - generic [ref=e191]: creative - - heading "Makeup Artist" [level=3] [ref=e192] - - paragraph [ref=e193]: Join Nxtgauge as a Makeup Artist and connect with verified requirements from customers and event planners. - - generic [ref=e194]: - - link "Explore Makeup Artist" [ref=e195] [cursor=pointer]: - - /url: /professionals/makeup-artist - - link "Join Makeup Artist" [ref=e196] [cursor=pointer]: - - /url: /signup?intent=professional&role=MAKEUP_ARTIST - - article [ref=e197]: - - img "Tutor" [ref=e199] - - generic [ref=e201]: - - generic [ref=e203]: education - - heading "Tutor" [level=3] [ref=e204] - - paragraph [ref=e205]: Join as a Tutor to receive verified educational requirements and present your profile with confidence. - - generic [ref=e206]: - - link "Explore Tutor" [ref=e207] [cursor=pointer]: - - /url: /professionals/tutor - - link "Join Tutor" [ref=e208] [cursor=pointer]: - - /url: /signup?intent=professional&role=TUTOR - - article [ref=e209]: - - img "Video Editor" [ref=e211] - - generic [ref=e213]: - - generic [ref=e215]: creative - - heading "Video Editor" [level=3] [ref=e216] - - paragraph [ref=e217]: Join as a Video Editor and connect with customers and companies through a review-based trust model. - - generic [ref=e218]: - - link "Explore Video Editor" [ref=e219] [cursor=pointer]: - - /url: /professionals/video-editor - - link "Join Video Editor" [ref=e220] [cursor=pointer]: - - /url: /signup?intent=professional&role=VIDEO_EDITOR - - article [ref=e221]: - - img "Graphic Designer" [ref=e223] - - generic [ref=e225]: - - generic [ref=e227]: creative - - heading "Graphic Designer" [level=3] [ref=e228] - - paragraph [ref=e229]: Join as a Graphic Designer to connect with verified requirements and role-relevant opportunities. - - generic [ref=e230]: - - link "Explore Graphic Designer" [ref=e231] [cursor=pointer]: - - /url: /professionals/graphic-designer - - link "Join Graphic Designer" [ref=e232] [cursor=pointer]: - - /url: /signup?intent=professional&role=GRAPHIC_DESIGNER - - article [ref=e233]: - - img "Social Media Manager" [ref=e235] - - generic [ref=e237]: - - generic [ref=e239]: marketing - - heading "Social Media Manager" [level=3] [ref=e240] - - paragraph [ref=e241]: Join as a Social Media Manager and grow with trusted customer and company requirements. - - generic [ref=e242]: - - link "Explore Social Media Manager" [ref=e243] [cursor=pointer]: - - /url: /professionals/social-media-manager - - link "Join Social Media Manager" [ref=e244] [cursor=pointer]: - - /url: /signup?intent=professional&role=SOCIAL_MEDIA_MANAGER - - article [ref=e245]: - - img "Fitness Trainer" [ref=e247] - - generic [ref=e249]: - - generic [ref=e251]: wellness - - heading "Fitness Trainer" [level=3] [ref=e252] - - paragraph [ref=e253]: Join as a Fitness Trainer and grow through profile-led visibility in a trust-first marketplace. - - generic [ref=e254]: - - link "Explore Fitness Trainer" [ref=e255] [cursor=pointer]: - - /url: /professionals/fitness-trainer - - link "Join Fitness Trainer" [ref=e256] [cursor=pointer]: - - /url: /signup?intent=professional&role=FITNESS_TRAINER - - generic [ref=e258]: - - generic [ref=e259]: - - paragraph [ref=e260]: Why Nxtgauge - - heading "Trust, approvals, and better matching in one flow." [level=2] [ref=e261] - - generic [ref=e262]: - - article [ref=e263]: - - generic [ref=e265]: - - paragraph [ref=e266]: Capability 1 of 6 - - img [ref=e268] - - heading "Verified profiles & businesses" [level=3] [ref=e271] - - paragraph [ref=e272]: Identity and profile checks reduce fake submissions and spam. - - generic [ref=e274]: - - button "Show Verified profiles & businesses" [ref=e275]: - - img [ref=e276] - - button "Show Approval-based quality (24-48 hours)" [ref=e279]: - - img [ref=e280] - - button "Show Smart matching using tags/skills" [ref=e282]: - - img [ref=e283] - - button "Show Focused discovery with filters" [ref=e285]: - - img [ref=e286] - - button "Show Controlled contact visibility" [ref=e289]: - - img [ref=e290] - - button "Show Notifications & updates" [ref=e293]: - - img [ref=e294] - - generic [ref=e297]: - - button "Previous slide" [ref=e298]: ◀ - - button "Next slide" [ref=e299]: ▶ - - generic [ref=e301]: - - generic [ref=e302]: - - paragraph [ref=e303]: How it works - - heading "Clear journey, zero confusion" [level=2] [ref=e304] - - article [ref=e305]: - - generic [ref=e306]: - - img "Customers role" [ref=e308] - - generic [ref=e309]: - - paragraph [ref=e310]: Customers - - heading "Hire trusted professionals with less noise" [level=3] [ref=e311] - - paragraph [ref=e312]: Post requirements, pass review checks, and receive better-fit responses. - - generic [ref=e313]: - - paragraph [ref=e314]: Step Flow - - generic [ref=e315]: - - generic [ref=e316]: - - generic [ref=e317]: "1" - - generic [ref=e318]: - - heading "Create account" [level=4] [ref=e319] - - paragraph [ref=e320]: Sign up quickly with customer intent. - - generic [ref=e321]: - - generic [ref=e322]: "2" - - generic [ref=e323]: - - heading "Share requirement" [level=4] [ref=e324] - - paragraph [ref=e325]: Add budget, scope, and timing. - - generic [ref=e326]: - - generic [ref=e327]: "3" - - generic [ref=e328]: - - heading "Review and verify" [level=4] [ref=e329] - - paragraph [ref=e330]: Quality checks run before visibility. - - generic [ref=e331]: - - generic [ref=e332]: "4" - - generic [ref=e333]: - - heading "Track responses" [level=4] [ref=e334] - - paragraph [ref=e335]: Monitor replies and status updates. - - generic [ref=e337]: - - button "Previous role" [ref=e338]: ← - - button "Next role" [ref=e339]: → - - generic [ref=e346]: - - heading "Frequently asked questions" [level=2] [ref=e347] - - paragraph [ref=e348]: Quick answers before you create your account. - - generic [ref=e349]: - - article [ref=e350]: - - button "What is Nxtgauge? ⌄" [ref=e351] [cursor=pointer]: - - generic [ref=e352]: What is Nxtgauge? - - generic [ref=e353]: ⌄ - - paragraph [ref=e354]: Nxtgauge connects customers, professionals, companies, and job seekers in one trusted platform. - - article [ref=e355]: - - button "Who can join Nxtgauge? ⌄" [ref=e356] [cursor=pointer]: - - generic [ref=e357]: Who can join Nxtgauge? - - generic [ref=e358]: ⌄ - - article [ref=e359]: - - button "Why does Nxtgauge require verification? ⌄" [ref=e360] [cursor=pointer]: - - generic [ref=e361]: Why does Nxtgauge require verification? - - generic [ref=e362]: ⌄ - - article [ref=e363]: - - button "How long does account approval take? ⌄" [ref=e364] [cursor=pointer]: - - generic [ref=e365]: How long does account approval take? - - generic [ref=e366]: ⌄ - - article [ref=e367]: - - button "Do I need to choose my role during signup? ⌄" [ref=e368] [cursor=pointer]: - - generic [ref=e369]: Do I need to choose my role during signup? - - generic [ref=e370]: ⌄ - - generic [ref=e372]: - - generic [ref=e373]: - - paragraph [ref=e374]: Quick Actions - - heading "Start with one account. Choose your path after." [level=2] [ref=e375] - - paragraph [ref=e376]: Hire, post, apply, or join as a professional from stable role-specific pages. - - generic [ref=e377]: - - link "Get Started" [ref=e378] [cursor=pointer]: - - /url: /signup/customer - - link "Explore Professionals" [ref=e379] [cursor=pointer]: - - /url: /professionals - - link "Post a Job" [ref=e380] [cursor=pointer]: - - /url: /signup/company - - generic [ref=e382]: - - img "NXTGAUGE" [ref=e383] - - paragraph [ref=e384]: © 2026 Nxtgauge. All rights reserved. - - generic [ref=e385]: - - link "Professionals" [ref=e386] [cursor=pointer]: - - /url: /professionals - - link "Terms" [ref=e387] [cursor=pointer]: - - /url: /terms - - link "Privacy" [ref=e388] [cursor=pointer]: - - /url: /privacy - - link "Help Center" [ref=e389] [cursor=pointer]: - - /url: /help-center - - button "AI Assistant" [ref=e390] [cursor=pointer]: - - img [ref=e391] -``` - -# Test source - -```ts - 1 | import { test, expect } from "@playwright/test"; - 2 | import AxeBuilder from "@axe-core/playwright"; - 3 | - 4 | test.describe("Public Frontend Accessibility", () => { - 5 | test("homepage should have no accessibility violations", async ({ page }) => { - 6 | await page.goto("/"); - 7 | const results = await new AxeBuilder({ page }).analyze(); -> 8 | expect(results.violations).toEqual([]); - | ^ Error: expect(received).toEqual(expected) // deep equality - 9 | }); - 10 | - 11 | test("professionals listing page should be accessible", async ({ page }) => { - 12 | await page.goto("/professionals"); - 13 | const results = await new AxeBuilder({ page }).analyze(); - 14 | expect(results.violations).toEqual([]); - 15 | }); - 16 | }); - 17 | -``` \ No newline at end of file diff --git a/playwright-reports/html/data/8b0ba6a85698d00d26816f25ffc2016d673ee525.md b/playwright-reports/html/data/8b0ba6a85698d00d26816f25ffc2016d673ee525.md deleted file mode 100644 index ae74116..0000000 --- a/playwright-reports/html/data/8b0ba6a85698d00d26816f25ffc2016d673ee525.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> AI API Endpoints >> Company AI - Generate Job Field >> POST /ai/generate-job-field returns generated content -- Location: tests/e2e/api.spec.ts:41:5 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 65 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/93d0ae7967a30d546d9ef9a30b8d0717323a046b.md b/playwright-reports/html/data/93d0ae7967a30d546d9ef9a30b8d0717323a046b.md deleted file mode 100644 index b0dafbd..0000000 --- a/playwright-reports/html/data/93d0ae7967a30d546d9ef9a30b8d0717323a046b.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> Auth API Endpoints >> POST /auth/login rate limits after too many attempts -- Location: tests/e2e/api.spec.ts:215:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 65 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/944a1c0a201049549ee4d1defb34c36d1928b32e.md b/playwright-reports/html/data/944a1c0a201049549ee4d1defb34c36d1928b32e.md deleted file mode 100644 index a06887a..0000000 --- a/playwright-reports/html/data/944a1c0a201049549ee4d1defb34c36d1928b32e.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> Auth API Endpoints >> POST /auth/login returns token for valid credentials -- Location: tests/e2e/api.spec.ts:182:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 61 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/9b6f5347abef16bfd786a8e0614f285c3fa04de8.md b/playwright-reports/html/data/9b6f5347abef16bfd786a8e0614f285c3fa04de8.md deleted file mode 100644 index 3283245..0000000 --- a/playwright-reports/html/data/9b6f5347abef16bfd786a8e0614f285c3fa04de8.md +++ /dev/null @@ -1,23 +0,0 @@ -# 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: security.spec.ts >> Security - Token Handling >> Expired token is rejected -- Location: tests/e2e/security.spec.ts:262:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZXhwIjoxNjAwMDAwMDAwfQ.dummysignature - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/9c4de630bf48b7ece7d902d260a13d9db3ab9ff6.md b/playwright-reports/html/data/9c4de630bf48b7ece7d902d260a13d9db3ab9ff6.md deleted file mode 100644 index ab9d947..0000000 --- a/playwright-reports/html/data/9c4de630bf48b7ece7d902d260a13d9db3ab9ff6.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> AI API Endpoints >> Company AI - Generate Job Field >> POST /ai/generate-job-field rate limits after daily quota -- Location: tests/e2e/api.spec.ts:91:5 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 65 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/9d584d87128a07c6303af40cc2f3bb1e1be01989.md b/playwright-reports/html/data/9d584d87128a07c6303af40cc2f3bb1e1be01989.md deleted file mode 100644 index 4cb2d4d..0000000 --- a/playwright-reports/html/data/9d584d87128a07c6303af40cc2f3bb1e1be01989.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> JWT token is not returned for invalid credentials -- Location: tests/e2e/security.spec.ts:6:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 62 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/a019d07c0d87598717d7e27edf94737ba8c29e92.md b/playwright-reports/html/data/a019d07c0d87598717d7e27edf94737ba8c29e92.md deleted file mode 100644 index 4eeb42a..0000000 --- a/playwright-reports/html/data/a019d07c0d87598717d7e27edf94737ba8c29e92.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> AI API Endpoints >> AI Usage Endpoint >> GET /ai/usage returns usage stats for company -- Location: tests/e2e/api.spec.ts:144:5 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 65 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/a414f4178718fedadb56c714fa10233bbef71c51.png b/playwright-reports/html/data/a414f4178718fedadb56c714fa10233bbef71c51.png deleted file mode 100644 index 70459e9..0000000 Binary files a/playwright-reports/html/data/a414f4178718fedadb56c714fa10233bbef71c51.png and /dev/null differ diff --git a/playwright-reports/html/data/ad1c0adc3acdce2b311e2e7b21e93ad2233f1d94.md b/playwright-reports/html/data/ad1c0adc3acdce2b311e2e7b21e93ad2233f1d94.md deleted file mode 100644 index 68a7506..0000000 --- a/playwright-reports/html/data/ad1c0adc3acdce2b311e2e7b21e93ad2233f1d94.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Input Validation >> Very long input is handled without crash -- Location: tests/e2e/security.spec.ts:175:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 20038 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/ad5ed732feb82425e8df8acb2002071a774c0a11.md b/playwright-reports/html/data/ad5ed732feb82425e8df8acb2002071a774c0a11.md deleted file mode 100644 index 75551af..0000000 --- a/playwright-reports/html/data/ad5ed732feb82425e8df8acb2002071a774c0a11.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Input Validation >> SQL injection in login email is handled safely -- Location: tests/e2e/security.spec.ts:149:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 45 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/b4fa704806a42ae5abf765b58b255a2799760289.md b/playwright-reports/html/data/b4fa704806a42ae5abf765b58b255a2799760289.md deleted file mode 100644 index 498a1b2..0000000 --- a/playwright-reports/html/data/b4fa704806a42ae5abf765b58b255a2799760289.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> Auth API Endpoints >> POST /auth/login returns 401 for invalid credentials -- Location: tests/e2e/api.spec.ts:202:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 58 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/b983a333ccc99199f8dc359e5f80e14e55037db0.md b/playwright-reports/html/data/b983a333ccc99199f8dc359e5f80e14e55037db0.md deleted file mode 100644 index 138fc8c..0000000 --- a/playwright-reports/html/data/b983a333ccc99199f8dc359e5f80e14e55037db0.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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: api.spec.ts >> Gateway API >> Gateway returns 404 for unknown routes -- Location: tests/e2e/api.spec.ts:255:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/nonexistent-route - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/bf1a7466d9c8bde95533d72167cb2b1c6f95818e.md b/playwright-reports/html/data/bf1a7466d9c8bde95533d72167cb2b1c6f95818e.md deleted file mode 100644 index 6cb1ecc..0000000 --- a/playwright-reports/html/data/bf1a7466d9c8bde95533d72167cb2b1c6f95818e.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Input Validation >> Very long input is handled without crash -- Location: tests/e2e/security.spec.ts:175:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 20038 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/c24b23b0edd98a62d69666129d18c11f70d2a8ef.md b/playwright-reports/html/data/c24b23b0edd98a62d69666129d18c11f70d2a8ef.md deleted file mode 100644 index cf75666..0000000 --- a/playwright-reports/html/data/c24b23b0edd98a62d69666129d18c11f70d2a8ef.md +++ /dev/null @@ -1,23 +0,0 @@ -# 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: security.spec.ts >> Security - Token Handling >> Expired token is rejected -- Location: tests/e2e/security.spec.ts:262:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZXhwIjoxNjAwMDAwMDAwfQ.dummysignature - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/c61f20f315397a51e6d5ef9e7fe853d01daf5740.webm b/playwright-reports/html/data/c61f20f315397a51e6d5ef9e7fe853d01daf5740.webm deleted file mode 100644 index 2fb4edc..0000000 Binary files a/playwright-reports/html/data/c61f20f315397a51e6d5ef9e7fe853d01daf5740.webm and /dev/null differ diff --git a/playwright-reports/html/data/d1c052738280b98b85a822b1c24e89000f917263.md b/playwright-reports/html/data/d1c052738280b98b85a822b1c24e89000f917263.md deleted file mode 100644 index 1b8fd5f..0000000 --- a/playwright-reports/html/data/d1c052738280b98b85a822b1c24e89000f917263.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Rate Limiting >> AI endpoints rate limit after daily quota exceeded -- Location: tests/e2e/security.spec.ts:108:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 65 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/d3a2b8d6fd240feebf8dd288c7ab54480c4f6c3c.md b/playwright-reports/html/data/d3a2b8d6fd240feebf8dd288c7ab54480c4f6c3c.md deleted file mode 100644 index dd94e67..0000000 --- a/playwright-reports/html/data/d3a2b8d6fd240feebf8dd288c7ab54480c4f6c3c.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> Protected endpoint rejects request without token -- Location: tests/e2e/security.spec.ts:42:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/d4abb98a0fa953a2a1c0a620b282a6b485b14ffa.png b/playwright-reports/html/data/d4abb98a0fa953a2a1c0a620b282a6b485b14ffa.png deleted file mode 100644 index bdb2da6..0000000 Binary files a/playwright-reports/html/data/d4abb98a0fa953a2a1c0a620b282a6b485b14ffa.png and /dev/null differ diff --git a/playwright-reports/html/data/d5e2143fadeeb80a2ba6a13b44dc2f885bed52e8.webm b/playwright-reports/html/data/d5e2143fadeeb80a2ba6a13b44dc2f885bed52e8.webm deleted file mode 100644 index efb3991..0000000 Binary files a/playwright-reports/html/data/d5e2143fadeeb80a2ba6a13b44dc2f885bed52e8.webm and /dev/null differ diff --git a/playwright-reports/html/data/dbe7713931491e0cd0e13e87e054626096f94b42.md b/playwright-reports/html/data/dbe7713931491e0cd0e13e87e054626096f94b42.md deleted file mode 100644 index f31f6b3..0000000 --- a/playwright-reports/html/data/dbe7713931491e0cd0e13e87e054626096f94b42.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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: security.spec.ts >> Security - Response Headers >> API error responses do not expose server internals -- Location: tests/e2e/security.spec.ts:248:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/api/nonexistent - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/e2b61759d862ca7a398cda1bc6334e81853f0226.md b/playwright-reports/html/data/e2b61759d862ca7a398cda1bc6334e81853f0226.md deleted file mode 100644 index 9de40b9..0000000 --- a/playwright-reports/html/data/e2b61759d862ca7a398cda1bc6334e81853f0226.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Input Validation >> SQL injection in login email is handled safely -- Location: tests/e2e/security.spec.ts:149:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 45 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/ed24922c377dc98065c87c05ed7096fb5e1a16c2.md b/playwright-reports/html/data/ed24922c377dc98065c87c05ed7096fb5e1a16c2.md deleted file mode 100644 index 738028b..0000000 --- a/playwright-reports/html/data/ed24922c377dc98065c87c05ed7096fb5e1a16c2.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> Auth API Endpoints >> POST /auth/login rate limits after too many attempts -- Location: tests/e2e/api.spec.ts:215:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 65 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/edc2a5b47bfb1d71845769f50bcef7543b186b6a.md b/playwright-reports/html/data/edc2a5b47bfb1d71845769f50bcef7543b186b6a.md deleted file mode 100644 index ce9f3f8..0000000 --- a/playwright-reports/html/data/edc2a5b47bfb1d71845769f50bcef7543b186b6a.md +++ /dev/null @@ -1,23 +0,0 @@ -# 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: api.spec.ts >> Gateway API >> Gateway routes /api/ai/* to users service -- Location: tests/e2e/api.spec.ts:240:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - Authorization: Bearer dummy - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/edf29051f2ade785c8a5d06183288d2a993f4662.md b/playwright-reports/html/data/edf29051f2ade785c8a5d06183288d2a993f4662.md deleted file mode 100644 index 1b2d773..0000000 --- a/playwright-reports/html/data/edf29051f2ade785c8a5d06183288d2a993f4662.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: api.spec.ts >> AI API Endpoints >> Company AI - Generate Job Field >> POST /ai/generate-job-field rate limits after daily quota -- Location: tests/e2e/api.spec.ts:91:5 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 65 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/eebce2ea34e3cc55eb49412e03b7d1eae96af1de.md b/playwright-reports/html/data/eebce2ea34e3cc55eb49412e03b7d1eae96af1de.md deleted file mode 100644 index c75aabc..0000000 --- a/playwright-reports/html/data/eebce2ea34e3cc55eb49412e03b7d1eae96af1de.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> Login without email returns proper error -- Location: tests/e2e/security.spec.ts:20:3 - -# Error details - -``` -Error: apiRequestContext.post: connect ECONNREFUSED ::1:3000 -Call log: - - → POST http://localhost:3000/api/auth/login - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - content-type: application/json - - content-length: 27 - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/f393c21e7b2fbfd80c17dbe7eac9aa52f30a10a7.md b/playwright-reports/html/data/f393c21e7b2fbfd80c17dbe7eac9aa52f30a10a7.md deleted file mode 100644 index f9e8ffb..0000000 --- a/playwright-reports/html/data/f393c21e7b2fbfd80c17dbe7eac9aa52f30a10a7.md +++ /dev/null @@ -1,23 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> Protected endpoint rejects request with empty Bearer token -- Location: tests/e2e/security.spec.ts:60:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - Authorization: Bearer - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/fa1a5cb77246728c47633b88397169ac791b4b2c.md b/playwright-reports/html/data/fa1a5cb77246728c47633b88397169ac791b4b2c.md deleted file mode 100644 index e204c9f..0000000 --- a/playwright-reports/html/data/fa1a5cb77246728c47633b88397169ac791b4b2c.md +++ /dev/null @@ -1,23 +0,0 @@ -# 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: security.spec.ts >> Security - Authentication >> Protected endpoint rejects request with empty Bearer token -- Location: tests/e2e/security.spec.ts:60:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/ai/usage - - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - - Authorization: Bearer - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/fbca0efdf5b201e7993e209c4d950fd21c7d2865.md b/playwright-reports/html/data/fbca0efdf5b201e7993e209c4d950fd21c7d2865.md deleted file mode 100644 index d764e3c..0000000 --- a/playwright-reports/html/data/fbca0efdf5b201e7993e209c4d950fd21c7d2865.md +++ /dev/null @@ -1,22 +0,0 @@ -# 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: security.spec.ts >> Security - Response Headers >> API error responses do not expose server internals -- Location: tests/e2e/security.spec.ts:248:3 - -# Error details - -``` -Error: apiRequestContext.get: connect ECONNREFUSED ::1:3000 -Call log: - - → GET http://localhost:3000/api/api/nonexistent - - user-agent: Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.7778.96 Mobile Safari/537.36 - - accept: */* - - accept-encoding: gzip,deflate,br - -``` \ No newline at end of file diff --git a/playwright-reports/html/data/fe5c78020a2eaf74673c87f209438ff9cd840f1f.md b/playwright-reports/html/data/fe5c78020a2eaf74673c87f209438ff9cd840f1f.md deleted file mode 100644 index 6467157..0000000 --- a/playwright-reports/html/data/fe5c78020a2eaf74673c87f209438ff9cd840f1f.md +++ /dev/null @@ -1,442 +0,0 @@ -# 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: accessibility.spec.ts >> Public Frontend Accessibility >> homepage should have no accessibility violations -- Location: tests/e2e/accessibility.spec.ts:5:3 - -# Error details - -``` -Error: expect(received).toEqual(expected) // deep equality - -- Expected - 1 -+ Received + 58 - -- Array [] -+ Array [ -+ Object { -+ "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds", -+ "help": "Elements must meet minimum color contrast ratio thresholds", -+ "helpUrl": "https://dequeuniversity.com/rules/axe/4.11/color-contrast?application=playwright", -+ "id": "color-contrast", -+ "impact": "serious", -+ "nodes": Array [ -+ Object { -+ "all": Array [], -+ "any": Array [ -+ Object { -+ "data": Object { -+ "bgColor": "#fd6116", -+ "contrastRatio": 3.04, -+ "expectedContrastRatio": "4.5:1", -+ "fgColor": "#ffffff", -+ "fontSize": "10.5pt (14px)", -+ "fontWeight": "normal", -+ "messageKey": null, -+ }, -+ "id": "color-contrast", -+ "impact": "serious", -+ "message": "Element has insufficient color contrast of 3.04 (foreground color: #ffffff, background color: #fd6116, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1", -+ "relatedNodes": Array [ -+ Object { -+ "html": "", -+ "target": Array [ -+ "button[data-hk=\"00000001000001000000000a1080\"]", -+ ], -+ }, -+ ], -+ }, -+ ], -+ "failureSummary": "Fix any of the following: -+ Element has insufficient color contrast of 3.04 (foreground color: #ffffff, background color: #fd6116, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1", -+ "html": "", -+ "impact": "serious", -+ "none": Array [], -+ "target": Array [ -+ "button[data-hk=\"00000001000001000000000a1080\"]", -+ ], -+ }, -+ ], -+ "tags": Array [ -+ "cat.color", -+ "wcag2aa", -+ "wcag143", -+ "TTv5", -+ "TT13.c", -+ "EN-301-549", -+ "EN-9.1.4.3", -+ "ACT", -+ "RGAAv4", -+ "RGAA-3.2.1", -+ ], -+ }, -+ ] -``` - -# Page snapshot - -```yaml -- generic [ref=e2]: - - main [ref=e3]: - - generic: - - generic: - - generic: - - img - - generic: - - img - - generic: - - img - - generic: - - img - - generic: - - img - - generic [ref=e4]: - - navigation [ref=e6]: - - link "Nxtgauge home" [ref=e7] [cursor=pointer]: - - /url: / - - img "NXTGAUGE" [ref=e8] - - generic [ref=e9]: - - link "Home" [ref=e10] [cursor=pointer]: - - /url: / - - link "Professionals" [ref=e11] [cursor=pointer]: - - /url: /professionals - - link "About Us" [ref=e12] [cursor=pointer]: - - /url: /about - - link "Help Center" [ref=e13] [cursor=pointer]: - - /url: /help-center - - link "Contact Us" [ref=e14] [cursor=pointer]: - - /url: /contact - - link "Login" [ref=e16] [cursor=pointer]: - - /url: /login - - generic [ref=e18]: - - generic [ref=e19]: - - heading "Trusted hiring and opportunities, verified." [level=1] [ref=e20] - - paragraph [ref=e21]: Hire trusted professionals, post verified jobs, and apply faster in one platform. - - generic [ref=e22]: - - link "Get Started" [ref=e23] [cursor=pointer]: - - /url: /signup/customer - - link "How It Works" [ref=e24] [cursor=pointer]: - - /url: /#how-it-works - - paragraph [ref=e25]: Most profile and listing reviews are completed within 24-48 hours. - - region "Nxtgauge opportunity graph preview" [ref=e27]: - - generic [ref=e28]: - - generic [ref=e29]: - - img [ref=e30] - - generic [ref=e47]: Developer - - generic [ref=e48]: - - generic [ref=e49]: - - generic [ref=e50]: ✓ - - generic [ref=e51]: Everything from Nxtgauge - - paragraph [ref=e52]: Opportunity Workspace - - generic [ref=e53]: - - strong [ref=e54]: Verified Profiles - - generic [ref=e55]: Trust layer ready - - generic [ref=e56]: - - strong [ref=e57]: Matched Opportunities - - generic [ref=e58]: Priority queue - - generic [ref=e59]: - - strong [ref=e60]: Responses - - generic [ref=e61]: Tracked in one flow - - generic [ref=e62]: - - strong [ref=e63]: Updates - - generic [ref=e64]: Live status signals - - paragraph [ref=e66]: Opportunities start scattered - - generic [ref=e68]: - - generic [ref=e70]: - - heading "Who Nxtgauge is for" [level=2] [ref=e71] - - paragraph [ref=e72]: Start with one account, then activate the journey you need. - - generic [ref=e73]: - - article [ref=e74]: - - img "Post a Job" [ref=e76] - - generic [ref=e78]: - - generic [ref=e79]: - - img [ref=e81] - - generic [ref=e85]: - - img [ref=e86] - - text: Company - - heading "Post a Job" [level=3] [ref=e89] - - paragraph [ref=e90]: Create verified job openings and find the right talent faster. - - link "Explore Company" [ref=e91] [cursor=pointer]: - - /url: /roles/company - - article [ref=e92]: - - img "Apply for Jobs" [ref=e94] - - generic [ref=e96]: - - generic [ref=e97]: - - img [ref=e99] - - generic [ref=e102]: - - img [ref=e103] - - text: Job Seeker - - heading "Apply for Jobs" [level=3] [ref=e106] - - paragraph [ref=e107]: Build your profile and apply to approved opportunities quickly. - - link "Explore Job Seeker" [ref=e108] [cursor=pointer]: - - /url: /roles/job-seeker - - article [ref=e109]: - - img "Hire a Professional" [ref=e111] - - generic [ref=e113]: - - generic [ref=e114]: - - img [ref=e116] - - generic [ref=e121]: - - img [ref=e122] - - text: Customer - - heading "Hire a Professional" [level=3] [ref=e125] - - paragraph [ref=e126]: Post your requirement and discover trusted specialists. - - link "Explore Customer" [ref=e127] [cursor=pointer]: - - /url: /roles/customer - - article [ref=e128]: - - img "Join as Professional" [ref=e130] - - generic [ref=e132]: - - generic [ref=e133]: - - img [ref=e135] - - generic [ref=e139]: - - img [ref=e140] - - text: Professional - - heading "Join as Professional" [level=3] [ref=e143] - - paragraph [ref=e144]: Create a trusted profile and grow through verified demand. - - link "Explore Professionals" [ref=e145] [cursor=pointer]: - - /url: /professionals - - generic [ref=e147]: - - generic [ref=e148]: - - heading "Explore professional categories" [level=2] [ref=e150] - - link "View all professionals" [ref=e151] [cursor=pointer]: - - /url: /professionals - - generic [ref=e152]: - - button "All" [ref=e153] [cursor=pointer] - - button "Creative" [ref=e154] [cursor=pointer] - - button "Tech" [ref=e155] [cursor=pointer] - - button "Education" [ref=e156] [cursor=pointer] - - button "Wellness" [ref=e157] [cursor=pointer] - - button "Events" [ref=e158] [cursor=pointer] - - button "Marketing" [ref=e159] [cursor=pointer] - - generic [ref=e160]: - - article [ref=e161]: - - img "Developer" [ref=e163] - - generic [ref=e165]: - - generic [ref=e167]: tech - - heading "Developer" [level=3] [ref=e168] - - paragraph [ref=e169]: Join Nxtgauge as a Developer and connect with customers and companies through a trust-first workflow. - - generic [ref=e170]: - - link "Explore Developer" [ref=e171] [cursor=pointer]: - - /url: /professionals/developer - - link "Join Developer" [ref=e172] [cursor=pointer]: - - /url: /signup?intent=professional&role=DEVELOPER - - article [ref=e173]: - - img "Photographer" [ref=e175] - - generic [ref=e177]: - - generic [ref=e179]: creative - - heading "Photographer" [level=3] [ref=e180] - - paragraph [ref=e181]: Join as a Photographer and receive opportunities where trust and profile quality matter. - - generic [ref=e182]: - - link "Explore Photographer" [ref=e183] [cursor=pointer]: - - /url: /professionals/photographer - - link "Join Photographer" [ref=e184] [cursor=pointer]: - - /url: /signup?intent=professional&role=PHOTOGRAPHER - - article [ref=e185]: - - img "Makeup Artist" [ref=e187] - - generic [ref=e189]: - - generic [ref=e191]: creative - - heading "Makeup Artist" [level=3] [ref=e192] - - paragraph [ref=e193]: Join Nxtgauge as a Makeup Artist and connect with verified requirements from customers and event planners. - - generic [ref=e194]: - - link "Explore Makeup Artist" [ref=e195] [cursor=pointer]: - - /url: /professionals/makeup-artist - - link "Join Makeup Artist" [ref=e196] [cursor=pointer]: - - /url: /signup?intent=professional&role=MAKEUP_ARTIST - - article [ref=e197]: - - img "Tutor" [ref=e199] - - generic [ref=e201]: - - generic [ref=e203]: education - - heading "Tutor" [level=3] [ref=e204] - - paragraph [ref=e205]: Join as a Tutor to receive verified educational requirements and present your profile with confidence. - - generic [ref=e206]: - - link "Explore Tutor" [ref=e207] [cursor=pointer]: - - /url: /professionals/tutor - - link "Join Tutor" [ref=e208] [cursor=pointer]: - - /url: /signup?intent=professional&role=TUTOR - - article [ref=e209]: - - img "Video Editor" [ref=e211] - - generic [ref=e213]: - - generic [ref=e215]: creative - - heading "Video Editor" [level=3] [ref=e216] - - paragraph [ref=e217]: Join as a Video Editor and connect with customers and companies through a review-based trust model. - - generic [ref=e218]: - - link "Explore Video Editor" [ref=e219] [cursor=pointer]: - - /url: /professionals/video-editor - - link "Join Video Editor" [ref=e220] [cursor=pointer]: - - /url: /signup?intent=professional&role=VIDEO_EDITOR - - article [ref=e221]: - - img "Graphic Designer" [ref=e223] - - generic [ref=e225]: - - generic [ref=e227]: creative - - heading "Graphic Designer" [level=3] [ref=e228] - - paragraph [ref=e229]: Join as a Graphic Designer to connect with verified requirements and role-relevant opportunities. - - generic [ref=e230]: - - link "Explore Graphic Designer" [ref=e231] [cursor=pointer]: - - /url: /professionals/graphic-designer - - link "Join Graphic Designer" [ref=e232] [cursor=pointer]: - - /url: /signup?intent=professional&role=GRAPHIC_DESIGNER - - article [ref=e233]: - - img "Social Media Manager" [ref=e235] - - generic [ref=e237]: - - generic [ref=e239]: marketing - - heading "Social Media Manager" [level=3] [ref=e240] - - paragraph [ref=e241]: Join as a Social Media Manager and grow with trusted customer and company requirements. - - generic [ref=e242]: - - link "Explore Social Media Manager" [ref=e243] [cursor=pointer]: - - /url: /professionals/social-media-manager - - link "Join Social Media Manager" [ref=e244] [cursor=pointer]: - - /url: /signup?intent=professional&role=SOCIAL_MEDIA_MANAGER - - article [ref=e245]: - - img "Fitness Trainer" [ref=e247] - - generic [ref=e249]: - - generic [ref=e251]: wellness - - heading "Fitness Trainer" [level=3] [ref=e252] - - paragraph [ref=e253]: Join as a Fitness Trainer and grow through profile-led visibility in a trust-first marketplace. - - generic [ref=e254]: - - link "Explore Fitness Trainer" [ref=e255] [cursor=pointer]: - - /url: /professionals/fitness-trainer - - link "Join Fitness Trainer" [ref=e256] [cursor=pointer]: - - /url: /signup?intent=professional&role=FITNESS_TRAINER - - generic [ref=e258]: - - generic [ref=e259]: - - paragraph [ref=e260]: Why Nxtgauge - - heading "Trust, approvals, and better matching in one flow." [level=2] [ref=e261] - - generic [ref=e262]: - - article [ref=e263]: - - generic [ref=e265]: - - paragraph [ref=e266]: Capability 1 of 6 - - img [ref=e268] - - heading "Verified profiles & businesses" [level=3] [ref=e271] - - paragraph [ref=e272]: Identity and profile checks reduce fake submissions and spam. - - generic [ref=e274]: - - button "Show Verified profiles & businesses" [ref=e275]: - - img [ref=e276] - - button "Show Approval-based quality (24-48 hours)" [ref=e279]: - - img [ref=e280] - - button "Show Smart matching using tags/skills" [ref=e282]: - - img [ref=e283] - - button "Show Focused discovery with filters" [ref=e285]: - - img [ref=e286] - - button "Show Controlled contact visibility" [ref=e289]: - - img [ref=e290] - - button "Show Notifications & updates" [ref=e293]: - - img [ref=e294] - - generic [ref=e297]: - - button "Previous slide" [ref=e298]: ◀ - - button "Next slide" [ref=e299]: ▶ - - generic [ref=e301]: - - generic [ref=e302]: - - paragraph [ref=e303]: How it works - - heading "Clear journey, zero confusion" [level=2] [ref=e304] - - article [ref=e305]: - - generic [ref=e306]: - - img "Customers role" [ref=e308] - - generic [ref=e309]: - - paragraph [ref=e310]: Customers - - heading "Hire trusted professionals with less noise" [level=3] [ref=e311] - - paragraph [ref=e312]: Post requirements, pass review checks, and receive better-fit responses. - - generic [ref=e313]: - - paragraph [ref=e314]: Step Flow - - generic [ref=e315]: - - generic [ref=e316]: - - generic [ref=e317]: "1" - - generic [ref=e318]: - - heading "Create account" [level=4] [ref=e319] - - paragraph [ref=e320]: Sign up quickly with customer intent. - - generic [ref=e321]: - - generic [ref=e322]: "2" - - generic [ref=e323]: - - heading "Share requirement" [level=4] [ref=e324] - - paragraph [ref=e325]: Add budget, scope, and timing. - - generic [ref=e326]: - - generic [ref=e327]: "3" - - generic [ref=e328]: - - heading "Review and verify" [level=4] [ref=e329] - - paragraph [ref=e330]: Quality checks run before visibility. - - generic [ref=e331]: - - generic [ref=e332]: "4" - - generic [ref=e333]: - - heading "Track responses" [level=4] [ref=e334] - - paragraph [ref=e335]: Monitor replies and status updates. - - generic [ref=e337]: - - button "Previous role" [ref=e338]: ← - - button "Next role" [ref=e339]: → - - generic [ref=e346]: - - heading "Frequently asked questions" [level=2] [ref=e347] - - paragraph [ref=e348]: Quick answers before you create your account. - - generic [ref=e349]: - - article [ref=e350]: - - button "What is Nxtgauge? ⌄" [ref=e351] [cursor=pointer]: - - generic [ref=e352]: What is Nxtgauge? - - generic [ref=e353]: ⌄ - - paragraph [ref=e354]: Nxtgauge connects customers, professionals, companies, and job seekers in one trusted platform. - - article [ref=e355]: - - button "Who can join Nxtgauge? ⌄" [ref=e356] [cursor=pointer]: - - generic [ref=e357]: Who can join Nxtgauge? - - generic [ref=e358]: ⌄ - - article [ref=e359]: - - button "Why does Nxtgauge require verification? ⌄" [ref=e360] [cursor=pointer]: - - generic [ref=e361]: Why does Nxtgauge require verification? - - generic [ref=e362]: ⌄ - - article [ref=e363]: - - button "How long does account approval take? ⌄" [ref=e364] [cursor=pointer]: - - generic [ref=e365]: How long does account approval take? - - generic [ref=e366]: ⌄ - - article [ref=e367]: - - button "Do I need to choose my role during signup? ⌄" [ref=e368] [cursor=pointer]: - - generic [ref=e369]: Do I need to choose my role during signup? - - generic [ref=e370]: ⌄ - - generic [ref=e372]: - - generic [ref=e373]: - - paragraph [ref=e374]: Quick Actions - - heading "Start with one account. Choose your path after." [level=2] [ref=e375] - - paragraph [ref=e376]: Hire, post, apply, or join as a professional from stable role-specific pages. - - generic [ref=e377]: - - link "Get Started" [ref=e378] [cursor=pointer]: - - /url: /signup/customer - - link "Explore Professionals" [ref=e379] [cursor=pointer]: - - /url: /professionals - - link "Post a Job" [ref=e380] [cursor=pointer]: - - /url: /signup/company - - generic [ref=e382]: - - img "NXTGAUGE" [ref=e383] - - paragraph [ref=e384]: © 2026 Nxtgauge. All rights reserved. - - generic [ref=e385]: - - link "Professionals" [ref=e386] [cursor=pointer]: - - /url: /professionals - - link "Terms" [ref=e387] [cursor=pointer]: - - /url: /terms - - link "Privacy" [ref=e388] [cursor=pointer]: - - /url: /privacy - - link "Help Center" [ref=e389] [cursor=pointer]: - - /url: /help-center - - button "AI Assistant" [ref=e390] [cursor=pointer]: - - img [ref=e391] -``` - -# Test source - -```ts - 1 | import { test, expect } from "@playwright/test"; - 2 | import AxeBuilder from "@axe-core/playwright"; - 3 | - 4 | test.describe("Public Frontend Accessibility", () => { - 5 | test("homepage should have no accessibility violations", async ({ page }) => { - 6 | await page.goto("/"); - 7 | const results = await new AxeBuilder({ page }).analyze(); -> 8 | expect(results.violations).toEqual([]); - | ^ Error: expect(received).toEqual(expected) // deep equality - 9 | }); - 10 | - 11 | test("professionals listing page should be accessible", async ({ page }) => { - 12 | await page.goto("/professionals"); - 13 | const results = await new AxeBuilder({ page }).analyze(); - 14 | expect(results.violations).toEqual([]); - 15 | }); - 16 | }); - 17 | -``` \ No newline at end of file diff --git a/playwright-reports/html/index.html b/playwright-reports/html/index.html deleted file mode 100644 index a312dce..0000000 --- a/playwright-reports/html/index.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - Playwright Test Report - - - - -
- - - \ No newline at end of file diff --git a/tests/e2e/api.spec.ts b/tests/e2e/api.spec.ts index f706c01..e638fa8 100644 --- a/tests/e2e/api.spec.ts +++ b/tests/e2e/api.spec.ts @@ -1,15 +1,19 @@ import { test, expect, request } from "@playwright/test"; +import { solveCaptcha } from "./helpers/captcha"; -const API_BASE = process.env.TEST_ENV === 'production' - ? "https://test111.nxtgauge.com/api" +const API_BASE = process.env.TEST_ENV === 'production' + ? "https://test111.nxtgauge.com/api" : "http://localhost:3000/api"; async function getAuthToken(): Promise { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "testtutora2026@example.com", password: "Test1234!", + captcha_id, + captcha_answer, }, }); if (!res.ok()) return null; @@ -19,10 +23,13 @@ async function getAuthToken(): Promise { async function getCompanyAuthToken(): Promise { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "testcompany@example.com", password: "TestPassword123!", + captcha_id, + captcha_answer, }, }); if (!res.ok()) return null; @@ -183,10 +190,13 @@ test.describe("AI API Endpoints", () => { test.describe("Auth API Endpoints", () => { test("POST /auth/login returns token for valid credentials", async () => { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "testtutora2026@example.com", password: "Test1234!", + captcha_id, + captcha_answer, }, }); @@ -203,10 +213,13 @@ test.describe("Auth API Endpoints", () => { test("POST /auth/login returns 401 for invalid credentials", async () => { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "invalid@example.com", password: "wrongpassword", + captcha_id, + captcha_answer, }, }); @@ -217,18 +230,24 @@ test.describe("Auth API Endpoints", () => { test("POST /auth/login rate limits after too many attempts", async () => { const ctx = await request.newContext(); for (let i = 0; i < 6; i++) { + const { captcha_id, captcha_answer } = await solveCaptcha(); await ctx.post(`${API_BASE}/auth/login`, { data: { email: "testtutora2026@example.com", password: "wrongpassword", + captcha_id, + captcha_answer, }, }); } + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "testtutora2026@example.com", password: "Test1234!", + captcha_id, + captcha_answer, }, }); diff --git a/tests/e2e/company-admin-e2e.spec.ts b/tests/e2e/company-admin-e2e.spec.ts index 89bdf2e..cb215c1 100644 --- a/tests/e2e/company-admin-e2e.spec.ts +++ b/tests/e2e/company-admin-e2e.spec.ts @@ -1,9 +1,11 @@ import { test, expect, chromium, BrowserContext, Page } from "@playwright/test"; import { randomUUID } from "crypto"; -import { execSync } from "child_process"; import * as fs from "fs"; +import * as path from "path"; +import { apiRegister, apiVerifyEmail, apiLogin } from "./helpers/auth-flow"; +import { APP_BASE_URL } from "./helpers/env"; -const SCREENSHOT_DIR = "/Users/ashwin/workspace/nxtgauge-frontend-solid/test-results/company-admin-e2e"; +const SCREENSHOT_DIR = path.join(__dirname, "..", "..", "test-results", "company-admin-e2e"); if (!fs.existsSync(SCREENSHOT_DIR)) { fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); @@ -20,63 +22,30 @@ interface TestUser { companyName?: string; } -async function getOTPFromRedis(userId: string): Promise { - try { - const plainKey = `otp:plain:${userId}`; - let otpCode = execSync(`redis-cli GET "${plainKey}"`, { encoding: "utf8" }).trim(); - if (otpCode && otpCode.length >= 4) return otpCode; - - const keys = execSync("redis-cli KEYS 'otp:code:*'", { encoding: "utf8" }) - .trim().split("\n").filter(Boolean); - for (const k of keys) { - const v = execSync(`redis-cli GET "${k}"`, { encoding: "utf8" }).trim(); - if (v === userId) { - otpCode = k.replace("otp:code:", ""); - return otpCode; - } - } - return null; - } catch { - return null; - } -} - async function registerUser(user: TestUser): Promise { console.log(`\n📝 Registering ${user.intent} via API...`); - const regResponse = await fetch("http://localhost:9100/api/auth/register", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(user), + const regData = await apiRegister({ + email: user.email, + password: user.password, + first_name: user.firstName, + last_name: user.lastName, + intent: user.intent, }); - const regData = await regResponse.json(); if (!regData.user_id) throw new Error(`Registration failed: ${JSON.stringify(regData)}`); user.userId = regData.user_id; console.log(` ✅ Registered, user_id: ${user.userId}`); - // Get OTP from Redis + // Get OTP from Redis + verify via API await new Promise(r => setTimeout(r, 500)); - const otpCode = await getOTPFromRedis(user.userId!); - if (!otpCode) throw new Error("Could not get OTP from Redis"); - console.log(` ✅ OTP retrieved: ${otpCode}`); - - // Verify OTP - const verifyResponse = await fetch("http://localhost:9100/api/auth/verify-email", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: user.userId, otp: otpCode }) - }); - if (!verifyResponse.ok) throw new Error("OTP verification failed"); + const verified = await apiVerifyEmail(user.email, user.userId!); + if (!verified) throw new Error("OTP verification failed"); console.log(` ✅ OTP verified!`); // Login - const loginResponse = await fetch("http://localhost:9100/api/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email: user.email, password: user.password }) - }); - const loginData = await loginResponse.json(); - if (!loginData.access_token) throw new Error("Login failed"); - user.accessToken = loginData.access_token; + const loginData = await apiLogin(user.email, user.password); + const accessToken = loginData?.access_token || ""; + if (!accessToken) throw new Error("Login failed"); + user.accessToken = accessToken; console.log(` ✅ Logged in, token length: ${user.accessToken!.length}`); return user; @@ -130,8 +99,8 @@ test.describe("Company E2E Flow with Admin Verification", () => { await registerUser(companyUser); // ==================== BROWSER SETUP ==================== - const browser = await chromium.launch({ headless: false, slowMo: 30 }); - const context = await browser.newContext({ viewport: { width: 1400, height: 900 } }); + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext({ baseURL: APP_BASE_URL, viewport: { width: 1400, height: 900 } }); // ==================== COMPANY FRONTEND FLOW ==================== console.log("\n" + "=".repeat(60)); @@ -142,7 +111,7 @@ test.describe("Company E2E Flow with Admin Verification", () => { await setupFrontendAuth(companyPage, companyUser); // Navigate to dashboard - await companyPage.goto("http://localhost:3000/dashboard?role=COMPANY", { waitUntil: "networkidle", timeout: 15000 }); + await companyPage.goto("/dashboard?role=COMPANY", { waitUntil: "networkidle", timeout: 15000 }); await new Promise(r => setTimeout(r, 3000)); await takeScreenshot(companyPage, "01_company_dashboard"); console.log(" ✅ Company dashboard loaded"); diff --git a/tests/e2e/company-complete-e2e-with-admin-approval.spec.ts b/tests/e2e/company-complete-e2e-with-admin-approval.spec.ts index bd71187..caa669e 100644 --- a/tests/e2e/company-complete-e2e-with-admin-approval.spec.ts +++ b/tests/e2e/company-complete-e2e-with-admin-approval.spec.ts @@ -1,9 +1,11 @@ import { test, expect, chromium, BrowserContext, Page } from "@playwright/test"; import { randomUUID } from "crypto"; -import { execSync } from "child_process"; import * as fs from "fs"; +import * as path from "path"; +import { apiRegister, apiVerifyEmail, apiLogin } from "./helpers/auth-flow"; +import { APP_BASE_URL, API_BASE } from "./helpers/env"; -const SCREENSHOT_DIR = "/Users/ashwin/workspace/nxtgauge-frontend-solid/test-results/company-complete-e2e"; +const SCREENSHOT_DIR = path.join(__dirname, "..", "..", "test-results", "company-complete-e2e"); if (!fs.existsSync(SCREENSHOT_DIR)) { fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); @@ -20,63 +22,30 @@ interface TestUser { companyName?: string; } -async function getOTPFromRedis(userId: string): Promise { - try { - const plainKey = `otp:plain:${userId}`; - let otpCode = execSync(`redis-cli GET "${plainKey}"`, { encoding: "utf8" }).trim(); - if (otpCode && otpCode.length >= 4) return otpCode; - - const keys = execSync("redis-cli KEYS 'otp:code:*'", { encoding: "utf8" }) - .trim().split("\n").filter(Boolean); - for (const k of keys) { - const v = execSync(`redis-cli GET "${k}"`, { encoding: "utf8" }).trim(); - if (v === userId) { - otpCode = k.replace("otp:code:", ""); - return otpCode; - } - } - return null; - } catch { - return null; - } -} - async function registerUser(user: TestUser): Promise { console.log(`\n📝 Registering ${user.intent} via API...`); - const regResponse = await fetch("http://localhost:9100/api/auth/register", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(user), + const regData = await apiRegister({ + email: user.email, + password: user.password, + first_name: user.firstName, + last_name: user.lastName, + intent: user.intent, }); - const regData = await regResponse.json(); if (!regData.user_id) throw new Error(`Registration failed: ${JSON.stringify(regData)}`); user.userId = regData.user_id; console.log(` ✅ Registered, user_id: ${user.userId}`); - // Get OTP from Redis + // Get OTP from Redis + verify via API await new Promise(r => setTimeout(r, 500)); - const otpCode = await getOTPFromRedis(user.userId!); - if (!otpCode) throw new Error("Could not get OTP from Redis"); - console.log(` ✅ OTP retrieved: ${otpCode}`); - - // Verify OTP - const verifyResponse = await fetch("http://localhost:9100/api/auth/verify-email", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: user.userId, otp: otpCode }) - }); - if (!verifyResponse.ok) throw new Error("OTP verification failed"); + const verified = await apiVerifyEmail(user.email, user.userId!); + if (!verified) throw new Error("OTP verification failed"); console.log(` ✅ OTP verified!`); // Login - const loginResponse = await fetch("http://localhost:9100/api/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email: user.email, password: user.password }) - }); - const loginData = await loginResponse.json(); - if (!loginData.access_token) throw new Error("Login failed"); - user.accessToken = loginData.access_token; + const loginData = await apiLogin(user.email, user.password); + const accessToken = loginData?.access_token || ""; + if (!accessToken) throw new Error("Login failed"); + user.accessToken = accessToken; console.log(` ✅ Logged in, token length: ${user.accessToken!.length}`); return user; @@ -130,8 +99,8 @@ test.describe("Company Complete E2E with Admin Approval", () => { await registerUser(companyUser); // ==================== BROWSER SETUP ==================== - const browser = await chromium.launch({ headless: false, slowMo: 30 }); - const context = await browser.newContext({ viewport: { width: 1400, height: 900 } }); + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext({ baseURL: APP_BASE_URL, viewport: { width: 1400, height: 900 } }); // ==================== COMPANY FRONTEND FLOW ==================== console.log("\n" + "=".repeat(60)); @@ -142,7 +111,7 @@ test.describe("Company Complete E2E with Admin Approval", () => { await setupFrontendAuth(companyPage, companyUser); // Navigate to dashboard - await companyPage.goto("http://localhost:3000/dashboard?role=COMPANY", { waitUntil: "networkidle", timeout: 15000 }); + await companyPage.goto("/dashboard?role=COMPANY", { waitUntil: "networkidle", timeout: 15000 }); await new Promise(r => setTimeout(r, 3000)); await takeScreenshot(companyPage, "01_company_dashboard"); console.log(" ✅ Company dashboard loaded"); @@ -223,7 +192,7 @@ test.describe("Company Complete E2E with Admin Approval", () => { // Click submit for verification via API console.log(" ℹ️ Submit button enabled, submitting via API..."); - const submitResponse = await fetch("http://localhost:9100/api/profile/submit-for-verification", { + const submitResponse = await fetch(`${API_BASE}/profile/submit-for-verification`, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/tests/e2e/company-e2e-complete-flow.spec.ts b/tests/e2e/company-e2e-complete-flow.spec.ts index f9048ce..c8fbc23 100644 --- a/tests/e2e/company-e2e-complete-flow.spec.ts +++ b/tests/e2e/company-e2e-complete-flow.spec.ts @@ -7,11 +7,12 @@ */ import { test, expect, chromium, BrowserContext, Page } from "@playwright/test"; import { randomUUID } from "crypto"; -import { execSync } from "child_process"; import * as fs from "fs"; import * as path from "path"; +import { apiRegister, apiVerifyEmail, apiLogin } from "./helpers/auth-flow"; +import { APP_BASE_URL, API_BASE } from "./helpers/env"; -const SCREENSHOT_DIR = "/Users/ashwin/workspace/nxtgauge-frontend-solid/test-results/company-e2e-complete"; +const SCREENSHOT_DIR = path.join(__dirname, "..", "..", "test-results", "company-e2e-complete"); if (!fs.existsSync(SCREENSHOT_DIR)) { fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); @@ -28,74 +29,30 @@ interface TestUser { companyName?: string; } -async function getOTPFromRedis(userId: string): Promise { - try { - // Try to get OTP from Redis using multiple key patterns - const plainKey = `otp:plain:${userId}`; - let otpCode = execSync(`redis-cli GET "${plainKey}"`, { encoding: "utf8" }).trim(); - if (otpCode && otpCode.length >= 4) return otpCode; - - // Try otp:code:* pattern - const keys = execSync("redis-cli KEYS 'otp:code:*'", { encoding: "utf8" }) - .trim().split("\n").filter(Boolean); - for (const k of keys) { - const v = execSync(`redis-cli GET "${k}"`, { encoding: "utf8" }).trim(); - if (v === userId) { - otpCode = k.replace("otp:code:", ""); - return otpCode; - } - } - // Fallback: try any otp key matching the userId - const allKeys = execSync("redis-cli KEYS 'otp:*'", { encoding: "utf8" }) - .trim().split("\n").filter(Boolean); - for (const k of allKeys) { - const v = execSync(`redis-cli GET "${k}"`, { encoding: "utf8" }).trim(); - if (v === userId) { - return k.split(":").pop() || null; - } - } - return null; - } catch { - return null; - } -} - async function registerUser(user: TestUser): Promise { console.log(`\n📝 Registering ${user.intent} via API...`); - const regResponse = await fetch("http://localhost:9100/api/auth/register", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(user), + const regData = await apiRegister({ + email: user.email, + password: user.password, + first_name: user.firstName, + last_name: user.lastName, + intent: user.intent, }); - const regData = await regResponse.json(); if (!regData.user_id) throw new Error(`Registration failed: ${JSON.stringify(regData)}`); user.userId = regData.user_id; console.log(` ✅ Registered, user_id: ${user.userId}`); - // Wait for OTP to be generated + // Wait for OTP to be generated, then verify via API await new Promise(r => setTimeout(r, 1000)); - const otpCode = await getOTPFromRedis(user.userId!); - if (!otpCode) throw new Error("Could not get OTP from Redis"); - console.log(` ✅ OTP retrieved: ${otpCode}`); - - // Verify OTP - const verifyResponse = await fetch("http://localhost:9100/api/auth/verify-email", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: user.userId, otp: otpCode }) - }); - if (!verifyResponse.ok) throw new Error("OTP verification failed"); + const verified = await apiVerifyEmail(user.email, user.userId!); + if (!verified) throw new Error("OTP verification failed"); console.log(` ✅ OTP verified!`); // Login - const loginResponse = await fetch("http://localhost:9100/api/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email: user.email, password: user.password }) - }); - const loginData = await loginResponse.json(); - if (!loginData.access_token) throw new Error("Login failed"); - user.accessToken = loginData.access_token; + const loginData = await apiLogin(user.email, user.password); + const accessToken = loginData?.access_token || ""; + if (!accessToken) throw new Error("Login failed"); + user.accessToken = accessToken; console.log(` ✅ Logged in, token length: ${user.accessToken!.length}`); return user; @@ -149,8 +106,8 @@ test.describe("Company E2E Complete Flow", () => { await registerUser(companyUser); // ==================== BROWSER SETUP ==================== - const browser = await chromium.launch({ headless: false, slowMo: 30 }); - const context = await browser.newContext({ viewport: { width: 1400, height: 900 } }); + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext({ baseURL: APP_BASE_URL, viewport: { width: 1400, height: 900 } }); // ==================== DASHBOARD FLOW ==================== console.log("\n" + "=".repeat(60)); @@ -161,7 +118,7 @@ test.describe("Company E2E Complete Flow", () => { await setupFrontendAuth(companyPage, companyUser); // Navigate to dashboard - await companyPage.goto("http://localhost:3000/dashboard?role=COMPANY", { waitUntil: "networkidle", timeout: 15000 }); + await companyPage.goto("/dashboard?role=COMPANY", { waitUntil: "networkidle", timeout: 15000 }); await new Promise(r => setTimeout(r, 3000)); await takeScreenshot(companyPage, "01_dashboard_loaded"); console.log(" ✅ Company dashboard loaded"); @@ -287,7 +244,7 @@ test.describe("Company E2E Complete Flow", () => { // Try to submit verification via API first to ensure it works console.log(" ℹ️ Attempting verification submission via API..."); - const submitResponse = await fetch("http://localhost:9100/api/profile/submit-for-verification", { + const submitResponse = await fetch(`${API_BASE}/profile/submit-for-verification`, { method: "POST", headers: { "Content-Type": "application/json", @@ -331,7 +288,7 @@ test.describe("Company E2E Complete Flow", () => { console.log(" ⚠️ Submit button still disabled - checking missing fields..."); // Check what fields are missing via API - const profileRes = await fetch("http://localhost:9100/api/companies/profile/me", { + const profileRes = await fetch(`${API_BASE}/companies/profile/me`, { headers: { "Authorization": `Bearer ${companyUser.accessToken}` } }); if (profileRes.ok) { diff --git a/tests/e2e/company-e2e-full.spec.ts b/tests/e2e/company-e2e-full.spec.ts index f973d8d..b533f20 100644 --- a/tests/e2e/company-e2e-full.spec.ts +++ b/tests/e2e/company-e2e-full.spec.ts @@ -1,35 +1,16 @@ import { test, expect, chromium } from "@playwright/test"; import { randomUUID } from "crypto"; -import { execSync } from "child_process"; import * as fs from "fs"; +import * as path from "path"; +import { apiRegister, apiVerifyEmail, apiLogin } from "./helpers/auth-flow"; +import { APP_BASE_URL } from "./helpers/env"; -const SCREENSHOT_DIR = "/Users/ashwin/workspace/nxtgauge-frontend-solid/test-results/company-e2e"; +const SCREENSHOT_DIR = path.join(__dirname, "..", "..", "test-results", "company-e2e"); if (!fs.existsSync(SCREENSHOT_DIR)) { fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); } -async function getOTPFromRedis(userId: string): Promise { - try { - const plainKey = `otp:plain:${userId}`; - let otpCode = execSync(`redis-cli GET "${plainKey}"`, { encoding: "utf8" }).trim(); - if (otpCode && otpCode.length >= 4) return otpCode; - - const keys = execSync("redis-cli KEYS 'otp:code:*'", { encoding: "utf8" }) - .trim().split("\n").filter(Boolean); - for (const k of keys) { - const v = execSync(`redis-cli GET "${k}"`, { encoding: "utf8" }).trim(); - if (v === userId) { - otpCode = k.replace("otp:code:", ""); - return otpCode; - } - } - return null; - } catch { - return null; - } -} - test.describe("Company E2E Full Flow", () => { test("complete company registration → OTP → login → dashboard → profile → verification", async () => { const testEmail = `e2ecompany${randomUUID().slice(0, 8)}@test.com`; @@ -39,19 +20,14 @@ test.describe("Company E2E Full Flow", () => { console.log("📧 Email:", testEmail); console.log("🏢 Company:", testCompanyName); - const browser = await chromium.launch({ headless: false, slowMo: 30 }); - const context = await browser.newContext({ viewport: { width: 1400, height: 900 } }); + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext({ baseURL: APP_BASE_URL, viewport: { width: 1400, height: 900 } }); // ==================== STEP 1: REGISTER VIA API ==================== console.log("\n📝 STEP 1: Register via API"); let regData: any; try { - const regResponse = await fetch("http://localhost:9100/api/auth/register", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email: testEmail, first_name: "John", last_name: "Doe", password: testPassword, intent: "company" }) - }); - regData = await regResponse.json(); + regData = await apiRegister({ email: testEmail, first_name: "John", last_name: "Doe", password: testPassword, intent: "company" }); expect(regData.user_id).toBeTruthy(); console.log(" ✅ PASS: Registration successful, user_id:", regData.user_id); } catch (e: any) { @@ -59,30 +35,13 @@ test.describe("Company E2E Full Flow", () => { throw e; } - // ==================== STEP 2: OTP VIA REDIS ==================== - console.log("\n🔐 STEP 2: OTP via Redis"); + // ==================== STEP 2+3: OTP VIA REDIS + VERIFY VIA API ==================== + console.log("\n🔐 STEP 2+3: OTP via Redis, verify via API"); await new Promise(r => setTimeout(r, 500)); - let otpCode: string | null = null; try { - otpCode = await getOTPFromRedis(regData.user_id); - expect(otpCode).toBeTruthy(); - console.log(" ✅ PASS: OTP retrieved from Redis:", otpCode); - } catch (e: any) { - console.log(" ❌ FAIL: Could not get OTP -", e.message); - throw e; - } - - // ==================== STEP 3: VERIFY OTP VIA API ==================== - console.log("\n✅ STEP 3: Verify OTP via API"); - try { - const verifyResponse = await fetch("http://localhost:9100/api/auth/verify-email", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: regData.user_id, otp: otpCode }) - }); - const verifyData = await verifyResponse.json(); - expect(verifyResponse.ok).toBe(true); - console.log(" ✅ PASS: OTP verified! Response:", JSON.stringify(verifyData)); + const verified = await apiVerifyEmail(testEmail, regData.user_id); + expect(verified).toBe(true); + console.log(" ✅ PASS: OTP retrieved and verified!"); } catch (e: any) { console.log(" ❌ FAIL: OTP verification failed -", e.message); throw e; @@ -92,13 +51,8 @@ test.describe("Company E2E Full Flow", () => { console.log("\n🔑 STEP 4: Login via API"); let accessToken = ""; try { - const loginResponse = await fetch("http://localhost:9100/api/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email: testEmail, password: testPassword }) - }); - const loginData = await loginResponse.json(); - accessToken = loginData.access_token || ""; + const loginData = await apiLogin(testEmail, testPassword); + accessToken = loginData?.access_token || ""; expect(accessToken).toBeTruthy(); console.log(" ✅ PASS: Login successful, token length:", accessToken.length); } catch (e: any) { @@ -125,7 +79,7 @@ test.describe("Company E2E Full Flow", () => { }, { token: accessToken, email: testEmail, userId: regData.user_id }); try { - await page.goto("http://localhost:3000/dashboard?role=COMPANY", { waitUntil: "networkidle", timeout: 15000 }); + await page.goto("/dashboard?role=COMPANY", { waitUntil: "networkidle", timeout: 15000 }); await page.waitForTimeout(3000); await page.screenshot({ path: `${SCREENSHOT_DIR}/step05_dashboard.png`, fullPage: true }); console.log(" ✅ PASS: Dashboard loaded"); diff --git a/tests/e2e/company-jobs.spec.ts b/tests/e2e/company-jobs.spec.ts index e1077c5..a4f2ba5 100644 --- a/tests/e2e/company-jobs.spec.ts +++ b/tests/e2e/company-jobs.spec.ts @@ -1,17 +1,12 @@ import { test, expect } from "@playwright/test"; import AxeBuilder from "@axe-core/playwright"; +import { apiLogin } from "./helpers/auth-flow"; async function setupAuth(page: any): Promise { - const res = await page.request.post("http://localhost:3000/api/auth/login", { - data: { - email: "testcompany@example.com", - password: "TestPassword123!", - }, - }); - if (!res.ok()) return false; - const data = await res.json(); - const token = data.access_token; - await page.goto("http://localhost:3000/dashboard"); + const loginData = await apiLogin("testcompany@example.com", "TestPassword123!"); + if (!loginData) return false; + const token = loginData.access_token; + await page.goto("/dashboard"); await page.evaluate((t: string) => { window.sessionStorage.setItem("nxtgauge_access_token", t); window.sessionStorage.setItem("nxtgauge_frontend_access_token", t); diff --git a/tests/e2e/company-verification-flow.spec.ts b/tests/e2e/company-verification-flow.spec.ts index b813c45..e564a32 100644 --- a/tests/e2e/company-verification-flow.spec.ts +++ b/tests/e2e/company-verification-flow.spec.ts @@ -1,5 +1,6 @@ import { test, expect, chromium } from "@playwright/test"; import { randomUUID } from "crypto"; +import { APP_BASE_URL } from "./helpers/env"; // Generate random test data const testEmail = `testcompany${randomUUID().slice(0, 8)}@test.com`; @@ -17,11 +18,11 @@ test.setTimeout(300000); // 5 minutes to allow manual CAPTCHA entry test("Company signup -> verification flow", async () => { // Launch browser with UI visible const browser = await chromium.launch({ - headless: false, - slowMo: 200, + headless: true, }); const context = await browser.newContext({ + baseURL: APP_BASE_URL, viewport: { width: 1400, height: 900 }, recordVideo: { dir: "./test-videos/", @@ -34,7 +35,7 @@ test("Company signup -> verification flow", async () => { try { // Step 1: Navigate to public website signup console.log("🌐 Step 1: Opening public website signup..."); - await page.goto("http://localhost:3001/signup?intent=company"); + await page.goto("/signup?intent=company"); await page.waitForLoadState("networkidle"); await page.screenshot({ path: "./test-results/01-signup-page.png", fullPage: true }); @@ -126,7 +127,7 @@ test("Company signup -> verification flow", async () => { // Should be redirected to login, or navigate there if (!page.url().includes("/login")) { - await page.goto("http://localhost:3001/login"); + await page.goto("/login"); await page.waitForLoadState("networkidle"); } @@ -196,7 +197,7 @@ test("Company signup -> verification flow", async () => { // Step 7: Open Admin Panel console.log("🔐 Step 7: Opening admin panel..."); const adminPage = await context.newPage(); - await adminPage.goto("http://localhost:3000/login"); + await adminPage.goto("/login"); await adminPage.waitForLoadState("networkidle"); await adminPage.screenshot({ path: "./test-results/07-admin-login.png", fullPage: true }); diff --git a/tests/e2e/full-e2e-company-jobseeker-admin.spec.ts b/tests/e2e/full-e2e-company-jobseeker-admin.spec.ts index c3ba8c6..4c46530 100644 --- a/tests/e2e/full-e2e-company-jobseeker-admin.spec.ts +++ b/tests/e2e/full-e2e-company-jobseeker-admin.spec.ts @@ -1,9 +1,11 @@ import { test, expect, chromium, BrowserContext, Page } from "@playwright/test"; import { randomUUID } from "crypto"; -import { execSync } from "child_process"; import * as fs from "fs"; +import * as path from "path"; +import { apiRegister, apiVerifyEmail, apiLogin } from "./helpers/auth-flow"; +import { APP_BASE_URL } from "./helpers/env"; -const SCREENSHOT_DIR = "/Users/ashwin/workspace/nxtgauge-frontend-solid/test-results/full-e2e"; +const SCREENSHOT_DIR = path.join(__dirname, "..", "..", "test-results", "full-e2e"); if (!fs.existsSync(SCREENSHOT_DIR)) { fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); @@ -20,63 +22,30 @@ interface TestUser { companyName?: string; } -async function getOTPFromRedis(userId: string): Promise { - try { - const plainKey = `otp:plain:${userId}`; - let otpCode = execSync(`redis-cli GET "${plainKey}"`, { encoding: "utf8" }).trim(); - if (otpCode && otpCode.length >= 4) return otpCode; - - const keys = execSync("redis-cli KEYS 'otp:code:*'", { encoding: "utf8" }) - .trim().split("\n").filter(Boolean); - for (const k of keys) { - const v = execSync(`redis-cli GET "${k}"`, { encoding: "utf8" }).trim(); - if (v === userId) { - otpCode = k.replace("otp:code:", ""); - return otpCode; - } - } - return null; - } catch { - return null; - } -} - async function registerUser(user: TestUser): Promise { console.log(`\n📝 Registering ${user.intent} via API...`); - const regResponse = await fetch("http://localhost:9100/api/auth/register", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(user), + const regData = await apiRegister({ + email: user.email, + password: user.password, + first_name: user.firstName, + last_name: user.lastName, + intent: user.intent, }); - const regData = await regResponse.json(); if (!regData.user_id) throw new Error(`Registration failed: ${JSON.stringify(regData)}`); user.userId = regData.user_id; console.log(` ✅ Registered, user_id: ${user.userId}`); - // Get OTP from Redis + // Get OTP from Redis + verify via API await new Promise(r => setTimeout(r, 500)); - const otpCode = await getOTPFromRedis(user.userId!); - if (!otpCode) throw new Error("Could not get OTP from Redis"); - console.log(` ✅ OTP retrieved: ${otpCode}`); - - // Verify OTP - const verifyResponse = await fetch("http://localhost:9100/api/auth/verify-email", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: user.userId, otp: otpCode }) - }); - if (!verifyResponse.ok) throw new Error("OTP verification failed"); + const verified = await apiVerifyEmail(user.email, user.userId!); + if (!verified) throw new Error("OTP verification failed"); console.log(` ✅ OTP verified!`); // Login - const loginResponse = await fetch("http://localhost:9100/api/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email: user.email, password: user.password }) - }); - const loginData = await loginResponse.json(); - if (!loginData.access_token) throw new Error("Login failed"); - user.accessToken = loginData.access_token; + const loginData = await apiLogin(user.email, user.password); + const accessToken = loginData?.access_token || ""; + if (!accessToken) throw new Error("Login failed"); + user.accessToken = accessToken; console.log(` ✅ Logged in, token length: ${user.accessToken!.length}`); return user; @@ -139,8 +108,8 @@ test.describe("Full Company + Job Seeker E2E with Admin Verification", () => { await registerUser(jobSeekerUser); // ==================== BROWSER SETUP ==================== - const browser = await chromium.launch({ headless: false, slowMo: 30 }); - const context = await browser.newContext({ viewport: { width: 1400, height: 900 } }); + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext({ baseURL: APP_BASE_URL, viewport: { width: 1400, height: 900 } }); // ==================== COMPANY FRONTEND FLOW ==================== console.log("\n" + "=".repeat(60)); @@ -150,7 +119,7 @@ test.describe("Full Company + Job Seeker E2E with Admin Verification", () => { const companyPage = await context.newPage(); await setupFrontendAuth(companyPage, companyUser); - await companyPage.goto("http://localhost:3000/dashboard?role=COMPANY", { waitUntil: "networkidle", timeout: 15000 }); + await companyPage.goto("/dashboard?role=COMPANY", { waitUntil: "networkidle", timeout: 15000 }); await new Promise(r => setTimeout(r, 3000)); await takeScreenshot(companyPage, "01_company_dashboard"); console.log(" ✅ Company dashboard loaded"); @@ -204,7 +173,7 @@ test.describe("Full Company + Job Seeker E2E with Admin Verification", () => { const jsPage = await context.newPage(); await setupFrontendAuth(jsPage, jobSeekerUser); - await jsPage.goto("http://localhost:3000/dashboard?role=JOB_SEEKER", { waitUntil: "networkidle", timeout: 15000 }); + await jsPage.goto("/dashboard?role=JOB_SEEKER", { waitUntil: "networkidle", timeout: 15000 }); await new Promise(r => setTimeout(r, 3000)); await takeScreenshot(jsPage, "04_jobseeker_dashboard"); console.log(" ✅ Job seeker dashboard loaded"); diff --git a/tests/e2e/guardrails.spec.ts b/tests/e2e/guardrails.spec.ts index 325f91c..1006700 100644 --- a/tests/e2e/guardrails.spec.ts +++ b/tests/e2e/guardrails.spec.ts @@ -1,6 +1,6 @@ import { test, expect, request } from "@playwright/test"; - -const API_BASE = "http://localhost:3000/api"; +import { apiLogin } from "./helpers/auth-flow"; +import { API_BASE } from "./helpers/env"; const PHONE_PATTERNS = [ /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/, @@ -33,29 +33,13 @@ function hasContactInfo(text: string): boolean { } async function getCompanyToken(): Promise { - const ctx = await request.newContext(); - const res = await ctx.post(`${API_BASE}/auth/login`, { - data: { - email: "testcompany@example.com", - password: "TestPassword123!", - }, - }); - if (!res.ok()) return null; - const data = await res.json(); - return data.access_token || null; + const loginData = await apiLogin("testcompany@example.com", "TestPassword123!"); + return loginData?.access_token || null; } async function getJobSeekerToken(): Promise { - const ctx = await request.newContext(); - const res = await ctx.post(`${API_BASE}/auth/login`, { - data: { - email: "testtutora2026@example.com", - password: "Test1234!", - }, - }); - if (!res.ok()) return null; - const data = await res.json(); - return data.access_token || null; + const loginData = await apiLogin("testtutora2026@example.com", "Test1234!"); + return loginData?.access_token || null; } test.describe("Guard Rails - AI Content Safety", () => { diff --git a/tests/e2e/helpers/auth-flow.ts b/tests/e2e/helpers/auth-flow.ts new file mode 100644 index 0000000..403fd03 --- /dev/null +++ b/tests/e2e/helpers/auth-flow.ts @@ -0,0 +1,65 @@ +import { API_BASE } from "./env"; +import { solveCaptcha } from "./captcha"; +import { getOtpFromRedis } from "./otp"; + +export interface RegisterInput { + email: string; + password: string; + first_name?: string; + last_name?: string; + phone?: string; + intent?: string; + profession?: string; +} + +/** POST /api/auth/register (captcha-solved automatically). */ +export async function apiRegister(input: RegisterInput): Promise<{ user_id: string; [k: string]: any }> { + const { captcha_id, captcha_answer } = await solveCaptcha(); + const res = await fetch(`${API_BASE}/auth/register`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ ...input, captcha_id, captcha_answer }), + }); + const data = await res.json(); + if (!res.ok) { + throw new Error(`Register failed (${res.status}): ${JSON.stringify(data)}`); + } + return data; +} + +/** POST /api/auth/verify-email, sourcing the OTP straight from Redis. */ +export async function apiVerifyEmail(email: string, userId: string): Promise { + const otp = await getOtpFromRedis(userId); + if (!otp) { + console.log(`⚠️ Could not retrieve OTP from Redis for user ${userId}`); + return false; + } + const res = await fetch(`${API_BASE}/auth/verify-email`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ otp, email }), + }); + return res.ok; +} + +/** POST /api/auth/login (captcha-solved automatically). Returns null on non-2xx. */ +export async function apiLogin(email: string, password: string): Promise<{ access_token: string; [k: string]: any } | null> { + const { captcha_id, captcha_answer } = await solveCaptcha(); + const res = await fetch(`${API_BASE}/auth/login`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email, password, captcha_id, captcha_answer }), + }); + if (!res.ok) return null; + return res.json(); +} + +/** Full register → verify → login convenience flow, returning the access token. */ +export async function registerVerifyLogin(input: RegisterInput): Promise<{ user_id: string; access_token: string }> { + const regData = await apiRegister(input); + const verified = await apiVerifyEmail(input.email, regData.user_id); + if (!verified) throw new Error(`Email verification failed for ${input.email}`); + const loginData = await apiLogin(input.email, input.password); + if (!loginData) throw new Error(`Login failed for ${input.email} after verification`); + return { user_id: regData.user_id, access_token: loginData.access_token }; +} diff --git a/tests/e2e/helpers/captcha.ts b/tests/e2e/helpers/captcha.ts new file mode 100644 index 0000000..734cd2f --- /dev/null +++ b/tests/e2e/helpers/captcha.ts @@ -0,0 +1,28 @@ +import { API_BASE } from "./env"; + +/** + * Fetches a fresh math captcha from the API and solves it. + * Captchas are single-use (Redis-backed) — call this immediately before the + * request that needs it, never cache/reuse a solved captcha_id. + */ +export async function solveCaptcha(): Promise<{ captcha_id: string; captcha_answer: string }> { + const res = await fetch(`${API_BASE}/auth/captcha`, { method: "POST" }); + if (!res.ok) { + throw new Error(`Failed to fetch captcha: ${res.status} ${await res.text()}`); + } + const { captcha_id, challenge } = await res.json(); + + // Challenge format observed from the API: "N + N = ?" / "N - N = ?" (also + // tolerate "*" just in case). Keep this in sync with + // apps/users/src/handlers/auth.rs's captcha generator. + const match = String(challenge).match(/(-?\d+)\s*([+\-*])\s*(-?\d+)/); + if (!match) { + throw new Error(`Unrecognized captcha challenge format: "${challenge}"`); + } + const [, aStr, op, bStr] = match; + const a = Number(aStr); + const b = Number(bStr); + const answer = op === "+" ? a + b : op === "-" ? a - b : a * b; + + return { captcha_id, captcha_answer: String(answer) }; +} diff --git a/tests/e2e/helpers/env.ts b/tests/e2e/helpers/env.ts new file mode 100644 index 0000000..cf379a2 --- /dev/null +++ b/tests/e2e/helpers/env.ts @@ -0,0 +1,11 @@ +/** + * Single source of truth for which environment the e2e suite targets. + * Mirrors the same TEST_ENV=production condition used in playwright.config.ts's + * `use.baseURL` — keep these in sync. + */ +export const APP_BASE_URL = + process.env.TEST_ENV === "production" + ? "https://test111.nxtgauge.com" + : "http://localhost:3000"; + +export const API_BASE = `${APP_BASE_URL}/api`; diff --git a/tests/e2e/helpers/otp.ts b/tests/e2e/helpers/otp.ts new file mode 100644 index 0000000..dbf57df --- /dev/null +++ b/tests/e2e/helpers/otp.ts @@ -0,0 +1,42 @@ +import { execFileSync } from "child_process"; + +/** + * Reads a registration OTP straight out of Redis, the same way a human would + * never get to (the API deliberately never returns it — see + * apps/users/src/handlers/auth.rs's RegisterResponse.otp, which is only + * populated for the DEMO_ACCOUNT_EMAILS allowlist, unset in this environment). + * + * This only works when the machine running the tests has `kubectl` access to + * the cluster (redis-master-0 in the `data` namespace) — i.e. run from the + * same host used for the rest of the live-server runbook, not arbitrary CI. + * Auths via the redis pod's own mounted password file rather than a secret + * value passed on our end, matching how docs/LIVE_SERVER_RUNBOOK.md's manual + * verification steps do it. + */ +export async function getOtpFromRedis(userId: string): Promise { + try { + const out = execFileSync( + "kubectl", + [ + "-n", + "data", + "exec", + "redis-master-0", + "--", + "sh", + "-c", + 'redis-cli -a "$(cat /opt/bitnami/redis/secrets/redis-password)" GET "otp:plain:$1"', + "--", // end of sh -c's own options, "$1" below binds to this + userId, + ], + { encoding: "utf8" } + ).trim(); + + // redis-cli prints "(nil)" for a missing key rather than exiting non-zero. + if (!out || out === "(nil)" || !/^\d{4,}$/.test(out)) return null; + return out; + } catch (e: any) { + console.log("⚠️ Could not read OTP from Redis via kubectl:", e.message); + return null; + } +} diff --git a/tests/e2e/job-seeker-complete.spec.ts b/tests/e2e/job-seeker-complete.spec.ts index f93b67b..1c1a676 100644 --- a/tests/e2e/job-seeker-complete.spec.ts +++ b/tests/e2e/job-seeker-complete.spec.ts @@ -1,29 +1,9 @@ -import { test, expect, chromium } from "@playwright/test"; +import { test, expect } from "@playwright/test"; import { randomUUID } from "crypto"; -import { execSync } from "child_process"; +import * as path from "path"; +import { apiRegister, apiVerifyEmail, apiLogin } from "./helpers/auth-flow"; -const SCREENSHOT_DIR = "/Users/ashwin/workspace/nxtgauge-frontend-solid/test-results/job-seeker-complete"; - -async function getOTPFromRedis(userId: string): Promise { - try { - const plainKey = `otp:plain:${userId}`; - let otpCode = execSync(`redis-cli GET "${plainKey}"`, { encoding: "utf8" }).trim(); - if (otpCode && otpCode.length >= 4) return otpCode; - - const keys = execSync("redis-cli KEYS 'otp:code:*'", { encoding: "utf8" }) - .trim().split("\n").filter(Boolean); - for (const k of keys) { - const v = execSync(`redis-cli GET "${k}"`, { encoding: "utf8" }).trim(); - if (v === userId) { - otpCode = k.replace("otp:code:", ""); - return otpCode; - } - } - return null; - } catch { - return null; - } -} +const SCREENSHOT_DIR = path.join(__dirname, "..", "..", "test-results", "job-seeker-complete"); test.describe("Job Seeker E2E Complete Flow", () => { test.beforeEach(async ({ page }) => { @@ -40,95 +20,74 @@ test.describe("Job Seeker E2E Complete Flow", () => { // ==================== STEP 1: REGISTER VIA API ==================== console.log("\n📝 STEP 1: Register via API"); - let regData: any; - const regResponse = await fetch("http://localhost:9100/api/auth/register", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - email: testEmail, - first_name: "Jane", - last_name: "Smith", - password: testPassword, - intent: "job_seeker" - }) + const regData = await apiRegister({ + email: testEmail, + first_name: "Jane", + last_name: "Smith", + password: testPassword, + intent: "job_seeker" }); - regData = await regResponse.json(); expect(regData.user_id).toBeTruthy(); console.log(" ✅ Registration successful, user_id:", regData.user_id); - // ==================== STEP 2: OTP VIA REDIS ==================== - console.log("\n🔐 STEP 2: Get OTP via Redis"); + // ==================== STEP 2+3: OTP VIA REDIS + VERIFY VIA API ==================== + console.log("\n🔐 STEP 2+3: Get OTP via Redis and verify via API"); await new Promise(r => setTimeout(r, 500)); - let otpCode = await getOTPFromRedis(regData.user_id); - expect(otpCode).toBeTruthy(); - console.log(" ✅ OTP retrieved:", otpCode); - - // ==================== STEP 3: VERIFY OTP VIA API ==================== - console.log("\n✅ STEP 3: Verify OTP via API"); - const verifyResponse = await fetch("http://localhost:9100/api/auth/verify-email", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: regData.user_id, otp: otpCode }) - }); - expect(verifyResponse.ok).toBe(true); + const verified = await apiVerifyEmail(testEmail, regData.user_id); + expect(verified).toBe(true); console.log(" ✅ OTP verified!"); // ==================== STEP 4: LOGIN VIA API ==================== console.log("\n🔑 STEP 4: Login via API"); - const loginResponse = await fetch("http://localhost:9100/api/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email: testEmail, password: testPassword }) - }); - const loginData = await loginResponse.json(); - const accessToken = loginData.access_token; + const loginData = await apiLogin(testEmail, testPassword); + const accessToken = loginData?.access_token || ""; expect(accessToken).toBeTruthy(); console.log(" ✅ Login successful, token length:", accessToken.length); // ==================== STEP 5: DASHBOARD ==================== console.log("\n🌐 STEP 5: Navigate to dashboard"); - + // Seed sessionStorage and localStorage with auth data (auth.tsx uses sessionStorage for token) await page.addInitScript(({ token, email, userId }) => { // auth.tsx getToken() reads from sessionStorage sessionStorage.setItem("nxtgauge_access_token", token); - + // localStorage for user data (used by various components) localStorage.setItem("nxtgauge_user", JSON.stringify({ - email, - roleKey: "JOB_SEEKER", - role: "JOB_SEEKER", + email, + roleKey: "JOB_SEEKER", + role: "JOB_SEEKER", active_role: "JOB_SEEKER", - selectedProfessionalRole: "JOB_SEEKER", - name: "Jane Smith", - fullName: "Jane Smith", + selectedProfessionalRole: "JOB_SEEKER", + name: "Jane Smith", + fullName: "Jane Smith", id: userId })); localStorage.setItem("nxtgauge_auth_user", JSON.stringify({ - email, - roleKey: "JOB_SEEKER", - role: "JOB_SEEKER", + email, + roleKey: "JOB_SEEKER", + role: "JOB_SEEKER", active_role: "JOB_SEEKER", - selectedProfessionalRole: "JOB_SEEKER", - name: "Jane Smith", - fullName: "Jane Smith", + selectedProfessionalRole: "JOB_SEEKER", + name: "Jane Smith", + fullName: "Jane Smith", id: userId })); localStorage.setItem("nxtgauge_signup_profile_v1", JSON.stringify({ - email, - roleKey: "JOB_SEEKER", - role: "JOB_SEEKER", + email, + roleKey: "JOB_SEEKER", + role: "JOB_SEEKER", active_role: "JOB_SEEKER", - selectedProfessionalRole: "JOB_SEEKER", - name: "Jane Smith", - fullName: "Jane Smith", + selectedProfessionalRole: "JOB_SEEKER", + name: "Jane Smith", + fullName: "Jane Smith", id: userId })); }, { token: accessToken, email: testEmail, userId: regData.user_id }); - await page.goto("http://localhost:3000/dashboard?role=JOB_SEEKER", { waitUntil: "networkidle", timeout: 15000 }); + await page.goto("/dashboard?role=JOB_SEEKER", { waitUntil: "networkidle", timeout: 15000 }); await page.waitForTimeout(2000); - + // Check dashboard loaded - URL should not redirect to login const currentUrl = page.url(); expect(currentUrl).not.toContain("/login"); @@ -136,21 +95,21 @@ test.describe("Job Seeker E2E Complete Flow", () => { // ==================== STEP 6: PROFILE FORM ==================== console.log("\n📋 STEP 6: Navigate to profile"); - + // Click My Profile button const profileBtn = page.getByRole("button", { name: /my profile/i }); if (await profileBtn.isVisible().catch(() => false)) { await profileBtn.click(); await page.waitForTimeout(2000); } else { - await page.goto("http://localhost:3000/dashboard/profile?role=JOB_SEEKER", { waitUntil: "networkidle" }); + await page.goto("/dashboard/profile?role=JOB_SEEKER", { waitUntil: "networkidle" }); await page.waitForTimeout(2000); } console.log(" ✅ Profile page displayed"); // ==================== STEP 6b: FILL PROFILE FORM ==================== console.log("\n📝 STEP 6b: Fill job seeker profile"); - + // Fill basic fields using label selectors since inputs have no name/id const fieldMappings: Record = { "First Name": "Jane", @@ -187,7 +146,7 @@ test.describe("Job Seeker E2E Complete Flow", () => { // ==================== STEP 7: DOCUMENTS TAB ==================== console.log("\n📄 STEP 7: Upload documents"); - + // Switch to Documents tab const docsTab = page.getByRole("button", { name: /documents/i }); if (await docsTab.isVisible().catch(() => false)) { @@ -201,15 +160,15 @@ test.describe("Job Seeker E2E Complete Flow", () => { // ==================== STEP 8: SUBMIT FOR VERIFICATION ==================== console.log("\n📤 STEP 8: Submit for verification"); - + const submitBtn = page.getByRole("button", { name: /submit for verification/i }); - + if (await submitBtn.isVisible({ timeout: 2000 }).catch(() => false)) { const isDisabled = await submitBtn.isDisabled().catch(() => true); - + if (isDisabled) { console.log(" ⚠️ Submit button disabled - checking what's missing"); - + // Check for missing fields message const bodyText = await page.locator("body").innerText(); if (bodyText.includes("required") || bodyText.includes("missing")) { @@ -218,7 +177,7 @@ test.describe("Job Seeker E2E Complete Flow", () => { } else { await submitBtn.click(); await page.waitForTimeout(3000); - + // Check for success message const bodyText = await page.locator("body").innerText(); if (bodyText.includes("Submitted") || bodyText.includes("success")) { diff --git a/tests/e2e/job-seeker-e2e-full.spec.ts b/tests/e2e/job-seeker-e2e-full.spec.ts index 472e6f9..beb4be3 100644 --- a/tests/e2e/job-seeker-e2e-full.spec.ts +++ b/tests/e2e/job-seeker-e2e-full.spec.ts @@ -1,35 +1,16 @@ import { test, expect, chromium } from "@playwright/test"; import { randomUUID } from "crypto"; -import { execSync } from "child_process"; import * as fs from "fs"; +import * as path from "path"; +import { apiRegister, apiVerifyEmail, apiLogin } from "./helpers/auth-flow"; +import { APP_BASE_URL } from "./helpers/env"; -const SCREENSHOT_DIR = "/Users/ashwin/workspace/nxtgauge-frontend-solid/test-results/job-seeker-e2e"; +const SCREENSHOT_DIR = path.join(__dirname, "..", "..", "test-results", "job-seeker-e2e"); if (!fs.existsSync(SCREENSHOT_DIR)) { fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); } -async function getOTPFromRedis(userId: string): Promise { - try { - const plainKey = `otp:plain:${userId}`; - let otpCode = execSync(`redis-cli GET "${plainKey}"`, { encoding: "utf8" }).trim(); - if (otpCode && otpCode.length >= 4) return otpCode; - - const keys = execSync("redis-cli KEYS 'otp:code:*'", { encoding: "utf8" }) - .trim().split("\n").filter(Boolean); - for (const k of keys) { - const v = execSync(`redis-cli GET "${k}"`, { encoding: "utf8" }).trim(); - if (v === userId) { - otpCode = k.replace("otp:code:", ""); - return otpCode; - } - } - return null; - } catch { - return null; - } -} - test.describe("Job Seeker E2E Full Flow", () => { test("complete job seeker registration → OTP → login → dashboard → profile → verification", async () => { const testEmail = `e2ejobseeker${randomUUID().slice(0, 8)}@test.com`; @@ -37,19 +18,14 @@ test.describe("Job Seeker E2E Full Flow", () => { console.log("📧 Email:", testEmail); - const browser = await chromium.launch({ headless: false, slowMo: 30 }); - const context = await browser.newContext({ viewport: { width: 1400, height: 900 } }); + const browser = await chromium.launch({ headless: true }); + const context = await browser.newContext({ baseURL: APP_BASE_URL, viewport: { width: 1400, height: 900 } }); // ==================== STEP 1: REGISTER VIA API ==================== console.log("\n📝 STEP 1: Register via API"); let regData: any; try { - const regResponse = await fetch("http://localhost:9100/api/auth/register", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email: testEmail, first_name: "Jane", last_name: "Smith", password: testPassword, intent: "job_seeker" }) - }); - regData = await regResponse.json(); + regData = await apiRegister({ email: testEmail, first_name: "Jane", last_name: "Smith", password: testPassword, intent: "job_seeker" }); expect(regData.user_id).toBeTruthy(); console.log(" ✅ PASS: Registration successful, user_id:", regData.user_id); } catch (e: any) { @@ -57,30 +33,13 @@ test.describe("Job Seeker E2E Full Flow", () => { throw e; } - // ==================== STEP 2: OTP VIA REDIS ==================== - console.log("\n🔐 STEP 2: OTP via Redis"); + // ==================== STEP 2+3: OTP VIA REDIS + VERIFY VIA API ==================== + console.log("\n🔐 STEP 2+3: OTP via Redis, verify via API"); await new Promise(r => setTimeout(r, 500)); - let otpCode: string | null = null; try { - otpCode = await getOTPFromRedis(regData.user_id); - expect(otpCode).toBeTruthy(); - console.log(" ✅ PASS: OTP retrieved from Redis:", otpCode); - } catch (e: any) { - console.log(" ❌ FAIL: Could not get OTP -", e.message); - throw e; - } - - // ==================== STEP 3: VERIFY OTP VIA API ==================== - console.log("\n✅ STEP 3: Verify OTP via API"); - try { - const verifyResponse = await fetch("http://localhost:9100/api/auth/verify-email", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ user_id: regData.user_id, otp: otpCode }) - }); - const verifyData = await verifyResponse.json(); - expect(verifyResponse.ok).toBe(true); - console.log(" ✅ PASS: OTP verified! Response:", JSON.stringify(verifyData)); + const verified = await apiVerifyEmail(testEmail, regData.user_id); + expect(verified).toBe(true); + console.log(" ✅ PASS: OTP retrieved and verified!"); } catch (e: any) { console.log(" ❌ FAIL: OTP verification failed -", e.message); throw e; @@ -90,13 +49,8 @@ test.describe("Job Seeker E2E Full Flow", () => { console.log("\n🔑 STEP 4: Login via API"); let accessToken = ""; try { - const loginResponse = await fetch("http://localhost:9100/api/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email: testEmail, password: testPassword }) - }); - const loginData = await loginResponse.json(); - accessToken = loginData.access_token || ""; + const loginData = await apiLogin(testEmail, testPassword); + accessToken = loginData?.access_token || ""; expect(accessToken).toBeTruthy(); console.log(" ✅ PASS: Login successful, token length:", accessToken.length); } catch (e: any) { @@ -107,7 +61,7 @@ test.describe("Job Seeker E2E Full Flow", () => { // ==================== STEP 5: DASHBOARD ==================== console.log("\n🌐 STEP 5: Navigate to dashboard?role=JOB_SEEKER"); const page = await context.newPage(); - + await page.addInitScript(({ token, email, userId }) => { localStorage.setItem("nxtgauge_access_token", token); localStorage.setItem("nxtgauge_user", JSON.stringify({ @@ -122,7 +76,7 @@ test.describe("Job Seeker E2E Full Flow", () => { }, { token: accessToken, email: testEmail, userId: regData.user_id }); try { - await page.goto("http://localhost:3000/dashboard?role=JOB_SEEKER", { waitUntil: "networkidle", timeout: 15000 }); + await page.goto("/dashboard?role=JOB_SEEKER", { waitUntil: "networkidle", timeout: 15000 }); await page.waitForTimeout(3000); await page.screenshot({ path: `${SCREENSHOT_DIR}/step05_dashboard.png`, fullPage: true }); console.log(" ✅ PASS: Dashboard loaded"); @@ -142,7 +96,7 @@ test.describe("Job Seeker E2E Full Flow", () => { await page.waitForTimeout(3000); } else { console.log(" ⚠️ My Profile button not visible, trying direct navigation"); - await page.goto("http://localhost:3000/dashboard/profile?role=JOB_SEEKER", { waitUntil: "networkidle" }); + await page.goto("/dashboard/profile?role=JOB_SEEKER", { waitUntil: "networkidle" }); await page.waitForTimeout(3000); } await page.screenshot({ path: `${SCREENSHOT_DIR}/step06_profile_form.png`, fullPage: true }); @@ -158,7 +112,7 @@ test.describe("Job Seeker E2E Full Flow", () => { const inputs = page.locator("input"); const count = await inputs.count(); console.log(" Total inputs found:", count); - + // Log all inputs with their attributes for (let i = 0; i < Math.min(count, 15); i++) { const input = inputs.nth(i); @@ -172,30 +126,30 @@ test.describe("Job Seeker E2E Full Flow", () => { console.log(` Input ${i}: type="${type}" placeholder="${placeholder}" id="${id}" name="${name}"`); } } - + // Try to find and fill common job seeker fields const nameFields = ["first_name", "firstName", "First Name", "first-name"]; const lastNameFields = ["last_name", "lastName", "Last Name", "last-name"]; const emailField = page.locator('input[name="email"], input[id="email"]').first(); const phoneField = page.locator('input[name="phone"], input[id="phone"], input[placeholder*="phone" i]').first(); const locationField = page.locator('input[name="location"], input[id="location"], input[placeholder*="location" i]').first(); - + if (await emailField.isVisible().catch(() => false)) { console.log(" ℹ️ Email field already has:", await emailField.inputValue().catch(() => "")); } - + // Fill phone if (await phoneField.isVisible().catch(() => false)) { await phoneField.fill("9876543210"); console.log(" ✅ Filled phone"); } - + // Fill location if (await locationField.isVisible().catch(() => false)) { await locationField.fill("Chennai"); console.log(" ✅ Filled location"); } - + // Try textareas too const textareas = page.locator("textarea"); const textareaCount = await textareas.count(); @@ -209,7 +163,7 @@ test.describe("Job Seeker E2E Full Flow", () => { console.log(` Textarea ${i}: placeholder="${placeholder}" name="${name}"`); } } - + await page.screenshot({ path: `${SCREENSHOT_DIR}/step06b_profile_inputs.png`, fullPage: true }); console.log(" ✅ Profile form analyzed"); } catch (e: any) { @@ -222,13 +176,13 @@ test.describe("Job Seeker E2E Full Flow", () => { try { const submitBtn = page.getByRole("button", { name: /submit for verification/i }); const btnVisible = await submitBtn.isVisible().catch(() => false); - + if (btnVisible) { const isDisabled = await submitBtn.isDisabled().catch(() => true); if (isDisabled) { console.log(" ⚠️ INFO: Submit button disabled - profile needs more fields filled"); await page.screenshot({ path: `${SCREENSHOT_DIR}/step07_submit_disabled.png`, fullPage: true }); - + // Try Documents tab const docsTab = page.getByRole("tab", { name: /documents/i }).first(); if (await docsTab.isVisible().catch(() => false)) { diff --git a/tests/e2e/security.spec.ts b/tests/e2e/security.spec.ts index d0b4fc2..52afb88 100644 --- a/tests/e2e/security.spec.ts +++ b/tests/e2e/security.spec.ts @@ -1,14 +1,18 @@ import { test, expect, request } from "@playwright/test"; +import { solveCaptcha } from "./helpers/captcha"; const API_BASE = "https://test111.nxtgauge.com/api"; test.describe("Security - Authentication", () => { test("JWT token is not returned for invalid credentials", async () => { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "nonexistent@example.com", password: "wrongpassword", + captcha_id, + captcha_answer, }, }); @@ -19,9 +23,12 @@ test.describe("Security - Authentication", () => { test("Login without email returns proper error", async () => { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { password: "somepassword", + captcha_id, + captcha_answer, }, }); @@ -30,9 +37,12 @@ test.describe("Security - Authentication", () => { test("Login without password returns proper error", async () => { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "test@example.com", + captcha_id, + captcha_answer, }, }); @@ -76,10 +86,13 @@ test.describe("Security - Rate Limiting", () => { let rateLimited = false; for (let i = 0; i < 5; i++) { + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: uniqueEmail, password: "wrongpassword", + captcha_id, + captcha_answer, }, }); if (res.status() === 429) { @@ -89,10 +102,13 @@ test.describe("Security - Rate Limiting", () => { } if (!rateLimited) { + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: uniqueEmail, password: "anypassword", + captcha_id, + captcha_answer, }, }); if (res.status() === 429) { @@ -107,10 +123,13 @@ test.describe("Security - Rate Limiting", () => { test("AI endpoints rate limit after daily quota exceeded", async () => { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const loginRes = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "testcompany@example.com", password: "TestPassword123!", + captcha_id, + captcha_answer, }, }); @@ -148,10 +167,13 @@ test.describe("Security - Rate Limiting", () => { test.describe("Security - Input Validation", () => { test("SQL injection in login email is handled safely", async () => { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "' OR '1'='1", password: "anything", + captcha_id, + captcha_answer, }, }); @@ -162,10 +184,13 @@ test.describe("Security - Input Validation", () => { test("XSS attempt in login email is handled safely", async () => { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "@example.com", password: "password", + captcha_id, + captcha_answer, }, }); @@ -175,11 +200,14 @@ test.describe("Security - Input Validation", () => { test("Very long input is handled without crash", async () => { const ctx = await request.newContext(); const longString = "a".repeat(10000); + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: `${longString}@example.com`, password: longString, + captcha_id, + captcha_answer, }, }); @@ -199,10 +227,13 @@ test.describe("Security - CORS Headers", () => { test.describe("Security - Authorization", () => { test("User cannot access admin endpoints with regular user token", async () => { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const loginRes = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "testtutora2026@example.com", password: "Test1234!", + captcha_id, + captcha_answer, }, }); @@ -229,11 +260,14 @@ test.describe("Security - Authorization", () => { test.describe("Security - Response Headers", () => { test("API does not leak sensitive information in error responses", async () => { const ctx = await request.newContext(); + const { captcha_id, captcha_answer } = await solveCaptcha(); const res = await ctx.post(`${API_BASE}/auth/login`, { data: { email: "test@example.com", password: "wrongpassword", + captcha_id, + captcha_answer, }, }); diff --git a/tests/e2e/signup-verification-submission.spec.ts b/tests/e2e/signup-verification-submission.spec.ts index 889adef..9f239aa 100644 --- a/tests/e2e/signup-verification-submission.spec.ts +++ b/tests/e2e/signup-verification-submission.spec.ts @@ -183,7 +183,7 @@ async function wireApiMock( } async function runSignup(page: any, scenario: SignupScenario, email: string) { - await page.goto(`http://localhost:3000${scenario.signupUrl}`); + await page.goto(scenario.signupUrl); await page.fill("#first-name", "Ari"); await page.fill("#last-name", "Tester"); @@ -271,7 +271,7 @@ test.describe("Signup and verification submission by role", () => { await runSignup(page, scenario, email); await seedSession(page, scenario, email); - await page.goto(`http://localhost:3000/dashboard?role=${scenario.dashboardRole}`); + await page.goto(`/dashboard?role=${scenario.dashboardRole}`); await page.getByRole("button", { name: /my profile/i }).click(); const submitButton = page.getByRole("button", { name: /submit for verification/i }); @@ -292,7 +292,7 @@ test.describe("Signup and verification submission by role", () => { await runSignup(page, scenario, email); await seedSession(page, scenario, email); - await page.goto(`http://localhost:3000/dashboard?role=${scenario.dashboardRole}`); + await page.goto(`/dashboard?role=${scenario.dashboardRole}`); await page.getByRole("button", { name: /my profile/i }).click(); const submitButton = page.getByRole("button", { name: /submit for verification/i }); diff --git a/tests/e2e/visual/dashboard-role.spec.ts b/tests/e2e/visual/dashboard-role.spec.ts index 259e54a..9818a80 100644 --- a/tests/e2e/visual/dashboard-role.spec.ts +++ b/tests/e2e/visual/dashboard-role.spec.ts @@ -1,7 +1,7 @@ import { test, expect, Page } from "@playwright/test"; async function loginViaApi(page: Page, email: string, password: string): Promise<{ access_token: string; active_role: string }> { - const res = await page.request.post("http://localhost:3000/api/auth/login", { + const res = await page.request.post("/api/auth/login", { data: { email, password }, headers: { "Content-Type": "application/json", Accept: "application/json" }, }); @@ -17,7 +17,7 @@ async function loginViaApi(page: Page, email: string, password: string): Promise } async function injectAuth(page: Page, email: string, token: string, role: string) { - await page.goto("http://localhost:3000/"); + await page.goto("/"); await page.evaluate( ({ email, token, role }) => { const payload = { @@ -54,7 +54,7 @@ test.describe("Dashboard Role Resolution", () => { await injectAuth(page, email, access_token, active_role); // Step 3: Navigate to dashboard with role param - await page.goto(`http://localhost:3000/dashboard?role=${active_role}`); + await page.goto(`/dashboard?role=${active_role}`); await page.waitForLoadState("networkidle"); await page.waitForTimeout(3000); // Wait for effects and bundle load @@ -87,7 +87,7 @@ test.describe("Dashboard Role Resolution", () => { const password = "Test1234!"; // First check if this email has PHOTOGRAPHER role - const checkRes = await page.request.post("http://localhost:3000/api/auth/check-email", { + const checkRes = await page.request.post("/api/auth/check-email", { data: { email }, headers: { "Content-Type": "application/json", Accept: "application/json" }, }); @@ -112,14 +112,14 @@ test.describe("Dashboard Role Resolution", () => { } catch { console.log(`[Login] ${email} login failed, trying API login with different approach`); // Try without password for discovery - const checkData = await (await page.request.post("http://localhost:3000/api/auth/check-email", { + const checkData = await (await page.request.post("/api/auth/check-email", { data: { email }, headers: { "Content-Type": "application/json", Accept: "application/json" }, })).json(); console.log(`[Check Email Result]:`, checkData); // If we can't login, at least verify the role detection - await page.goto("http://localhost:3000/login"); + await page.goto("/login"); await page.fill("#login-email", email); await page.waitForTimeout(500); await page.screenshot({ path: "test-results/login-email-check.png" }); @@ -130,7 +130,7 @@ test.describe("Dashboard Role Resolution", () => { await injectAuth(page, email, token, targetRole); - await page.goto(`http://localhost:3000/dashboard?role=${targetRole}`); + await page.goto(`/dashboard?role=${targetRole}`); await page.waitForLoadState("networkidle"); await page.waitForTimeout(3000); @@ -155,7 +155,7 @@ test.describe("Dashboard Role Resolution", () => { test("direct dashboard navigation with role param should show correct role", async ({ page }) => { // This test just verifies the URL param is respected - await page.goto("http://localhost:3000/dashboard?role=PHOTOGRAPHER"); + await page.goto("/dashboard?role=PHOTOGRAPHER"); await page.waitForLoadState("networkidle"); await page.waitForTimeout(2000); diff --git a/tests/e2e/visual/debug-dashboard.spec.ts b/tests/e2e/visual/debug-dashboard.spec.ts index 393533c..06825a8 100644 --- a/tests/e2e/visual/debug-dashboard.spec.ts +++ b/tests/e2e/visual/debug-dashboard.spec.ts @@ -5,7 +5,7 @@ test("API login shows correct TUTOR dashboard", async ({ page }) => { page.on("pageerror", (err) => errors.push(err.message)); // Login via API - const loginRes = await page.request.post("http://localhost:3000/api/auth/login", { + const loginRes = await page.request.post("/api/auth/login", { data: { email: "testtutora2026@example.com", password: "Test1234!" }, headers: { "Content-Type": "application/json", Accept: "application/json" }, }); @@ -14,7 +14,7 @@ test("API login shows correct TUTOR dashboard", async ({ page }) => { const role = loginData.user?.active_role || "TUTOR"; // Inject auth - await page.goto("http://localhost:3000/"); + await page.goto("/"); await page.waitForLoadState("networkidle"); await page.evaluate( ({ token, role }) => { @@ -37,7 +37,7 @@ test("API login shows correct TUTOR dashboard", async ({ page }) => { ); // Navigate to dashboard - await page.goto(`http://localhost:3000/dashboard?role=${role}`); + await page.goto(`/dashboard?role=${role}`); await page.waitForLoadState("domcontentloaded"); await page.waitForTimeout(3000); await page.screenshot({ path: "test-results/dashboard-tutor-check.png", fullPage: true });