Files
leocrm/frontend/e2e/contact-crud.spec.ts
T
Agent Zero 42b19040ce Phase 5.4-5.11: Playwright E2E test infrastructure with 7 spec files
- playwright.config.ts: chromium project, webServer, baseURL, trace/screenshot/video
- e2e/helpers.ts: API mock setup, login/logout helpers, mock data for all entities
- 7 E2E spec files (1164 lines total):
  - auth.spec.ts: login/logout workflow
  - contact-crud.spec.ts: create/edit/delete contacts, add persons
  - search.spec.ts: global search, filter, results
  - plugin-toggle.spec.ts: enable/disable plugins, UI changes
  - mail.spec.ts: mail account setup, folders, mail viewing
  - dms.spec.ts: folder creation, file upload, preview, share
  - calendar.spec.ts: appointment creation, calendar switch, kanban view
- @playwright/test added as devDependency
- e2e scripts added to package.json
- TSC: 0 new errors (only pre-existing Dms.tsx)
2026-07-23 22:35:03 +02:00

133 lines
5.5 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { setupApiMocks, login, navigateTo, MOCK_CONTACTS } from './helpers';
test.describe('Contact CRUD E2E', () => {
test.beforeEach(async ({ page }) => {
await setupApiMocks(page);
await login(page);
});
test('create a new company contact', async ({ page }) => {
await navigateTo(page, '/contacts');
// Wait for contacts page to load
await expect(page.locator('[data-testid="contact-list-view"], [data-testid="contact-list-empty"], [data-testid="contact-list-loading"]')).toBeVisible({ timeout: 15_000 });
// Click the "new" toolbar button (Plus icon in plugin toolbar)
const newBtn = page.locator('[data-testid="plugin-toolbar"] button').filter({ hasText: /new|neu|erstellen|create/i }).first();
await newBtn.click();
// Contact edit modal should appear
await expect(page.locator('[data-testid="contact-type-select"]')).toBeVisible({ timeout: 10_000 });
// Select company type (should be default)
await expect(page.locator('[data-testid="contact-name-input"]')).toBeVisible();
// Fill in company name
await page.locator('[data-testid="contact-name-input"]').fill('Test Company GmbH');
// Fill in email
await page.locator('input[type="email"]').first().fill('test@company.test');
// Submit
await page.locator('[data-testid="contact-submit-btn"]').click();
// Modal should close (success)
await expect(page.locator('[data-testid="contact-type-select"]')).not.toBeVisible({ timeout: 10_000 });
});
test('view contact detail and edit', async ({ page }) => {
await navigateTo(page, '/contacts');
// Wait for list to load
await expect(page.locator('[data-testid="contact-list-view"], [data-testid="contact-list-empty"]')).toBeVisible({ timeout: 15_000 });
// Click on first contact in the list
const firstContact = page.locator('[data-testid="contact-list-view"] > div, [data-testid="contact-list-view"] button').first();
if (await firstContact.isVisible()) {
await firstContact.click();
// Detail should appear
await expect(page.locator('[data-testid="contact-detail"]')).toBeVisible({ timeout: 10_000 });
// Click edit button in detail
const editBtn = page.locator('[data-testid="contact-detail"] button').filter({ hasText: /edit|bearbeiten/i }).first();
if (await editBtn.isVisible()) {
await editBtn.click();
// Edit modal should appear
await expect(page.locator('[data-testid="contact-type-select"]')).toBeVisible({ timeout: 10_000 });
// Modify name
await page.locator('[data-testid="contact-name-input"]').fill('Updated Company GmbH');
// Save
await page.locator('[data-testid="contact-submit-btn"]').click();
// Modal should close
await expect(page.locator('[data-testid="contact-type-select"]')).not.toBeVisible({ timeout: 10_000 });
}
}
});
test('add ansprechpartner (contact person) to a company', async ({ page }) => {
await navigateTo(page, '/contacts');
// Wait for list and click first contact
await expect(page.locator('[data-testid="contact-list-view"], [data-testid="contact-list-empty"]')).toBeVisible({ timeout: 15_000 });
const firstContact = page.locator('[data-testid="contact-list-view"] > div, [data-testid="contact-list-view"] button').first();
if (await firstContact.isVisible()) {
await firstContact.click();
await expect(page.locator('[data-testid="contact-detail"]')).toBeVisible({ timeout: 10_000 });
// Find and click "Add Person" button
const addPersonBtn = page.locator('[data-testid="contact-detail"] button').filter({ hasText: /add.*person|ansprechpartner.*hinzuf/i }).first();
if (await addPersonBtn.isVisible()) {
await addPersonBtn.click();
// Person modal should appear
await expect(page.locator('[data-testid="person-save-btn"]')).toBeVisible({ timeout: 10_000 });
// Fill person details
const inputs = page.locator('[data-testid="contact-detail"] input, .modal input, [role="dialog"] input');
const firstNameInput = inputs.nth(0);
const lastNameInput = inputs.nth(1);
await firstNameInput.fill('Anna');
await lastNameInput.fill('Schmidt');
// Save person
await page.locator('[data-testid="person-save-btn"]').click();
// Modal should close
await expect(page.locator('[data-testid="person-save-btn"]')).not.toBeVisible({ timeout: 10_000 });
}
}
});
test('delete a contact', async ({ page }) => {
await navigateTo(page, '/contacts');
await expect(page.locator('[data-testid="contact-list-view"], [data-testid="contact-list-empty"]')).toBeVisible({ timeout: 15_000 });
const firstContact = page.locator('[data-testid="contact-list-view"] > div, [data-testid="contact-list-view"] button').first();
if (await firstContact.isVisible()) {
await firstContact.click();
await expect(page.locator('[data-testid="contact-detail"]')).toBeVisible({ timeout: 10_000 });
// Set up confirm dialog handler
page.on('dialog', (dialog) => dialog.accept());
// Click delete button
const deleteBtn = page.locator('[data-testid="contact-detail"] button').filter({ hasText: /delete|löschen/i }).first();
if (await deleteBtn.isVisible()) {
await deleteBtn.click();
// Detail should become empty after deletion
await expect(page.locator('[data-testid="contact-detail-empty"], [data-testid="contact-detail-loading"]')).toBeVisible({ timeout: 10_000 });
}
}
});
});