// Tests de l'accueil et de l'invitation à installer, sans navigateur : // premier lancement, règles d'apparition de la carte d'installation, // réglages enregistrés par une version précédente. // // 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 { installPlatform } from "../src/ui/install.js"; import { DEFAULT_SETTINGS, INSTALL_AGAIN_AFTER, installCardShown, isFirstLaunch, parseSettings, type Settings, } from "../src/ui/settings.js"; test("réglages d'une version précédente : les nouveaux champs prennent leur valeur par défaut", () => { const old = parseSettings('{"vibrations": false, "theme": "dark", "gamesStarted": 4}'); assert.equal(old.welcomed, false); assert.equal(old.gamesFinished, 0); assert.equal(old.tutorialSeen, 0); assert.equal(old.installed, false); const saved = parseSettings( '{"welcomed": true, "gamesFinished": 2, "tutorialSeen": 5, "installDismissals": 1, ' + '"installDismissedAt": 2, "installed": true}', ); assert.deepEqual(saved, { ...DEFAULT_SETTINGS, welcomed: true, gamesFinished: 2, tutorialSeen: 5, installDismissals: 1, installDismissedAt: 2, installed: true, }); assert.equal(parseSettings('{"tutorialSeen": -3, "welcomed": "oui"}').tutorialSeen, 0); }); test("premier lancement : tant que rien n'a été fait, une seule fois", () => { assert.ok(isFirstLaunch(DEFAULT_SETTINGS)); // La partie commencée d'office au chargement ne compte pas. assert.ok(isFirstLaunch({ ...DEFAULT_SETTINGS, gamesStarted: 1 })); assert.ok(!isFirstLaunch({ ...DEFAULT_SETTINGS, welcomed: true })); assert.ok(!isFirstLaunch({ ...DEFAULT_SETTINGS, gamesStarted: 2 })); assert.ok(!isFirstLaunch({ ...DEFAULT_SETTINGS, gamesFinished: 1 })); assert.ok(!isFirstLaunch({ ...DEFAULT_SETTINGS, tutorialSeen: 1 })); }); test("invitation à installer : après la première partie terminée, une seule relance", () => { const at = (changes: Partial, installed = false) => installCardShown({ ...DEFAULT_SETTINGS, ...changes }, installed); assert.ok(!at({}), "jamais avant la première partie terminée"); assert.ok(at({ gamesFinished: 1 })); assert.ok(!at({ gamesFinished: 1 }, true), "jamais une fois installée"); assert.ok(!at({ gamesFinished: 1, installed: true })); // « Plus tard » : masquée, puis de retour après trois parties de plus. const later = { installDismissals: 1, installDismissedAt: 1 }; assert.equal(INSTALL_AGAIN_AFTER, 3); assert.ok(!at({ ...later, gamesFinished: 1 })); assert.ok(!at({ ...later, gamesFinished: 3 })); assert.ok(at({ ...later, gamesFinished: 4 })); // Une seconde fois « Plus tard » : plus jamais sur l'accueil. assert.ok(!at({ installDismissals: 2, installDismissedAt: 4, gamesFinished: 40 })); }); test("la marche à suivre pour installer dépend du navigateur", () => { const iphone = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 " + "(KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1"; const ipad = "Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X) AppleWebKit/605.1.15"; const firefox = "Mozilla/5.0 (Android 14; Mobile; rv:131.0) Gecko/131.0 Firefox/131.0"; const chrome = "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/130.0 Mobile Safari/537.36"; assert.equal(installPlatform(iphone), "ios"); assert.equal(installPlatform(ipad), "ios"); assert.equal(installPlatform(firefox), "firefox"); // Sans invite du système, Chrome suit la marche à suivre de Firefox // (menu ⋮, « Installer »), la plus proche de la sienne. assert.equal(installPlatform(chrome), "firefox"); });