mediaDrop/tests/pages.test.ts
Nicolas 0962c2cc1f
All checks were successful
Publier l'image Docker / test (push) Successful in 22s
Publier l'image Docker / publish (push) Successful in 12s
Page de connexion distincte avant la page de dépôt
`GET /` décide côté serveur, à chaque requête, laquelle des deux pages
servir : `public/login.html` (aucune session valide — juste le bouton
« Se connecter », aucune zone de dépôt dans le HTML) ou
`public/upload.html` (session valide — nom d'utilisateur injecté et
échappé, bouton « Se déconnecter »). Nouveau `src/routes/pages.ts`.

Simplifications côté page de dépôt, permises par cette garantie
serveur : plus de fetch `/auth/me` ni de bascule `authenticated` en
JS, plus de retrait/réinsertion de la zone de dépôt dans le DOM — elle
est toujours présente puisque la page n'est servie qu'aux sessions
valides. Une session qui expire en cours d'usage (401 sur /targets ou
/upload*) renvoie vers `/?login=expired`, qui affiche alors la vraie
page de connexion avec le message adapté.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 20:49:21 +02:00

97 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { FastifyInstance } from 'fastify';
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
import { buildApp } from '../src/app.js';
import type { OidcConfig } from '../src/config.js';
import {
login,
makeConfig,
makeTempDir,
oneTarget,
removeDir,
SESSION_COOKIE,
startStubIdp,
type StubIdp,
} from './helpers.js';
let idp: StubIdp;
let app: FastifyInstance;
let uploadDir: string;
function oidcOverride(ref: StubIdp): OidcConfig {
return {
issuer: ref.issuer,
clientId: ref.clientId,
clientSecret: ref.clientSecret,
scopes: ['openid', 'profile', 'email'],
};
}
beforeAll(async () => {
idp = await startStubIdp();
});
afterAll(async () => {
await idp.close();
});
beforeEach(async () => {
uploadDir = await makeTempDir();
app = await buildApp(makeConfig({ targets: oneTarget(uploadDir), oidc: oidcOverride(idp) }), {
logger: false,
});
await app.ready();
});
afterEach(async () => {
await app.close();
await removeDir(uploadDir);
});
describe('GET /', () => {
it('sans session : sert la page de connexion, pas la page de dépôt', async () => {
const res = await app.inject({ method: 'GET', url: '/' });
expect(res.statusCode).toBe(200);
expect(res.headers['content-type']).toContain('text/html');
expect(res.body).toContain('href="/auth/login"');
expect(res.body).not.toContain('id="dropzone"');
expect(res.body).not.toContain('Se déconnecter');
});
it('avec session valide : sert la page de dépôt avec le nom injecté', async () => {
const cookie = await login(app, idp, { name: 'Alice' });
const res = await app.inject({
method: 'GET',
url: '/',
cookies: { [SESSION_COOKIE]: cookie },
});
expect(res.statusCode).toBe(200);
expect(res.body).toContain('Connecté : Alice');
expect(res.body).toContain('Se déconnecter');
expect(res.body).toContain('id="dropzone"');
expect(res.body).not.toContain('{{USER_NAME}}');
expect(res.body).not.toContain('href="/auth/login"');
});
it('échappe le nom d’utilisateur avant de l’insérer dans le HTML', async () => {
const cookie = await login(app, idp, { name: '<script>alert(1)</script>' });
const res = await app.inject({
method: 'GET',
url: '/',
cookies: { [SESSION_COOKIE]: cookie },
});
expect(res.body).not.toContain('<script>alert(1)</script>');
expect(res.body).toContain('&lt;script&gt;alert(1)&lt;/script&gt;');
});
it('session invalide (cookie illisible) : retombe sur la page de connexion', async () => {
const res = await app.inject({
method: 'GET',
url: '/',
cookies: { [SESSION_COOKIE]: 'cookie-bidon' },
});
expect(res.statusCode).toBe(200);
expect(res.body).toContain('href="/auth/login"');
expect(res.body).not.toContain('id="dropzone"');
});
});