From d24d6459a08d3f139476d4f1421974e1bd4af753 Mon Sep 17 00:00:00 2001 From: "IETM_FIXE\\ietm6" Date: Sat, 25 Apr 2026 00:51:32 +0200 Subject: [PATCH] =?UTF-8?q?Ajout=20de=20test,=20correctif=20d'un=20probl?= =?UTF-8?q?=C3=A8me=20d'horloge=20pour=20le=20workflow=20gitea=20actions?= =?UTF-8?q?=20pour=20le=20e2e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/e2e.yml | 7 +++ web/e2e/tests/campaign/arc-delete.spec.ts | 49 ++++++++++++++++ .../tests/campaign/campaign-delete.spec.ts | 41 +++++++++++++ web/e2e/tests/lore/lore-delete.spec.ts | 53 +++++++++++++++++ web/e2e/tests/lore/page-delete.spec.ts | 58 +++++++++++++++++++ web/e2e/tests/lore/template-delete.spec.ts | 51 ++++++++++++++++ 6 files changed, 259 insertions(+) create mode 100644 web/e2e/tests/campaign/arc-delete.spec.ts create mode 100644 web/e2e/tests/campaign/campaign-delete.spec.ts create mode 100644 web/e2e/tests/lore/lore-delete.spec.ts create mode 100644 web/e2e/tests/lore/page-delete.spec.ts create mode 100644 web/e2e/tests/lore/template-delete.spec.ts diff --git a/.gitea/workflows/e2e.yml b/.gitea/workflows/e2e.yml index ce463a3..d68aea6 100644 --- a/.gitea/workflows/e2e.yml +++ b/.gitea/workflows/e2e.yml @@ -60,6 +60,13 @@ jobs: working-directory: web run: npm ci + - name: Work around runner clock skew for apt + run: | + sudo tee /etc/apt/apt.conf.d/99no-check-valid-until >/dev/null <<'EOF' + Acquire::Check-Valid-Until "false"; + Acquire::Check-Date "false"; + EOF + - name: Install Playwright browsers working-directory: web run: npx playwright install --with-deps chromium diff --git a/web/e2e/tests/campaign/arc-delete.spec.ts b/web/e2e/tests/campaign/arc-delete.spec.ts new file mode 100644 index 0000000..20544ce --- /dev/null +++ b/web/e2e/tests/campaign/arc-delete.spec.ts @@ -0,0 +1,49 @@ +import { test, expect } from '@playwright/test'; +import { + seedCampaign, + seedArc, + deleteCampaign, + type SeededCampaign, + type SeededArc, +} from '../../fixtures/api'; + +test.describe('Arc delete', () => { + 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('deletes an arc after accepting confirm and redirects to the campaign', async ({ + page, + request, + }) => { + page.on('dialog', (dialog) => dialog.accept()); + + await page.goto(`/campaigns/${campaign.id}/arcs/${arc.id}/edit`); + await page.getByRole('button', { name: /^Supprimer$/i }).click(); + + await expect(page).toHaveURL(new RegExp(`/campaigns/${campaign.id}$`)); + + const res = await request.get(`/api/arcs/${arc.id}`); + expect(res.status()).toBe(404); + }); + + test('keeps the arc when confirm is dismissed', async ({ page, request }) => { + page.on('dialog', (dialog) => dialog.dismiss()); + + await page.goto(`/campaigns/${campaign.id}/arcs/${arc.id}/edit`); + await page.getByRole('button', { name: /^Supprimer$/i }).click(); + + await expect(page).toHaveURL(new RegExp(`/campaigns/${campaign.id}/arcs/${arc.id}/edit$`)); + + const res = await request.get(`/api/arcs/${arc.id}`); + expect(res.ok()).toBeTruthy(); + }); +}); diff --git a/web/e2e/tests/campaign/campaign-delete.spec.ts b/web/e2e/tests/campaign/campaign-delete.spec.ts new file mode 100644 index 0000000..1b48515 --- /dev/null +++ b/web/e2e/tests/campaign/campaign-delete.spec.ts @@ -0,0 +1,41 @@ +import { test, expect } from '@playwright/test'; +import { seedCampaign, deleteCampaign, type SeededCampaign } from '../../fixtures/api'; + +test.describe('Campaign delete', () => { + let campaign: SeededCampaign; + + test.beforeEach(async ({ request }) => { + campaign = await seedCampaign(request); + }); + + test.afterEach(async ({ request }) => { + if (campaign?.id) await deleteCampaign(request, campaign.id); + }); + + test('deletes a campaign after accepting confirm and redirects to the list', async ({ + page, + request, + }) => { + page.on('dialog', (dialog) => dialog.accept()); + + await page.goto(`/campaigns/${campaign.id}`); + await page.getByRole('button', { name: /^Supprimer$/i }).click(); + + await expect(page).toHaveURL(/\/campaigns$/); + + const res = await request.get(`/api/campaigns/${campaign.id}`); + expect(res.status()).toBe(404); + }); + + test('keeps the campaign when confirm is dismissed', async ({ page, request }) => { + page.on('dialog', (dialog) => dialog.dismiss()); + + await page.goto(`/campaigns/${campaign.id}`); + await page.getByRole('button', { name: /^Supprimer$/i }).click(); + + await expect(page).toHaveURL(new RegExp(`/campaigns/${campaign.id}$`)); + + const res = await request.get(`/api/campaigns/${campaign.id}`); + expect(res.ok()).toBeTruthy(); + }); +}); diff --git a/web/e2e/tests/lore/lore-delete.spec.ts b/web/e2e/tests/lore/lore-delete.spec.ts new file mode 100644 index 0000000..ea3b2f1 --- /dev/null +++ b/web/e2e/tests/lore/lore-delete.spec.ts @@ -0,0 +1,53 @@ +import { test, expect } from '@playwright/test'; +import { seedLoreWithFolder, deleteLore, type SeededLore } from '../../fixtures/api'; + +test.describe('Lore delete', () => { + let seeded: SeededLore; + + test.beforeEach(async ({ request }) => { + seeded = await seedLoreWithFolder(request); + }); + + test.afterEach(async ({ request }) => { + // Best-effort cleanup — ne fait rien si déjà supprimé par le test. + if (seeded?.id) await deleteLore(request, seeded.id); + }); + + test('deletes a lore after accepting the confirm and redirects to the list', async ({ + page, + request, + }) => { + let confirmMessage = ''; + page.on('dialog', async (dialog) => { + confirmMessage = dialog.message(); + await dialog.accept(); + }); + + await page.goto(`/lore/${seeded.id}`); + await page.getByRole('button', { name: /^Supprimer$/i }).click(); + + // Attente du dialog et du retour sur la liste des lores. + await expect(page).toHaveURL(/\/lore$/); + expect(confirmMessage).toContain(seeded.name); + // Lore contient un dossier seedé : le récapitulatif doit l'indiquer. + expect(confirmMessage).toMatch(/1 dossier/i); + + const res = await request.get(`/api/lores/${seeded.id}`); + expect(res.status()).toBe(404); + }); + + test('keeps the lore when the confirm is dismissed', async ({ page, request }) => { + page.on('dialog', async (dialog) => { + await dialog.dismiss(); + }); + + await page.goto(`/lore/${seeded.id}`); + await page.getByRole('button', { name: /^Supprimer$/i }).click(); + + // On reste sur le détail, le titre du lore est toujours visible. + await expect(page.locator('.detail-header h1')).toHaveText(seeded.name); + + const res = await request.get(`/api/lores/${seeded.id}`); + expect(res.ok()).toBeTruthy(); + }); +}); diff --git a/web/e2e/tests/lore/page-delete.spec.ts b/web/e2e/tests/lore/page-delete.spec.ts new file mode 100644 index 0000000..867e40b --- /dev/null +++ b/web/e2e/tests/lore/page-delete.spec.ts @@ -0,0 +1,58 @@ +import { test, expect } from '@playwright/test'; +import { + seedLoreWithFolder, + seedTemplate, + seedPage, + deleteLore, + type SeededLore, + type SeededTemplate, + type SeededPage, +} from '../../fixtures/api'; + +test.describe('Page delete', () => { + 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, + }); + 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('deletes the page after accepting confirm', async ({ page, request }) => { + page.on('dialog', (dialog) => dialog.accept()); + + await page.goto(`/lore/${seeded.id}/pages/${pageEntity.id}/edit`); + await page.getByRole('button', { name: /^Supprimer$/i }).click(); + + // Le composant redirige vers la racine du Lore après suppression. + await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}$`)); + + const res = await request.get(`/api/pages/${pageEntity.id}`); + expect(res.status()).toBe(404); + }); + + test('keeps the page when confirm is dismissed', async ({ page, request }) => { + page.on('dialog', (dialog) => dialog.dismiss()); + + await page.goto(`/lore/${seeded.id}/pages/${pageEntity.id}/edit`); + await page.getByRole('button', { name: /^Supprimer$/i }).click(); + + await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}/pages/${pageEntity.id}/edit$`)); + + const res = await request.get(`/api/pages/${pageEntity.id}`); + expect(res.ok()).toBeTruthy(); + }); +}); diff --git a/web/e2e/tests/lore/template-delete.spec.ts b/web/e2e/tests/lore/template-delete.spec.ts new file mode 100644 index 0000000..fc17f9b --- /dev/null +++ b/web/e2e/tests/lore/template-delete.spec.ts @@ -0,0 +1,51 @@ +import { test, expect } from '@playwright/test'; +import { + seedLoreWithFolder, + seedTemplate, + deleteLore, + getTemplatesForLore, + type SeededLore, + type SeededTemplate, +} from '../../fixtures/api'; + +test.describe('Template delete', () => { + let seeded: SeededLore; + let template: SeededTemplate; + + test.beforeEach(async ({ request }) => { + seeded = await seedLoreWithFolder(request); + template = await seedTemplate(request, { + loreId: seeded.id, + defaultNodeId: seeded.rootFolderId, + }); + }); + + test.afterEach(async ({ request }) => { + if (seeded?.id) await deleteLore(request, seeded.id); + }); + + test('deletes the template after accepting confirm', async ({ page, request }) => { + page.on('dialog', (dialog) => dialog.accept()); + + await page.goto(`/lore/${seeded.id}/templates/${template.id}`); + await page.locator('.page-header .btn-danger').click(); + + await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}$`)); + + const templates = await getTemplatesForLore(request, seeded.id); + expect(templates.find((t) => t.id === template.id)).toBeUndefined(); + }); + + test('keeps the template when confirm is dismissed', async ({ page, request }) => { + page.on('dialog', (dialog) => dialog.dismiss()); + + await page.goto(`/lore/${seeded.id}/templates/${template.id}`); + await page.locator('.page-header .btn-danger').click(); + + // On reste sur l'écran d'édition (l'URL ne change pas). + await expect(page).toHaveURL(new RegExp(`/lore/${seeded.id}/templates/${template.id}$`)); + + const templates = await getTemplatesForLore(request, seeded.id); + expect(templates.find((t) => t.id === template.id)).toBeDefined(); + }); +});