Mise en place de tests utilisateurs avec playwright pour la partie angular + corrections au niveau des labels avec for et id pour cliquer dessus
Some checks failed
E2E Tests / e2e (push) Failing after 5m30s
Some checks failed
E2E Tests / e2e (push) Failing after 5m30s
This commit is contained in:
77
web/e2e/tests/lore/lore-create.spec.ts
Normal file
77
web/e2e/tests/lore/lore-create.spec.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { deleteLore } from '../../fixtures/api';
|
||||
|
||||
test.describe('Lore creation', () => {
|
||||
const createdLoreIds: string[] = [];
|
||||
|
||||
test.afterEach(async ({ request }) => {
|
||||
while (createdLoreIds.length) {
|
||||
const id = createdLoreIds.pop()!;
|
||||
await deleteLore(request, id);
|
||||
}
|
||||
});
|
||||
|
||||
test('opens the modal, creates a lore, and shows it in the grid', async ({ page, request }) => {
|
||||
const loreName = `Univers E2E ${Date.now()}`;
|
||||
const description = "Un univers créé par les tests automatisés.";
|
||||
|
||||
await page.goto('/lore');
|
||||
|
||||
await expect(page.getByRole('heading', { name: /Vos Univers/i })).toBeVisible();
|
||||
|
||||
await page.locator('.lore-card.card-new').click();
|
||||
|
||||
const modal = page.locator('.modal');
|
||||
await expect(modal).toBeVisible();
|
||||
await expect(modal.getByRole('heading', { name: /Créer un nouveau Lore/i })).toBeVisible();
|
||||
|
||||
await modal.getByLabel(/Nom de l'univers/i).fill(loreName);
|
||||
await modal.getByLabel('Description').fill(description);
|
||||
|
||||
await modal.getByRole('button', { name: /^Créer le lore$/i }).click();
|
||||
|
||||
await expect(modal).not.toBeVisible();
|
||||
|
||||
const newCard = page.locator('.lore-card', { hasText: loreName });
|
||||
await expect(newCard).toBeVisible();
|
||||
await expect(newCard).toContainText(description);
|
||||
|
||||
const allLores = await request.get('/api/lores').then((r) => r.json());
|
||||
const created = allLores.find((l: { name: string; id: string }) => l.name === loreName);
|
||||
expect(created).toBeDefined();
|
||||
createdLoreIds.push(created.id);
|
||||
});
|
||||
|
||||
test('submit button is disabled when name is empty', async ({ page }) => {
|
||||
await page.goto('/lore');
|
||||
await page.locator('.lore-card.card-new').click();
|
||||
|
||||
const modal = page.locator('.modal');
|
||||
const submit = modal.getByRole('button', { name: /^Créer le lore$/i });
|
||||
|
||||
await expect(submit).toBeDisabled();
|
||||
|
||||
await modal.getByLabel(/Nom de l'univers/i).fill('Quelque chose');
|
||||
await expect(submit).toBeEnabled();
|
||||
|
||||
await modal.getByLabel(/Nom de l'univers/i).fill('');
|
||||
await expect(submit).toBeDisabled();
|
||||
});
|
||||
|
||||
test('clicking the backdrop closes the modal without creating', async ({ page }) => {
|
||||
const typedButAbandoned = `Univers abandonné ${Date.now()}`;
|
||||
|
||||
await page.goto('/lore');
|
||||
await page.locator('.lore-card.card-new').click();
|
||||
|
||||
const modal = page.locator('.modal');
|
||||
await expect(modal).toBeVisible();
|
||||
|
||||
await modal.getByLabel(/Nom de l'univers/i).fill(typedButAbandoned);
|
||||
|
||||
await page.locator('.modal-backdrop').click({ position: { x: 5, y: 5 } });
|
||||
await expect(modal).not.toBeVisible();
|
||||
|
||||
await expect(page.locator('.lore-card', { hasText: typedButAbandoned })).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
90
web/e2e/tests/lore/page-create.spec.ts
Normal file
90
web/e2e/tests/lore/page-create.spec.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import {
|
||||
seedLoreWithFolder,
|
||||
seedTemplate,
|
||||
deleteLore,
|
||||
getPagesForLore,
|
||||
type SeededLore,
|
||||
type SeededTemplate,
|
||||
} from '../../fixtures/api';
|
||||
|
||||
test.describe('Page creation', () => {
|
||||
let seeded: SeededLore;
|
||||
let template: SeededTemplate;
|
||||
|
||||
test.beforeEach(async ({ request }) => {
|
||||
seeded = await seedLoreWithFolder(request);
|
||||
template = await seedTemplate(request, {
|
||||
loreId: seeded.id,
|
||||
defaultNodeId: seeded.rootFolderId,
|
||||
fieldNames: ['Apparence', 'Motivation'],
|
||||
});
|
||||
});
|
||||
|
||||
test.afterEach(async ({ request }) => {
|
||||
if (seeded?.id) await deleteLore(request, seeded.id);
|
||||
});
|
||||
|
||||
test('creates an empty page from a template and redirects to edit', async ({ page, request }) => {
|
||||
const pageTitle = `Maître Eldrin ${Date.now()}`;
|
||||
|
||||
await page.goto(`/lore/${seeded.id}/pages/create`);
|
||||
|
||||
await expect(page.getByRole('heading', { name: /Créer une nouvelle Page/i })).toBeVisible();
|
||||
|
||||
await page.getByLabel(/Titre de la page/i).fill(pageTitle);
|
||||
|
||||
await page.locator('.template-card', { hasText: template.name }).click();
|
||||
await expect(page.locator('.template-card.selected', { hasText: template.name })).toBeVisible();
|
||||
|
||||
await expect(page.locator('#page-node')).toHaveValue(seeded.rootFolderId);
|
||||
|
||||
await page.getByRole('button', { name: /^Créer la page$/i }).click();
|
||||
|
||||
await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}/pages/[^/]+/edit$`));
|
||||
|
||||
const pages = await getPagesForLore(request, seeded.id);
|
||||
const created = pages.find((p) => p.title === pageTitle);
|
||||
expect(created).toBeDefined();
|
||||
expect(created?.templateId).toBe(template.id);
|
||||
expect(created?.nodeId).toBe(seeded.rootFolderId);
|
||||
});
|
||||
|
||||
test('submit is disabled until title, template and folder are set', async ({ page }) => {
|
||||
await page.goto(`/lore/${seeded.id}/pages/create`);
|
||||
|
||||
const submit = page.getByRole('button', { name: /^Créer la page$/i });
|
||||
await expect(submit).toBeDisabled();
|
||||
|
||||
await page.getByLabel(/Titre de la page/i).fill('Un titre');
|
||||
await expect(submit).toBeDisabled();
|
||||
|
||||
await page.locator('.template-card', { hasText: template.name }).click();
|
||||
await expect(submit).toBeEnabled();
|
||||
});
|
||||
|
||||
test('entering on a folder-scoped route preselects that folder', async ({ page, request }) => {
|
||||
const pageTitle = `Page scoped ${Date.now()}`;
|
||||
|
||||
const secondFolderRes = await request.post('/api/lore-nodes', {
|
||||
data: { loreId: seeded.id, name: 'Autre dossier', icon: 'folder', description: '' },
|
||||
});
|
||||
expect(secondFolderRes.ok()).toBeTruthy();
|
||||
const secondFolderId = (await secondFolderRes.json()).id;
|
||||
|
||||
await page.goto(`/lore/${seeded.id}/nodes/${secondFolderId}/pages/create`);
|
||||
|
||||
const nodeSelect = page.locator('#page-node');
|
||||
await expect(nodeSelect).toHaveValue(secondFolderId);
|
||||
await expect(nodeSelect).toBeDisabled();
|
||||
|
||||
await page.getByLabel(/Titre de la page/i).fill(pageTitle);
|
||||
await page.locator('.template-card', { hasText: template.name }).click();
|
||||
await page.getByRole('button', { name: /^Créer la page$/i }).click();
|
||||
|
||||
await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}/pages/[^/]+/edit$`));
|
||||
|
||||
const pages = await getPagesForLore(request, seeded.id);
|
||||
expect(pages.find((p) => p.title === pageTitle)?.nodeId).toBe(secondFolderId);
|
||||
});
|
||||
});
|
||||
95
web/e2e/tests/lore/page-edit.spec.ts
Normal file
95
web/e2e/tests/lore/page-edit.spec.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import {
|
||||
seedLoreWithFolder,
|
||||
seedTemplate,
|
||||
seedPage,
|
||||
deleteLore,
|
||||
getPageById,
|
||||
type SeededLore,
|
||||
type SeededTemplate,
|
||||
type SeededPage,
|
||||
} from '../../fixtures/api';
|
||||
|
||||
test.describe('Page edit', () => {
|
||||
let seeded: SeededLore;
|
||||
let template: SeededTemplate;
|
||||
let pageEntity: SeededPage;
|
||||
|
||||
test.beforeEach(async ({ request }) => {
|
||||
seeded = await seedLoreWithFolder(request);
|
||||
template = await seedTemplate(request, {
|
||||
loreId: seeded.id,
|
||||
defaultNodeId: seeded.rootFolderId,
|
||||
fieldNames: ['Apparence', 'Motivation'],
|
||||
});
|
||||
pageEntity = await seedPage(request, {
|
||||
loreId: seeded.id,
|
||||
nodeId: seeded.rootFolderId,
|
||||
templateId: template.id,
|
||||
});
|
||||
});
|
||||
|
||||
test.afterEach(async ({ request }) => {
|
||||
if (seeded?.id) await deleteLore(request, seeded.id);
|
||||
});
|
||||
|
||||
test('edits title, dynamic fields, notes and persists to API', async ({ page, request }) => {
|
||||
const newTitle = `Maître Eldrin ${Date.now()}`;
|
||||
const apparence = 'Vieil homme au regard perçant.';
|
||||
const motivation = 'Protéger la cité à tout prix.';
|
||||
const notes = 'MJ : il connaît le secret du roi.';
|
||||
|
||||
await page.goto(`/lore/${seeded.id}/pages/${pageEntity.id}/edit`);
|
||||
|
||||
await expect(page.locator('input[name="title"]')).toHaveValue(pageEntity.title);
|
||||
|
||||
await page.locator('input[name="title"]').fill(newTitle);
|
||||
await page.getByPlaceholder('Valeur pour Apparence...').fill(apparence);
|
||||
await page.getByPlaceholder('Valeur pour Motivation...').fill(motivation);
|
||||
await page.locator('textarea[name="notes"]').fill(notes);
|
||||
|
||||
await page.getByRole('button', { name: /^Sauvegarder$/i }).click();
|
||||
|
||||
await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}/pages/${pageEntity.id}$`));
|
||||
|
||||
const persisted = await getPageById(request, pageEntity.id);
|
||||
expect(persisted.title).toBe(newTitle);
|
||||
expect(persisted.values?.['Apparence']).toBe(apparence);
|
||||
expect(persisted.values?.['Motivation']).toBe(motivation);
|
||||
expect(persisted.notes).toBe(notes);
|
||||
});
|
||||
|
||||
test('adds a tag via chips input and persists it', async ({ page, request }) => {
|
||||
await page.goto(`/lore/${seeded.id}/pages/${pageEntity.id}/edit`);
|
||||
|
||||
const chipsInput = page.locator('app-chips-input input');
|
||||
await chipsInput.fill('dangereux');
|
||||
await chipsInput.press('Enter');
|
||||
await chipsInput.fill('ancien');
|
||||
await chipsInput.press('Enter');
|
||||
|
||||
await expect(page.locator('app-chips-input').getByText('dangereux')).toBeVisible();
|
||||
await expect(page.locator('app-chips-input').getByText('ancien')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: /^Sauvegarder$/i }).click();
|
||||
await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}/pages/${pageEntity.id}$`));
|
||||
|
||||
const persisted = await getPageById(request, pageEntity.id);
|
||||
expect(persisted.tags).toEqual(expect.arrayContaining(['dangereux', 'ancien']));
|
||||
});
|
||||
|
||||
test('save button is disabled when title is empty', async ({ page }) => {
|
||||
await page.goto(`/lore/${seeded.id}/pages/${pageEntity.id}/edit`);
|
||||
|
||||
const titleInput = page.locator('input[name="title"]');
|
||||
const saveBtn = page.getByRole('button', { name: /^Sauvegarder$/i });
|
||||
|
||||
await expect(saveBtn).toBeEnabled();
|
||||
await titleInput.fill('');
|
||||
await expect(saveBtn).toBeDisabled();
|
||||
await titleInput.fill(' ');
|
||||
await expect(saveBtn).toBeDisabled();
|
||||
await titleInput.fill('OK');
|
||||
await expect(saveBtn).toBeEnabled();
|
||||
});
|
||||
});
|
||||
72
web/e2e/tests/lore/template-create.spec.ts
Normal file
72
web/e2e/tests/lore/template-create.spec.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { seedLoreWithFolder, deleteLore, getTemplatesForLore, type SeededLore } from '../../fixtures/api';
|
||||
|
||||
test.describe('Template creation', () => {
|
||||
let seeded: SeededLore;
|
||||
|
||||
test.beforeEach(async ({ request }) => {
|
||||
seeded = await seedLoreWithFolder(request);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ request }) => {
|
||||
if (seeded?.id) await deleteLore(request, seeded.id);
|
||||
});
|
||||
|
||||
test('creates a template with default fields', async ({ page, request }) => {
|
||||
const templateName = `Auberge ${Date.now()}`;
|
||||
|
||||
await page.goto(`/lore/${seeded.id}/templates/create`);
|
||||
|
||||
await expect(page.getByRole('heading', { name: /Créer un nouveau Template/i })).toBeVisible();
|
||||
|
||||
await page.getByLabel(/Nom du template/i).fill(templateName);
|
||||
await page.getByLabel('Description').fill('Template créé via E2E');
|
||||
await page.getByLabel(/Dossier par défaut/i).selectOption({ label: seeded.rootFolderName });
|
||||
|
||||
await page.getByRole('button', { name: /^Créer le template$/i }).click();
|
||||
|
||||
await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}$`));
|
||||
|
||||
const templates = await getTemplatesForLore(request, seeded.id);
|
||||
expect(templates.map((t) => t.name)).toContain(templateName);
|
||||
});
|
||||
|
||||
test('submit is disabled when name is empty', async ({ page }) => {
|
||||
await page.goto(`/lore/${seeded.id}/templates/create`);
|
||||
|
||||
const submit = page.getByRole('button', { name: /^Créer le template$/i });
|
||||
await expect(submit).toBeDisabled();
|
||||
|
||||
await page.getByLabel(/Nom du template/i).fill('Valid name');
|
||||
await page.getByLabel(/Dossier par défaut/i).selectOption({ label: seeded.rootFolderName });
|
||||
await expect(submit).toBeEnabled();
|
||||
});
|
||||
|
||||
test('can add and remove a custom field before creating', async ({ page, request }) => {
|
||||
const templateName = `Artefact ${Date.now()}`;
|
||||
|
||||
await page.goto(`/lore/${seeded.id}/templates/create`);
|
||||
|
||||
await page.getByLabel(/Nom du template/i).fill(templateName);
|
||||
await page.getByLabel(/Dossier par défaut/i).selectOption({ label: seeded.rootFolderName });
|
||||
|
||||
const addFieldInput = page.getByPlaceholder('+ Ajouter un champ');
|
||||
await addFieldInput.fill('Pouvoir');
|
||||
await addFieldInput.press('Enter');
|
||||
await expect(page.locator('.fields-list .field-chip', { hasText: 'Pouvoir' })).toBeVisible();
|
||||
|
||||
await addFieldInput.fill('Origine');
|
||||
await addFieldInput.press('Enter');
|
||||
await expect(page.locator('.fields-list .field-chip', { hasText: 'Origine' })).toBeVisible();
|
||||
|
||||
const origineRow = page.locator('.fields-list .field-row', { hasText: 'Origine' });
|
||||
await origineRow.getByRole('button', { name: 'Supprimer' }).click();
|
||||
await expect(page.locator('.fields-list .field-chip', { hasText: 'Origine' })).toHaveCount(0);
|
||||
|
||||
await page.getByRole('button', { name: /^Créer le template$/i }).click();
|
||||
await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}$`));
|
||||
|
||||
const templates = await getTemplatesForLore(request, seeded.id);
|
||||
expect(templates.find((t) => t.name === templateName)).toBeDefined();
|
||||
});
|
||||
});
|
||||
74
web/e2e/tests/lore/template-edit.spec.ts
Normal file
74
web/e2e/tests/lore/template-edit.spec.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import {
|
||||
seedLoreWithFolder,
|
||||
seedTemplate,
|
||||
deleteLore,
|
||||
getTemplateById,
|
||||
type SeededLore,
|
||||
type SeededTemplate,
|
||||
} from '../../fixtures/api';
|
||||
|
||||
test.describe('Template edit', () => {
|
||||
let seeded: SeededLore;
|
||||
let template: SeededTemplate;
|
||||
|
||||
test.beforeEach(async ({ request }) => {
|
||||
seeded = await seedLoreWithFolder(request);
|
||||
template = await seedTemplate(request, {
|
||||
loreId: seeded.id,
|
||||
defaultNodeId: seeded.rootFolderId,
|
||||
fieldNames: ['Nom', 'Description'],
|
||||
});
|
||||
});
|
||||
|
||||
test.afterEach(async ({ request }) => {
|
||||
if (seeded?.id) await deleteLore(request, seeded.id);
|
||||
});
|
||||
|
||||
test('form is prefilled with the current template data', async ({ page }) => {
|
||||
await page.goto(`/lore/${seeded.id}/templates/${template.id}`);
|
||||
|
||||
await expect(page.getByLabel(/^Nom$/)).toHaveValue(template.name);
|
||||
await expect(page.getByLabel(/Dossier par défaut/i)).toHaveValue(seeded.rootFolderId);
|
||||
await expect(page.locator('.fields-list .field-chip', { hasText: 'Nom' })).toBeVisible();
|
||||
await expect(page.locator('.fields-list .field-chip', { hasText: 'Description' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('renames the template and persists to API', async ({ page, request }) => {
|
||||
const newName = `${template.name} renamed`;
|
||||
|
||||
await page.goto(`/lore/${seeded.id}/templates/${template.id}`);
|
||||
|
||||
const nameInput = page.getByLabel(/^Nom$/);
|
||||
await nameInput.fill(newName);
|
||||
await page.getByLabel(/Description/i).fill('Description mise à jour');
|
||||
|
||||
await page.getByRole('button', { name: /^Sauvegarder$/i }).click();
|
||||
|
||||
await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}$`));
|
||||
|
||||
const persisted = await getTemplateById(request, template.id);
|
||||
expect(persisted.name).toBe(newName);
|
||||
expect(persisted.description).toBe('Description mise à jour');
|
||||
});
|
||||
|
||||
test('adds a new field, removes an existing one, and persists the order', async ({ page, request }) => {
|
||||
await page.goto(`/lore/${seeded.id}/templates/${template.id}`);
|
||||
|
||||
const addInput = page.getByPlaceholder('+ Ajouter un champ');
|
||||
await addInput.fill('Stats');
|
||||
await addInput.press('Enter');
|
||||
await expect(page.locator('.fields-list .field-chip', { hasText: 'Stats' })).toBeVisible();
|
||||
|
||||
const descriptionRow = page.locator('.fields-list .field-row', { hasText: 'Description' });
|
||||
await descriptionRow.getByRole('button', { name: 'Supprimer' }).click();
|
||||
await expect(page.locator('.fields-list .field-chip', { hasText: 'Description' })).toHaveCount(0);
|
||||
|
||||
await page.getByRole('button', { name: /^Sauvegarder$/i }).click();
|
||||
await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}$`));
|
||||
|
||||
const persisted = await getTemplateById(request, template.id);
|
||||
const names = persisted.fields.map((f) => f.name);
|
||||
expect(names).toEqual(['Nom', 'Stats']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user