gestion_table_tournante_libre/test/chargements.test.js
Mathieu Benoit 4b442ded14 [ADD] tests: guards for layers, clock, sources, colours, watch patterns
Guards scan the tree file by file and line by line, fail on an empty scan,
and are each shown red on a temporary tree. They cover the interface import
graph, the Electron bridge, one clock, refused dependencies, how tests are
written, hard-coded strings, translation keys, forbidden words, colours and
the conflict hue, markup, pruned selectors and watch patterns. Coverage
keeps the engine integration test on a small machine. Fixes: delays in
tests, the conflict hatch class; « la rend évitable » is a named exception
(§ 5.6). Checked: 2587 node, 465 browser tests, from the index alone.

--- FR ---

[ADD] épreuves : gardes des couches, horloge, sources, couleurs, motifs

Les gardes relèvent par fichier et par ligne, échouent sur un balayage vide
et sont chacune montrées rouges sur un arbre temporaire. Elles couvrent le
graphe d'imports de l'interface, le pont d'Electron, l'horloge unique, les
dépendances refusées, l'écriture des épreuves, les chaînes en dur, les clés
de traduction, les mots interdits, les couleurs et la teinte de conflit, le
balisage, les sélecteurs élagués et les motifs de surveillance. Corrigés :
délais d'épreuves, classe de la trame de conflit ; « la rend évitable » est
une exception nommée (§ 5.6). Vérifié : 2587 node, 465 navigateur.

Assisted-by: Claude Opus 5.5
2026-10-07 13:21:21 -04:00

142 lines
5.5 KiB
JavaScript
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.

// © 2026 TechnoLibre (http://www.technolibre.ca)
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
// L'analyseur des chargements, test/chargements.js, que lisent les gardes de
// l'arborescence et du livrable. Chaque épreuve donne un texte et les
// chargements qu'il demande, dans l'ordre du texte, ou un composant Svelte et
// le texte de ses blocs de script. Les spécificateurs nus
// des données sont des paquets de devDependencies ou des modules de Node :
// la garde des dépendances lit aussi ce fichier.
import assert from 'node:assert/strict';
import { blocsScript, chargements, scriptsDe } from './chargements.js';
import { describe, test } from './lanceur.js';
// Numéro, à partir de 1, de la ligne qui porte le caractère d'indice donné.
const ligneDe = (texte, indice) => texte.slice(0, indice).split('\n').length;
// Chaque chargement d'un texte, en « ligne spécificateur », ou en « ligne
// calculé argument » pour un argument qui n'est pas une chaîne littérale.
const lus = (texte) =>
chargements(texte).map(({ indice, specificateur, argument }) =>
specificateur === undefined
? `${ligneDe(texte, indice)} calculé ${argument}`
: `${ligneDe(texte, indice)} ${specificateur}`,
);
describe('chargements', () => {
test("un import dont les accolades portent un commentaire à apostrophe se relève, comme celui qui le suit", () => {
const texte = [
'import {',
" // l'écran d'accueil",
' App,',
"} from '../interface/App.svelte';",
"import { a } from './a.js';",
].join('\n');
assert.deepEqual(
chargements(texte).map(({ specificateur }) => specificateur),
['../interface/App.svelte', './a.js'],
);
});
test('import et export à from, import seul, import() et require() de chaîne, avec ou sans options, se relèvent à leur ligne, dans l’ordre du texte', () => {
const texte = [
"import { a } from './a.js';",
'import b, { c } from "../b.js";',
"export * from './c.js';",
"export { d } from'./d.js';",
"import 'svelte';",
'const e = require("electron");',
"const f = await import('node:fs', { with: {} });",
'const g = await import(`vitest`);',
"import * as h from 'svelte/store'; const i = require('./i.cjs');",
].join('\n');
assert.deepEqual(lus(texte), [
'1 ./a.js',
'2 ../b.js',
'3 ./c.js',
'4 ./d.js',
'5 svelte',
'6 electron',
'7 node:fs',
'8 vitest',
'9 svelte/store',
'9 ./i.cjs',
]);
});
test("un argument qui n'est pas une chaîne littérale se relève comme calculé, sans spécificateur", () => {
const texte = [
'const a = require(nom);',
'const b = await import(`./${module}.js`);',
"const c = require('./' + nom);",
'const d = await import( cible );',
].join('\n');
assert.deepEqual(lus(texte), [
'1 calculé nom',
'2 calculé `./${module}.js`',
"3 calculé './' + nom",
'4 calculé cible',
]);
});
test("ni une chaîne qui finit sur « import », ni la règle @import d'une feuille de style ne sont des chargements", () => {
const texte = [
"spawnSync(process.execPath, ['--import', 'chargeur.js']);",
'<style>',
" @import 'jetons.css';",
'</style>',
"const mot = 'import';",
].join('\n');
assert.deepEqual(lus(texte), []);
});
});
describe('chargements : les blocs de script d’un composant Svelte', () => {
// Un composant à deux blocs, dont l'un sous attribut, du balisage qui écrit
// « import » en texte et en expression, un style à @import, et une balise
// de script en commentaire HTML.
const COMPOSANT = [
'<script module>',
" import { tirer } from './module.js';",
'</script>',
'',
'<script>',
" import { onMount } from 'svelte';",
" import Voisin from './Voisin.svelte';",
'</script>',
'',
"<!-- <script>import { cache } from './commentaire.js';</script> -->",
"<p>import { faux } from './texte.js';</p>",
"<p>{import('./expression.js')}</p>",
'<style>',
" @import 'jetons.css';",
'</style>',
].join('\n');
test('le texte rendu garde la longueur et les lignes du composant ; seuls les blocs de script y restent', () => {
const scripts = scriptsDe(COMPOSANT);
assert.equal(scripts.length, COMPOSANT.length);
assert.equal(scripts.split('\n').length, COMPOSANT.split('\n').length);
assert.deepEqual(lus(scripts), ['2 ./module.js', '6 svelte', '7 ./Voisin.svelte']);
assert.deepEqual(scripts.split('\n')[5], " import { onMount } from 'svelte';");
assert.match(scripts.split('\n')[10], /^ *$/);
});
test('les étendues désignent le contenu de chaque bloc, balises exclues, et un bloc non fermé court jusqu’à la fin', () => {
const blocs = blocsScript(COMPOSANT);
assert.equal(blocs.length, 2);
assert.deepEqual(
blocs.map(({ debut, fin }) => COMPOSANT.slice(debut, fin)),
["\n import { tirer } from './module.js';\n", "\n import { onMount } from 'svelte';\n import Voisin from './Voisin.svelte';\n"],
);
const ouvert = "<p></p>\n<script lang=\"js\">\nimport { a } from './a.js';";
assert.deepEqual(lus(scriptsDe(ouvert)), ['3 ./a.js']);
});
test('un composant sans bloc de script ne charge rien ; une balise voisine n’ouvre aucun bloc', () => {
for (const texte of ["<p>import { x } from './x.js';</p>", "<scripts>import { x } from './x.js';</scripts>"]) {
assert.deepEqual(blocsScript(texte), [], texte);
assert.deepEqual(lus(scriptsDe(texte)), [], texte);
}
});
});