Test onboarding visibility in production builds
All checks were successful
Deploy Sokko G / deploy (push) Successful in 19s

This commit is contained in:
Shinuwa 2026-09-13 20:55:46 +02:00
parent 6de016ec7b
commit 713bc3b70e
9 changed files with 87 additions and 4 deletions

View file

@ -46,3 +46,4 @@
{"id":"int-cc87d51843f7ee62beb6445939aa30d5","kind":"field_change","created_at":"2026-09-13T06:35:52.804580049Z","actor":"Shinuwa","issue_id":"sokko-g-zsd","extra":{"field":"status","new_value":"open","old_value":"in_progress"}} {"id":"int-cc87d51843f7ee62beb6445939aa30d5","kind":"field_change","created_at":"2026-09-13T06:35:52.804580049Z","actor":"Shinuwa","issue_id":"sokko-g-zsd","extra":{"field":"status","new_value":"open","old_value":"in_progress"}}
{"id":"int-e554bed2958d709f2965a55fea9aa956","kind":"field_change","created_at":"2026-09-13T06:53:17.211928645Z","actor":"Shinuwa","issue_id":"sokko-g-zsd","extra":{"field":"status","new_value":"open","old_value":"in_progress"}} {"id":"int-e554bed2958d709f2965a55fea9aa956","kind":"field_change","created_at":"2026-09-13T06:53:17.211928645Z","actor":"Shinuwa","issue_id":"sokko-g-zsd","extra":{"field":"status","new_value":"open","old_value":"in_progress"}}
{"id":"int-6f399629fa8cc8212d36ca21853eaa52","kind":"field_change","created_at":"2026-09-13T17:46:00.430152831Z","actor":"Shinuwa","issue_id":"sokko-g-zsd","extra":{"field":"status","new_value":"open","old_value":"in_progress"}} {"id":"int-6f399629fa8cc8212d36ca21853eaa52","kind":"field_change","created_at":"2026-09-13T17:46:00.430152831Z","actor":"Shinuwa","issue_id":"sokko-g-zsd","extra":{"field":"status","new_value":"open","old_value":"in_progress"}}
{"id":"int-3823a391b02ae7a3d58956b24c10e998","kind":"field_change","created_at":"2026-09-13T18:43:29.457174095Z","actor":"Shinuwa","issue_id":"sokko-g-zsd","extra":{"field":"status","new_value":"open","old_value":"in_progress"}}

View file

@ -61,13 +61,22 @@ npm run check
Cette commande vérifie la configuration Node/Vite, lance les tests, puis exécute le build React. Cette commande vérifie la configuration Node/Vite, lance les tests, puis exécute le build React.
Pour inclure les parcours navigateur, lancer `npm run check:full`. Les deux Pour inclure les parcours navigateur, lancer `npm run check:full`. Le check, les
validations sont exécutées successivement pour éviter la concurrence avec le build. E2E de développement et les E2E de production sont exécutés successivement pour
éviter la concurrence avec le build.
La suite E2E utilise deux workers par défaut pour limiter la contention lors du La suite E2E utilise deux workers par défaut pour limiter la contention lors du
chargement initial des outils par Vite. Le parallélisme reste ajustable avec chargement initial des outils par Vite. Le parallélisme reste ajustable avec
`npm run test:e2e -- --workers=4`. Les traces des tests en échec sont conservées `npm run test:e2e -- --workers=4`. Les traces des tests en échec sont conservées
dans `test-results` et consultables depuis le rapport Playwright. dans `test-results` et consultables depuis le rapport Playwright.
`npm run test:e2e:production` reconstruit l'application puis teste les comportements
propres à la production, notamment la visibilité de la prise en main. Cette suite
utilise temporairement `127.0.0.1:4173` ; le port doit être libre, aucune instance
existante n'est réutilisée. Le navigateur est isolé des données personnelles et
le serveur est fermé à la fin. Ses diagnostics sont conservés séparément dans
`test-results/production` et `playwright-report/production`. La suite habituelle
`npm run test:e2e` reste sur Vite en développement et exclut ces scénarios.
L'application est enveloppée dans `StrictMode` : en développement (y compris les L'application est enveloppée dans `StrictMode` : en développement (y compris les
E2E sur Vite), React rejoue certains rendus et cycles d'effets pour détecter les E2E sur Vite), React rejoue certains rendus et cycles d'effets pour détecter les
effets de bord et les nettoyages manquants. Ces contrôles supplémentaires ne effets de bord et les nettoyages manquants. Ces contrôles supplémentaires ne

View file

@ -79,3 +79,9 @@ Le check doit passer avant de pousser :
- génération des index de listes ; - génération des index de listes ;
- tests Node ; - tests Node ;
- build de production. - build de production.
`npm run check:full` ajoute les parcours navigateur de développement puis de
production. Pour la prise en main de la home, vérifier sa présence sans toolbox,
sa disparition après création en production, son retour après suppression de la
dernière toolbox et la persistance de ces états après rechargement. Elle reste
visible en développement et sur la page À propos.

View file

