First Commit
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
# 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: application.spec.js >> Forms Validation and Error Messages >> Temperature field accepts only 0.0-1.0 range
|
||||
- Location: tests/e2e/application.spec.js:543:3
|
||||
|
||||
# Error details
|
||||
|
||||
```
|
||||
Error: expect(locator).toBeVisible() failed
|
||||
|
||||
Locator: locator('.text-danger')
|
||||
Expected: visible
|
||||
Error: strict mode violation: locator('.text-danger') resolved to 2 elements:
|
||||
1) <div class="text-danger small">The title field is required.</div> aka getByText('The title field is required.')
|
||||
2) <div class="text-danger small">The temperature field must not be greater than 1.</div> aka getByText('The temperature field must')
|
||||
|
||||
Call log:
|
||||
- Expect "toBeVisible" with timeout 10000ms
|
||||
- waiting for locator('.text-danger')
|
||||
- waiting for navigation to finish...
|
||||
- navigated to "http://localhost:16820/prompt-projects/create"
|
||||
|
||||
```
|
||||
|
||||
# Test source
|
||||
|
||||
```ts
|
||||
460 | untestableFeatures.push('AI provider detail view: no providers exist to click');
|
||||
461 | }
|
||||
462 | });
|
||||
463 |
|
||||
464 | test('Edit AI provider', async () => {
|
||||
465 | await adminPage.goto(`${BASE_URL}/ai-providers`);
|
||||
466 | const firstEdit = adminPage.locator('a[href*="ai-providers/"][class*="btn-warning"]').first();
|
||||
467 | const count = await firstEdit.count();
|
||||
468 | if (count > 0) {
|
||||
469 | await firstEdit.click();
|
||||
470 | await adminPage.waitForURL((url) => url.pathname.includes('/ai-providers/') && !url.pathname.includes('/create'), { timeout: 10000 });
|
||||
471 | await adminPage.waitForLoadState('networkidle').catch(() => {});
|
||||
472 | await expect(adminPage.locator('#name')).toBeVisible();
|
||||
473 | } else {
|
||||
474 | untestableFeatures.push('AI provider edit: no providers exist to edit');
|
||||
475 | }
|
||||
476 | });
|
||||
477 |
|
||||
478 | test('Delete AI provider', async () => {
|
||||
479 | await adminPage.goto(`${BASE_URL}/ai-providers`);
|
||||
480 | const firstDelete = adminPage.locator('form[action*="ai-providers"][method="POST"]').first();
|
||||
481 | const count = await firstDelete.count();
|
||||
482 | if (count > 0) {
|
||||
483 | adminPage.on('dialog', (dialog) => dialog.accept());
|
||||
484 | await firstDelete.evaluate((form) => form.submit());
|
||||
485 | await expect(adminPage.locator('.alert-success, .alert-danger')).toBeVisible({ timeout: 10000 });
|
||||
486 | } else {
|
||||
487 | untestableFeatures.push('AI provider delete: no providers exist to delete');
|
||||
488 | }
|
||||
489 | });
|
||||
490 |
|
||||
491 | test('API key visibility toggle works', async () => {
|
||||
492 | await adminPage.goto(`${BASE_URL}/ai-providers/create`);
|
||||
493 | const apiKeyInput = adminPage.locator('#api_key');
|
||||
494 | const toggleBtn = adminPage.locator('#toggleApiKey');
|
||||
495 | if (await toggleBtn.count() > 0) {
|
||||
496 | await expect(apiKeyInput).toHaveAttribute('type', 'password');
|
||||
497 | await toggleBtn.click();
|
||||
498 | await expect(apiKeyInput).toHaveAttribute('type', 'text');
|
||||
499 | await toggleBtn.click();
|
||||
500 | await expect(apiKeyInput).toHaveAttribute('type', 'password');
|
||||
501 | } else {
|
||||
502 | untestableFeatures.push('API key toggle: toggle button not found');
|
||||
503 | }
|
||||
504 | });
|
||||
505 | });
|
||||
506 |
|
||||
507 | // ========== FORMS VALIDATION ==========
|
||||
508 | test.describe('Forms Validation and Error Messages', () => {
|
||||
509 | let adminPage;
|
||||
510 |
|
||||
511 | test.beforeEach(async ({ context }) => {
|
||||
512 | adminPage = await login(context, SUPERADMIN_EMAIL, SUPERADMIN_PASSWORD);
|
||||
513 | });
|
||||
514 |
|
||||
515 | test.afterEach(async () => {
|
||||
516 | await adminPage.close();
|
||||
517 | });
|
||||
518 |
|
||||
519 | test('Prompt project form shows validation errors for missing fields', async () => {
|
||||
520 | await adminPage.goto(`${BASE_URL}/prompt-projects/create`);
|
||||
521 | await adminPage.evaluate(() => {
|
||||
522 | const form = document.querySelector('form:has(#title)');
|
||||
523 | if (form) {
|
||||
524 | Array.from(form.querySelectorAll('[required]')).forEach(el => el.removeAttribute('required'));
|
||||
525 | form.submit();
|
||||
526 | }
|
||||
527 | });
|
||||
528 | await expect(adminPage.locator('.text-danger')).toHaveCount(2, { timeout: 10000 });
|
||||
529 | });
|
||||
530 |
|
||||
531 | test('AI provider form shows validation errors for missing fields', async () => {
|
||||
532 | await adminPage.goto(`${BASE_URL}/ai-providers/create`);
|
||||
533 | await adminPage.evaluate(() => {
|
||||
534 | const form = document.querySelector('form:has(#name)');
|
||||
535 | if (form) {
|
||||
536 | Array.from(form.querySelectorAll('[required]')).forEach(el => el.removeAttribute('required'));
|
||||
537 | form.submit();
|
||||
538 | }
|
||||
539 | });
|
||||
540 | await expect(adminPage.locator('.text-danger')).toHaveCount(4, { timeout: 10000 });
|
||||
541 | });
|
||||
542 |
|
||||
543 | test('Temperature field accepts only 0.0-1.0 range', async () => {
|
||||
544 | await adminPage.goto(`${BASE_URL}/prompt-projects/create`);
|
||||
545 | await adminPage.fill('#temperature', '1.5');
|
||||
546 | await adminPage.evaluate(() => {
|
||||
547 | const form = document.querySelector('form:has(#title)');
|
||||
548 | if (form) form.submit();
|
||||
549 | });
|
||||
550 | const providerSelect = adminPage.locator('#provider_id');
|
||||
551 | const providerCount = await providerSelect.locator('option').count();
|
||||
552 | if (providerCount > 1) {
|
||||
553 | await providerSelect.selectOption({ index: 1 });
|
||||
554 | }
|
||||
555 | await adminPage.fill('#objective', 'Test');
|
||||
556 | await adminPage.evaluate(() => {
|
||||
557 | const form = document.querySelector('form:has(#title)');
|
||||
558 | if (form) form.submit();
|
||||
559 | });
|
||||
> 560 | await expect(adminPage.locator('.text-danger')).toBeVisible({ timeout: 10000 });
|
||||
| ^ Error: expect(locator).toBeVisible() failed
|
||||
561 | });
|
||||
562 |
|
||||
563 | test('Max tokens must be a positive integer', async () => {
|
||||
564 | await adminPage.goto(`${BASE_URL}/prompt-projects/create`);
|
||||
565 | await adminPage.fill('#max_tokens', '-5');
|
||||
566 | await adminPage.evaluate(() => {
|
||||
567 | const form = document.querySelector('form:has(#title)');
|
||||
568 | if (form) form.submit();
|
||||
569 | });
|
||||
570 | const providerSelect = adminPage.locator('#provider_id');
|
||||
571 | const providerCount = await providerSelect.locator('option').count();
|
||||
572 | if (providerCount > 1) {
|
||||
573 | await providerSelect.selectOption({ index: 1 });
|
||||
574 | }
|
||||
575 | await adminPage.fill('#objective', 'Test');
|
||||
576 | await adminPage.evaluate(() => {
|
||||
577 | const form = document.querySelector('form:has(#title)');
|
||||
578 | if (form) form.submit();
|
||||
579 | });
|
||||
580 | await expect(adminPage.locator('.text-danger')).toBeVisible({ timeout: 10000 });
|
||||
581 | });
|
||||
582 | });
|
||||
583 |
|
||||
584 | // ========== COOKIE CONSENT ==========
|
||||
585 | test.describe('Cookie Consent Banner', () => {
|
||||
586 | test('Banner appears on public pages', async ({ page }) => {
|
||||
587 | await page.goto(BASE_URL);
|
||||
588 | await expect(page.locator('#cookieConsentBanner')).toBeAttached();
|
||||
589 | });
|
||||
590 |
|
||||
591 | test('Accept all cookies closes banner', async ({ page }) => {
|
||||
592 | await page.goto(BASE_URL);
|
||||
593 | await page.waitForSelector('#cookieConsentBanner', { state: 'visible', timeout: 5000 }).catch(() => {});
|
||||
594 | const acceptBtn = page.locator('.cookie-btn-accept');
|
||||
595 | if (await acceptBtn.count() > 0) {
|
||||
596 | await acceptBtn.click();
|
||||
597 | await page.waitForTimeout(500);
|
||||
598 | await expect(page.locator('#cookieConsentBanner')).not.toBeVisible();
|
||||
599 | } else {
|
||||
600 | untestableFeatures.push('Cookie consent accept button not visible (banner may not have appeared)');
|
||||
601 | }
|
||||
602 | });
|
||||
603 |
|
||||
604 | test('Cookie preferences modal can be opened', async ({ page }) => {
|
||||
605 | await page.goto(BASE_URL);
|
||||
606 | await page.waitForSelector('#cookieConsentBanner', { state: 'visible', timeout: 5000 }).catch(() => {});
|
||||
607 | const customizeBtn = page.locator('.cookie-btn-customize');
|
||||
608 | if (await customizeBtn.count() > 0) {
|
||||
609 | await customizeBtn.click();
|
||||
610 | await expect(page.locator('#cookieConsentPreferences')).toBeVisible();
|
||||
611 | await page.locator('.btn-close').click();
|
||||
612 | } else {
|
||||
613 | untestableFeatures.push('Cookie customize button not visible');
|
||||
614 | }
|
||||
615 | });
|
||||
616 |
|
||||
617 | test('Cookie consent persists across page navigation', async ({ page }) => {
|
||||
618 | await page.goto(BASE_URL);
|
||||
619 | await page.waitForSelector('#cookieConsentBanner', { state: 'visible', timeout: 5000 }).catch(() => {});
|
||||
620 | const acceptBtn = page.locator('.cookie-btn-accept');
|
||||
621 | if (await acceptBtn.count() > 0) {
|
||||
622 | await acceptBtn.click();
|
||||
623 | await page.waitForTimeout(500);
|
||||
624 | await page.goto(`${BASE_URL}/login`);
|
||||
625 | const banner = page.locator('#cookieConsentBanner');
|
||||
626 | await expect(banner).not.toBeVisible({ timeout: 3000 }).catch(() => {});
|
||||
627 | } else {
|
||||
628 | untestableFeatures.push('Cookie consent accept button not visible for persistence test');
|
||||
629 | }
|
||||
630 | });
|
||||
631 | });
|
||||
632 |
|
||||
633 | // ========== RESPONSIVENESS ==========
|
||||
634 | test.describe('Responsiveness', () => {
|
||||
635 | test('Desktop viewport 1920x1080: sidebar visible and layout stable', async ({ page }) => {
|
||||
636 | await page.setViewportSize({ width: 1920, height: 1080 });
|
||||
637 | await page.goto(`${BASE_URL}/login`);
|
||||
638 | await expect(page.locator('.login-card')).toBeVisible();
|
||||
639 | });
|
||||
640 |
|
||||
641 | test('Mobile viewport 375x667: login page adapts', async ({ page }) => {
|
||||
642 | await page.setViewportSize({ width: 375, height: 667 });
|
||||
643 | await page.goto(`${BASE_URL}/login`);
|
||||
644 | await expect(page.locator('.login-card')).toBeVisible();
|
||||
645 | await expect(page.locator('#email')).toBeVisible();
|
||||
646 | });
|
||||
647 |
|
||||
648 | test('Mobile: hamburger menu toggles sidebar', async ({ context }) => {
|
||||
649 | const page = await login(context, SUPERADMIN_EMAIL, SUPERADMIN_PASSWORD);
|
||||
650 | await page.setViewportSize({ width: 375, height: 667 });
|
||||
651 | await page.goto(`${BASE_URL}/dashboard`);
|
||||
652 | const sidebar = page.locator('#sidebar');
|
||||
653 | await expect(sidebar).toBeVisible();
|
||||
654 | await page.click('#sidebarCollapse');
|
||||
655 | await page.waitForTimeout(300);
|
||||
656 | await page.close();
|
||||
657 | });
|
||||
658 |
|
||||
659 | test('Mobile: dashboard cards stack vertically', async ({ page }) => {
|
||||
660 | await page.setViewportSize({ width: 375, height: 667 });
|
||||
```
|
||||
Reference in New Issue
Block a user