146 lines
5.3 KiB
TypeScript
146 lines
5.3 KiB
TypeScript
// Tests du routeur (app/src/ui/router.ts) : chaque adresse du tableau de
|
|
// l'ADR 0013 (amendement du 2026-11-03) mène au bon écran, et chaque écran
|
|
// redonne son adresse.
|
|
//
|
|
// Copyright (C) 2026 les auteurs d'Ustagammon.
|
|
// Licence : GPLv3 ou ultérieure (voir LICENSE).
|
|
|
|
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
import {
|
|
isHelpScreen,
|
|
parseRoute,
|
|
type Route,
|
|
routeHash,
|
|
slug,
|
|
stepsBack,
|
|
} from "../src/ui/router.js";
|
|
|
|
const START = "4HPwATDgc/ABMA";
|
|
const MATCH = "cAkAAAAAAAAA";
|
|
|
|
/** Les adresses du tableau de l'ADR, et l'écran de chacune. */
|
|
const TABLE: [string, Route][] = [
|
|
["#/", { screen: "game" }],
|
|
["#/accueil", { screen: "home" }],
|
|
["#/aide", { screen: "help", question: null }],
|
|
["#/aide/comment-jouer", { screen: "help", question: "comment-jouer" }],
|
|
["#/glossaire", { screen: "glossary", term: null }],
|
|
["#/glossaire/videau", { screen: "glossary", term: "videau" }],
|
|
["#/a-propos", { screen: "about" }],
|
|
["#/tutoriel", { screen: "tutorial", step: null, practice: false }],
|
|
["#/tutoriel/3", { screen: "tutorial", step: 3, practice: false }],
|
|
["#/tutoriel/3/a-vous", { screen: "tutorial", step: 3, practice: true }],
|
|
["#/tutoriel/fin", { screen: "tutorialEnd" }],
|
|
["#/installer", { screen: "install" }],
|
|
["#/parties", { screen: "games", game: null }],
|
|
["#/parties/mh2k3x-a1b2c3", { screen: "games", game: "mh2k3x-a1b2c3" }],
|
|
["#/menu", { screen: "menu" }],
|
|
[
|
|
"#/position/4HPwATDgc%2FABMA/cAkAAAAAAAAA",
|
|
{ screen: "position", position: START, match: MATCH },
|
|
],
|
|
["#/position/4HPwATDgc%2FABMA", { screen: "position", position: START, match: null }],
|
|
];
|
|
|
|
test("chaque adresse du tableau de l'ADR 0013 mène à son écran, et inversement", () => {
|
|
for (const [hash, route] of TABLE) {
|
|
assert.deepEqual(parseRoute(hash), route, hash);
|
|
assert.equal(routeHash(route), hash, hash);
|
|
}
|
|
});
|
|
|
|
test("une adresse vide ou « # » est la partie en cours ; une barre finale ne change rien", () => {
|
|
assert.deepEqual(parseRoute(""), { screen: "game" });
|
|
assert.deepEqual(parseRoute("#"), { screen: "game" });
|
|
assert.deepEqual(parseRoute("#/aide/"), { screen: "help", question: null });
|
|
assert.deepEqual(parseRoute("#/glossaire/blot/"), { screen: "glossary", term: "blot" });
|
|
});
|
|
|
|
test("une adresse inconnue est reconnue comme telle", () => {
|
|
for (const hash of [
|
|
"#/inconnu",
|
|
"#aide",
|
|
"#/aide/a/b",
|
|
"#/aide/Majuscules",
|
|
"#/menu/plus",
|
|
"#/tutoriel/0",
|
|
"#/tutoriel/x",
|
|
"#/tutoriel/3/b",
|
|
"#/tutoriel/3/a-vous/x",
|
|
"#/tutoriel/fin/a-vous",
|
|
"#/installer/x",
|
|
"#/a-propos/x",
|
|
"#/position",
|
|
"#/position/%E0%A4%A",
|
|
]) {
|
|
assert.equal(parseRoute(hash).screen, "unknown", hash);
|
|
}
|
|
assert.equal(routeHash(parseRoute("#/inconnu")), "#/inconnu");
|
|
});
|
|
|
|
test("un lien de position collé sans encodage (+ et / du base64) est compris", () => {
|
|
assert.deepEqual(parseRoute(`#/position/${START}/${MATCH}`), {
|
|
screen: "position",
|
|
position: START,
|
|
match: MATCH,
|
|
});
|
|
assert.deepEqual(parseRoute("#/position/jGfkASjg8wcBMA/cAkAAAAAAAAA"), {
|
|
screen: "position",
|
|
position: "jGfkASjg8wcBMA",
|
|
match: MATCH,
|
|
});
|
|
// + reste un + (pas une espace), encodé ou non.
|
|
const plus = "AAAA+AAAAAAAAA";
|
|
assert.equal(
|
|
routeHash({ screen: "position", position: plus, match: null }),
|
|
"#/position/AAAA%2BAAAAAAAAA",
|
|
);
|
|
assert.deepEqual(parseRoute("#/position/AAAA%2BAAAAAAAAA"), {
|
|
screen: "position",
|
|
position: plus,
|
|
match: null,
|
|
});
|
|
assert.deepEqual(parseRoute(`#/position/${plus}`), {
|
|
screen: "position",
|
|
position: plus,
|
|
match: null,
|
|
});
|
|
});
|
|
|
|
test("un identifiant de position de mauvaise longueur arrive tel quel au moteur, qui jugera", () => {
|
|
assert.deepEqual(parseRoute("#/position/abc"), {
|
|
screen: "position",
|
|
position: "abc",
|
|
match: null,
|
|
});
|
|
});
|
|
|
|
test("l'identifiant d'une question : le titre sans accents ni ponctuation", () => {
|
|
assert.equal(slug("Jouer un coup, puis l'annuler"), "jouer-un-coup-puis-l-annuler");
|
|
assert.equal(slug(" Été : déjà vu ? "), "ete-deja-vu");
|
|
assert.equal(
|
|
slug("Rejouer d'ici sans perdre l'original"),
|
|
"rejouer-d-ici-sans-perdre-l-original",
|
|
);
|
|
});
|
|
|
|
test("les écrans de l'aide : jouer, les mots du jeu, à propos", () => {
|
|
assert.ok(isHelpScreen(parseRoute("#/aide/x")));
|
|
assert.ok(isHelpScreen(parseRoute("#/glossaire")));
|
|
assert.ok(isHelpScreen(parseRoute("#/a-propos")));
|
|
assert.ok(!isHelpScreen(parseRoute("#/menu")));
|
|
});
|
|
|
|
test("quitter un écran revient à la dernière adresse qui n'en est pas", () => {
|
|
const help = (route: Route) => isHelpScreen(route);
|
|
// Partie, aide, une question : « Retour » remonte de deux pas.
|
|
assert.equal(stepsBack(["#/", "#/aide", "#/aide/q"], help), -2);
|
|
// Le menu ouvert depuis l'aide : on revient à l'aide.
|
|
const menu = (route: Route) => route.screen === "menu";
|
|
assert.equal(stepsBack(["#/", "#/aide", "#/menu"], menu), -1);
|
|
// Ouvert directement dans un nouvel onglet : rien avant, on remplace.
|
|
assert.equal(stepsBack(["#/aide/q"], help), null);
|
|
assert.equal(stepsBack(["#/aide", "#/glossaire/blot"], help), null);
|
|
});
|