gestion_table_tournante_libre/test/installation.test.js
Mathieu Benoit b9090508ff [ADD] run: make and ./run.sh, Electron start check, container matrix
make with no target does what ./run.sh does: Electron on a graphical
session, Vite's server without one or where Electron is not published
(s390x, ppc64le). Every npm, npx and node recipe goes through the
project's Node. make essai_demarrage starts the real Electron through
Playwright and reads its window title, on Chromium's headless Ozone
platform when there is no display: no X server is needed.
scripts/verifier_systemes.sh replays install and tests in podman over
the QEMU catalogue of TODO, and stops only its own containers.

Checked: 17 Linux images and Nix pass all six steps; pwsh parses.

--- FR ---

[ADD] lancement : make et ./run.sh, essai d'Electron, matrice

make sans cible fait ce que fait ./run.sh : Electron sur une session
graphique, le serveur de Vite sans elle ou là où Electron n'est pas
publié (s390x, ppc64le). Chaque recette npm, npx ou node passe par le
Node du projet. make essai_demarrage lance la vraie coquille Electron
par Playwright et lit le titre de sa fenêtre, sur la plateforme Ozone
headless de Chromium sans écran : aucun serveur X n'est requis.
scripts/verifier_systemes.sh rejoue installation et épreuves sous
podman sur le catalogue QEMU de TODO, et n'arrête que ses conteneurs.

Vérifié : 17 images Linux et Nix passent les six étapes ; pwsh analyse.

Assisted-by: Claude Opus 5.5
Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
2026-10-06 05:52:17 -04:00

112 lines
5.2 KiB
JavaScript
Raw Permalink 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.

// © 2026 TechnoLibre (http://www.technolibre.ca)
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
// Les points d'entrée de l'installation et du lancement : ./install.sh,
// ./install_dev.sh, ./run.sh et le Makefile sous Linux et macOS ;
// install.cmd, install_dev.cmd et run.cmd sous Windows, qui passent la main
// à PowerShell ; shell.nix sous NixOS. Ces épreuves gardent ce qui les casse
// sans rien faire échouer ailleurs : un bit d'exécution perdu, un chemin
// renommé, une commande npm qui contourne le Node du projet, une fin de ligne
// d'Unix dans un fichier de commandes, une marque d'ordre d'octets absente.
import assert from 'node:assert/strict';
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, test } from './lanceur.js';
const RACINE = fileURLToPath(new URL('..', import.meta.url));
const lire = (chemin) => readFileSync(join(RACINE, chemin), 'utf8');
const SCRIPTS_BASH = ['install.sh', 'install_dev.sh', 'run.sh', 'scripts/installation/avec_node.sh'];
describe('installation : les scripts bash', () => {
test('ils sont exécutables, et cherchent bash dans le PATH : /bin/bash manque sous NixOS', () => {
for (const script of SCRIPTS_BASH) {
assert.equal(lire(script).split('\n')[0], '#!/usr/bin/env bash', script);
if (process.platform !== 'win32') {
assert.equal(statSync(join(RACINE, script)).mode & 0o111, 0o111, `${script} n'est pas exécutable`);
}
}
});
test('ils sourcent la bibliothèque commune, et s’arrêtent à la première erreur', () => {
for (const script of SCRIPTS_BASH) {
const texte = lire(script);
assert.match(texte, /^set -euo pipefail$/m, script);
assert.match(texte, /commun\.sh/, script);
}
});
});
describe('installation : le Makefile', () => {
const makefile = lire('Makefile');
const recettes = makefile.split('\n').filter((ligne) => ligne.startsWith('\t'));
test('make sans cible lance l’application, comme ./run.sh', () => {
assert.match(makefile, /^\.DEFAULT_GOAL := run$/m);
assert.match(makefile, /^LANCER := \.\/run\.sh$/m);
assert.match(makefile, /^run: .*\n\t\$\(LANCER\)$/m);
});
test('chaque script que le Makefile nomme existe', () => {
const chemins = [...makefile.matchAll(/(?:^|[\s=])(?:\.\/)?((?:scripts\/[\w/.-]+\.(?:sh|ps1|js))|[\w-]+\.sh)\b/gm)]
.map(([, chemin]) => chemin);
assert.ok(chemins.length > 0, 'aucun script relevé');
assert.deepEqual(chemins.filter((chemin) => !existsSync(join(RACINE, chemin))), []);
});
test('npm, npx et node ne s’y lancent que par avec_node, qui pose le Node du projet', () => {
const directes = recettes.filter((ligne) => /\b(?:npm|npx|node)\b/.test(ligne) && !ligne.includes('$(AVEC_NODE)'));
assert.ok(recettes.length > 0, 'aucune recette relevée');
assert.deepEqual(directes, []);
});
});
describe('installation : Windows', () => {
const commandes = readdirSync(RACINE).filter((nom) => nom.endsWith('.cmd')).sort();
test('install.cmd, install_dev.cmd et run.cmd existent à la racine', () => {
assert.deepEqual(commandes, ['install.cmd', 'install_dev.cmd', 'run.cmd']);
});
test('chaque fichier de commandes appelle un script PowerShell qui existe, avec ses arguments', () => {
for (const nom of commandes) {
const appel = lire(nom).match(/-File "%~dp0(scripts\\windows\\[\w.]+\.ps1)"[^\r\n]*%\*/);
assert.ok(appel, `${nom} n'appelle aucun script PowerShell avec %*`);
assert.ok(existsSync(join(RACINE, ...appel[1].split('\\'))), `${nom} → ${appel[1]} absent`);
}
});
test('les fichiers de commandes ont des fins de ligne de Windows', () => {
for (const nom of commandes) {
const texte = lire(nom);
assert.equal(texte.replaceAll('\r\n', '').includes('\n'), false, `${nom} a une fin de ligne d'Unix`);
}
});
test('les scripts PowerShell portent la marque d’ordre d’octets UTF-8 : sans elle, Windows PowerShell 5.1 défigure les accents', () => {
const scripts = readdirSync(join(RACINE, 'scripts', 'windows')).filter((nom) => nom.endsWith('.ps1'));
assert.ok(scripts.length > 0, 'aucun script PowerShell');
for (const nom of scripts) {
const octets = readFileSync(join(RACINE, 'scripts', 'windows', nom));
assert.deepEqual([...octets.subarray(0, 3)], [0xef, 0xbb, 0xbf], nom);
}
});
});
describe('installation : la majeure de Node a une seule source', () => {
test('.node-version porte une majeure entière', () => {
assert.match(lire('.node-version'), /^\d+\n$/);
});
test('bash, PowerShell et shell.nix la lisent dans .node-version au lieu de l’écrire', () => {
assert.match(lire('scripts/installation/commun.sh'), /\$GTT_RACINE\/\.node-version/);
assert.match(lire('scripts/windows/commun.ps1'), /'\.node-version'/);
assert.match(lire('scripts/installation/shell.nix'), /readFile \.\.\/\.\.\/\.node-version/);
});
test('shell.nix épingle nixpkgs par révision et par somme', () => {
const nix = lire('scripts/installation/shell.nix');
assert.match(nix, /archive\/[0-9a-f]{40}\.tar\.gz/);
assert.match(nix, /sha256 = "[0-9a-z]{52}"/);
});
});