Fix workflow gitea action pour e2e (tests automatisés via playwright) + correction d'une incohérence dans l'API coté java. Ajout d'autres tests utilisateur
Some checks failed
E2E Tests / e2e (push) Failing after 2m31s

This commit is contained in:
2026-04-25 00:45:04 +02:00
parent 6c6bd20f0d
commit 4b866e5212
17 changed files with 327 additions and 51 deletions

View File

@@ -0,0 +1,50 @@
import { test, expect } from '@playwright/test';
import {
seedCampaign,
deleteCampaign,
getArcById,
type SeededCampaign,
} from '../../fixtures/api';
test.describe('Arc creation', () => {
let campaign: SeededCampaign;
test.beforeEach(async ({ request }) => {
campaign = await seedCampaign(request);
});
test.afterEach(async ({ request }) => {
if (campaign?.id) await deleteCampaign(request, campaign.id);
});
test('creates an arc and redirects to its view', async ({ page, request }) => {
const arcName = `Arc ${Date.now()}`;
const description = 'Synopsis test';
await page.goto(`/campaigns/${campaign.id}/arcs/create`);
await expect(page.getByRole('heading', { name: /Créer un nouvel arc/i })).toBeVisible();
await page.getByLabel(/Nom de l'arc/i).fill(arcName);
await page.getByLabel(/Description/i).fill(description);
await page.getByRole('button', { name: /^Créer l'arc$/i }).click();
await expect(page).toHaveURL(new RegExp(`/campaigns/${campaign.id}/arcs/\\d+$`));
const createdId = page.url().match(/\/arcs\/(\d+)$/)?.[1];
expect(createdId).toBeTruthy();
const persisted = await getArcById(request, createdId!);
expect(persisted.name).toBe(arcName);
expect(persisted.description).toBe(description);
});
test('submit is disabled when name is empty', async ({ page }) => {
await page.goto(`/campaigns/${campaign.id}/arcs/create`);
const submit = page.getByRole('button', { name: /^Créer l'arc$/i });
await expect(submit).toBeDisabled();
await page.getByLabel(/Nom de l'arc/i).fill('Quelque chose');
await expect(submit).toBeEnabled();
});
});

View File

@@ -0,0 +1,54 @@
import { test, expect } from '@playwright/test';
import {
seedCampaign,
seedArc,
deleteCampaign,
getChapterById,
type SeededCampaign,
type SeededArc,
} from '../../fixtures/api';
test.describe('Chapter creation', () => {
let campaign: SeededCampaign;
let arc: SeededArc;
test.beforeEach(async ({ request }) => {
campaign = await seedCampaign(request);
arc = await seedArc(request, { campaignId: campaign.id });
});
test.afterEach(async ({ request }) => {
if (campaign?.id) await deleteCampaign(request, campaign.id);
});
test('creates a chapter and redirects to its view', async ({ page, request }) => {
const chapterName = `Chapitre ${Date.now()}`;
await page.goto(`/campaigns/${campaign.id}/arcs/${arc.id}/chapters/create`);
await expect(page.getByRole('heading', { name: /Créer un nouveau chapitre/i })).toBeVisible();
await page.getByLabel(/Nom du chapitre/i).fill(chapterName);
await page.getByLabel(/Description/i).fill('Synopsis du chapitre');
await page.getByRole('button', { name: /^Créer le chapitre$/i }).click();
await expect(page).toHaveURL(
new RegExp(`/campaigns/${campaign.id}/arcs/${arc.id}/chapters/\\d+$`),
);
const createdId = page.url().match(/\/chapters\/(\d+)$/)?.[1];
expect(createdId).toBeTruthy();
const persisted = await getChapterById(request, createdId!);
expect(persisted.name).toBe(chapterName);
});
test('submit is disabled when name is empty', async ({ page }) => {
await page.goto(`/campaigns/${campaign.id}/arcs/${arc.id}/chapters/create`);
const submit = page.getByRole('button', { name: /^Créer le chapitre$/i });
await expect(submit).toBeDisabled();
await page.getByLabel(/Nom du chapitre/i).fill('OK');
await expect(submit).toBeEnabled();
});
});

View File

@@ -0,0 +1,64 @@
import { test, expect } from '@playwright/test';
import {
seedCampaign,
seedArc,
seedChapter,
deleteCampaign,
getSceneById,
type SeededCampaign,
type SeededArc,
type SeededChapter,
} from '../../fixtures/api';
test.describe('Scene creation', () => {
let campaign: SeededCampaign;
let arc: SeededArc;
let chapter: SeededChapter;
test.beforeEach(async ({ request }) => {
campaign = await seedCampaign(request);
arc = await seedArc(request, { campaignId: campaign.id });
chapter = await seedChapter(request, { arcId: arc.id });
});
test.afterEach(async ({ request }) => {
if (campaign?.id) await deleteCampaign(request, campaign.id);
});
test('creates a scene and redirects to its view', async ({ page, request }) => {
const sceneName = `Scène ${Date.now()}`;
await page.goto(
`/campaigns/${campaign.id}/arcs/${arc.id}/chapters/${chapter.id}/scenes/create`,
);
await expect(page.getByRole('heading', { name: /Créer une nouvelle scène/i })).toBeVisible();
await page.getByLabel(/Nom de la scène/i).fill(sceneName);
await page.getByLabel(/Description/i).fill('Résumé rapide de la scène.');
await page.getByRole('button', { name: /^Créer la scène$/i }).click();
await expect(page).toHaveURL(
new RegExp(
`/campaigns/${campaign.id}/arcs/${arc.id}/chapters/${chapter.id}/scenes/\\d+$`,
),
);
const createdId = page.url().match(/\/scenes\/(\d+)$/)?.[1];
expect(createdId).toBeTruthy();
const persisted = await getSceneById(request, createdId!);
expect(persisted.name).toBe(sceneName);
});
test('submit is disabled when name is empty', async ({ page }) => {
await page.goto(
`/campaigns/${campaign.id}/arcs/${arc.id}/chapters/${chapter.id}/scenes/create`,
);
const submit = page.getByRole('button', { name: /^Créer la scène$/i });
await expect(submit).toBeDisabled();
await page.getByLabel(/Nom de la scène/i).fill('OK');
await expect(submit).toBeEnabled();
});
});

View File

@@ -0,0 +1,70 @@
import { test, expect } from '@playwright/test';
import { seedLoreWithFolder, deleteLore, getLoreById, type SeededLore } from '../../fixtures/api';
test.describe('Lore inline edit (on detail page)', () => {
let seeded: SeededLore;
test.beforeEach(async ({ request }) => {
seeded = await seedLoreWithFolder(request);
});
test.afterEach(async ({ request }) => {
if (seeded?.id) await deleteLore(request, seeded.id);
});
test('renames the lore inline and persists the change', async ({ page, request }) => {
const newName = `${seeded.name} renamed`;
const newDescription = 'Nouvelle description éditée via UI';
await page.goto(`/lore/${seeded.id}`);
await expect(page.locator('.detail-header h1')).toHaveText(seeded.name);
await page.getByRole('button', { name: /^Modifier$/i }).click();
const nameInput = page.getByLabel(/^Nom$/);
const descInput = page.getByLabel(/^Description$/);
await expect(nameInput).toHaveValue(seeded.name);
await nameInput.fill(newName);
await descInput.fill(newDescription);
await page.getByRole('button', { name: /^Sauvegarder$/i }).click();
// Sortie du mode édition : le header bascule en mode lecture avec la nouvelle valeur.
await expect(page.locator('.detail-header h1')).toHaveText(newName);
await expect(page.locator('.detail-header .description')).toHaveText(newDescription);
const persisted = await getLoreById(request, seeded.id);
expect(persisted.name).toBe(newName);
expect(persisted.description).toBe(newDescription);
});
test('save button is disabled when name is emptied during edit', async ({ page }) => {
await page.goto(`/lore/${seeded.id}`);
await page.getByRole('button', { name: /^Modifier$/i }).click();
const saveBtn = page.getByRole('button', { name: /^Sauvegarder$/i });
await expect(saveBtn).toBeEnabled();
await page.getByLabel(/^Nom$/).fill('');
await expect(saveBtn).toBeDisabled();
await page.getByLabel(/^Nom$/).fill(' ');
await expect(saveBtn).toBeDisabled();
});
test('cancel discards the in-flight edits', async ({ page, request }) => {
await page.goto(`/lore/${seeded.id}`);
await page.getByRole('button', { name: /^Modifier$/i }).click();
await page.getByLabel(/^Nom$/).fill('Name jamais sauvegardé');
await page.getByRole('button', { name: /^Annuler$/i }).click();
// Retour en mode lecture avec le nom d'origine.
await expect(page.locator('.detail-header h1')).toHaveText(seeded.name);
const persisted = await getLoreById(request, seeded.id);
expect(persisted.name).toBe(seeded.name);
});
});