@ -71,6 +71,10 @@ test("home onboarding creates the initial tools and starts the tutorial", async
await expect(page.locator('[data-module-type="memos"]')).toBeVisible(); await expect(page.locator('[data-module-type="memos"]')).toBeVisible();
await expect(page.locator('[data-module-type="links"]')).toBeVisible(); await expect(page.locator('[data-module-type="links"]')).toBeVisible();
await expect(page.locator(".toolbox-tutorial-count")).toHaveText(/^1\//); await expect(page.locator(".toolbox-tutorial-count")).toHaveText(/^1\//);
await page.goto("/#/");
await expect(page.getByRole("region", { name: content.title, exact: true })).toBeVisible();
await page.reload();
await expect(page.getByRole("region", { name: content.title, exact: true })).toBeVisible();
}); });
test("about reuses onboarding without creation and exposes tool examples and destinations", async ({ page }) => { test("about reuses onboarding without creation and exposes tool examples and destinations", async ({ page }) => {

View file

@ -0,0 +1,41 @@
// Rôle : vérifie la visibilité de la prise en main sur le vrai build après création et suppression persistées.
import { test, expect } from "@playwright/test";
import { readFile } from "node:fs/promises";
const site = JSON.parse(await readFile("website/public/data/site.json", "utf8"));
test("production home hides onboarding after creation and restores it after deleting the last toolbox", async ({ page }) => {
const errors = [];
page.on("pageerror", (error) => errors.push(error.message));
await page.goto("/");
const onboarding = page.getByRole("region", { name: site.home.onboarding.title, exact: true });
await expect(onboarding).toBeVisible();
await expect(page.locator('script[type="module"][src^="/assets/"]')).toHaveCount(1);
await expect(page.locator('script[src*="/@vite/client"]')).toHaveCount(0);
await onboarding.getByRole("button", { name: site.home.onboarding.primaryAction, exact: true }).click();
const dialog = page.getByRole("dialog");
await dialog.getByRole("textbox", { name: "Nom", exact: true }).fill("Production toolbox");
await dialog.getByRole("button", { name: "Créer", exact: true }).click();
await expect(page).toHaveURL(/#\/toolbox\//);
await expect(page.locator('[data-module-type="memos"]')).toBeVisible();
await expect(page.locator('[data-module-type="links"]')).toBeVisible();
await page.goto("/#/");
await expect(page.getByRole("heading", { name: site.home.hero.title, exact: true })).toBeVisible();
await expect(onboarding).toHaveCount(0);
await page.reload();
await expect(page.getByRole("heading", { name: site.home.hero.title, exact: true })).toBeVisible();
await expect(onboarding).toHaveCount(0);
await page.goto("/#/about");
await expect(onboarding).toBeVisible();
await page.goto("/#/toolboxes");
await page.getByRole("button", { name: "Supprimer Production toolbox", exact: true }).click();
await page.getByRole("dialog").getByRole("button", { name: "Supprimer", exact: true }).click();
await expect(page.locator(".toolbox-card")).toHaveCount(0);
await page.goto("/#/");
await expect(onboarding).toBeVisible();
await page.reload();
await expect(onboarding).toBeVisible();
expect(errors).toEqual([]);
});

View file

@ -11,9 +11,10 @@
"lists:index": "node scripts/generate-list-indexes.mjs", "lists:index": "node scripts/generate-list-indexes.mjs",
"lint": "eslint .", "lint": "eslint .",
"check": "node --check server.mjs && node --check vite.config.js && npm run lint && npm run lists:index && npm test && npm run build", "check": "node --check server.mjs && node --check vite.config.js && npm run lint && npm run lists:index && npm test && npm run build",
"check:full": "npm run check && npm run test:e2e", "check:full": "npm run check && npm run test:e2e && npm run test:e2e:production",
"test": "node --test", "test": "node --test",
"test:e2e": "playwright test", "test:e2e": "playwright test",
"test:e2e:production": "npm run build && playwright test --config playwright.production.config.js",
"profile:toolboxes": "npm run build && node scripts/profile-toolbox-loading.mjs" "profile:toolboxes": "npm run build && node scripts/profile-toolbox-loading.mjs"
}, },
"dependencies": { "dependencies": {

View file

@ -3,6 +3,7 @@ import { defineConfig, devices } from "@playwright/test";
export default defineConfig({ export default defineConfig({
testDir: "./e2e", testDir: "./e2e",
testIgnore: "**/production/**",
fullyParallel: true, fullyParallel: true,
// Le serveur Vite partagé compile les outils lazy à la première visite. // Le serveur Vite partagé compile les outils lazy à la première visite.
workers: 2, workers: 2,

View file

@ -0,0 +1,21 @@
// Rôle : vérifie le build de production sans réutiliser une instance Vite de développement.
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./e2e/production",
workers: 1,
outputDir: "test-results/production",
reporter: [["list"], ["html", { open: "never", outputFolder: "playwright-report/production" }]],
use: {
baseURL: "http://127.0.0.1:4173",
trace: "retain-on-failure",
screenshot: "only-on-failure"
},
webServer: {
command: "npm run preview -- --host 127.0.0.1 --port 4173 --strictPort",
url: "http://127.0.0.1:4173",
reuseExistingServer: false,
timeout: 120000
},
projects: [{ name: "chromium-production", use: { ...devices["Desktop Chrome"] } }]
});

View file

@ -30,7 +30,6 @@ test("vite entrypoint and app shell are wired", async () => {
assert.match(overlays, /uid\("marker"\)/); assert.match(overlays, /uid\("marker"\)/);
assert.match(imageViewer, /export function ImageViewer/); assert.match(imageViewer, /export function ImageViewer/);
assert.match(imageViewer, /function openImageInNewTab/); assert.match(imageViewer, /function openImageInNewTab/);
assert.match(homePage, /import\.meta\.env\.DEV \|\| toolboxes\.length === 0/);
assert.match(homePage, /dragon-home\.png/); assert.match(homePage, /dragon-home\.png/);
assert.match(aboutPage, /module-icon-\$\{group\.icon \|\| "notepad"\}/); assert.match(aboutPage, /module-icon-\$\{group\.icon \|\| "notepad"\}/);
}); });