236 lines
11 KiB
Markdown
Executable File
236 lines
11 KiB
Markdown
Executable File
# 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 });
|
|
``` |