[FIX] local settings: an unreadable or unwritable file no longer blocks
An unreadable reglages_locaux.json, or a folder that refused to write it, refused the paper choice, so no badge sheet or preview could be produced on that machine. The chosen paper now holds for the session on that working folder, in memory, and the file is never overwritten. On that folder it wins over a stale paper still in the file; the first settings write that succeeds there retains it and withdraws the warning. The warning names the file and no longer promises defaults never written. Checked: node 3918, browser 773; each fix red under the previous code. --- FR --- [FIX] réglages locaux : un fichier illisible ou non écrit ne bloque plus Un reglages_locaux.json illisible, ou un dossier qui refusait de l'écrire, refusait le choix du papier : ni planche ni aperçu sur cette machine. Le papier choisi vaut désormais pour la séance sur ce dossier de travail, en mémoire, et le fichier n'est jamais écrasé. Sur ce dossier, il l'emporte sur un papier périmé du fichier ; la première écriture des réglages qui y réussit le retient et retire l'avertissement. L'avertissement nomme le fichier et ne promet plus de valeurs par défaut jamais écrites. Vérifié : node 3918, navigateur 773 ; chaque correction rouge avant elle. Assisted-by: Claude Opus 5.5
This commit is contained in:
parent
a50d3278f1
commit
73d08294f5
8 changed files with 562 additions and 44 deletions
8
spec.md
8
spec.md
|
|
@ -1444,8 +1444,12 @@ geste, jamais un minuteur » du § 8.2 : un zoom continu écrit une fois par dé
|
|||
jamais à chaque image. **Le papier s'écrit au geste qui le choisit.** Les deux
|
||||
passent par une même file, qui relit le fichier avant chaque écriture : deux
|
||||
écrivains qui le liraient chacun avant que l'autre l'ait écrit perdraient l'un
|
||||
des deux réglages. Un fichier que la lecture ne prend pas ne s'écrase pas : le
|
||||
choix du papier se refuse, et le cadrage attend.
|
||||
des deux réglages. Un fichier que la lecture ne prend pas ne s'écrase pas. Ni
|
||||
lui, ni un dossier qui refuse de l'écrire, ne bloque le choix du papier : le
|
||||
papier choisi vaut pour la séance sur ce dossier de travail, sans être retenu sur
|
||||
la machine, et un avertissement nomme le fichier et dit que le choix n'est pas
|
||||
retenu ; la première écriture des réglages qui y réussit le retient, et
|
||||
l'avertissement se retire. Le cadrage attend.
|
||||
|
||||
### 8.6 Les fichiers
|
||||
|
||||
|
|
|
|||
|
|
@ -323,6 +323,7 @@ const EXEMPLES = new Map([
|
|||
['PLATEFORME_WEB', {}],
|
||||
['SUPPORT_AMOVIBLE', {}],
|
||||
['REGLAGES_ILLISIBLES', { chemin: 'reglages_locaux.json', raison: 'FORME' }],
|
||||
['REGLAGES_NON_ECRITS', { chemin: 'reglages_locaux.json', dossier: 'E:\\data', cause: 'EROFS' }],
|
||||
// CSV
|
||||
['UTF16_SANS_MARQUE', { remede: 'ENREGISTRER_EN_UTF8' }],
|
||||
['UTF16_INVALIDE', { remede: 'ENREGISTRER_EN_UTF8' }],
|
||||
|
|
@ -700,6 +701,25 @@ describe('libellés : les textes (§ 14.6, § 8.2)', () => {
|
|||
assert.match(libelle('EVENEMENT_CHANGE', { revision: 7 }), /\b7\b/);
|
||||
});
|
||||
|
||||
test("des réglages locaux illisibles nomment le fichier, disent le papier choisi pour la séance et le cadrage non retenu, sans promettre de valeurs par défaut", () => {
|
||||
for (const raison of ['JSON', 'FORME', 'EACCES']) {
|
||||
const texte = libelle('REGLAGES_ILLISIBLES', { chemin: 'reglages_locaux.json', raison });
|
||||
assert.match(texte, /« reglages_locaux\.json »/, raison);
|
||||
assert.ok(texte.includes('le papier choisi vaut pour cette séance, sans être retenu sur cette machine'), `${raison} : « ${texte} »`);
|
||||
assert.ok(texte.includes("le cadrage n'est pas retenu"), `${raison} : le cadrage ne se promet pas pour la séance`);
|
||||
assert.doesNotMatch(texte, /défaut|cadrage choisis?/, raison);
|
||||
}
|
||||
assert.match(libelle('REGLAGES_ILLISIBLES', { chemin: 'reglages_locaux.json', raison: 'EACCES' }), /\(EACCES\)/);
|
||||
});
|
||||
|
||||
test("des réglages locaux non écrits nomment le fichier, le dossier et la cause, et disent le papier non retenu sur la machine", () => {
|
||||
const texte = libelle('REGLAGES_NON_ECRITS', { chemin: 'reglages_locaux.json', dossier: 'E:\\data', cause: 'EROFS' });
|
||||
for (const motif of [/« reglages_locaux\.json »/, /« E:\\data »/, /\(EROFS\)/]) assert.match(texte, motif);
|
||||
assert.ok(texte.includes('sans être retenu sur cette machine'), texte);
|
||||
assert.doesNotMatch(texte, /défaut|ne se lisent pas/);
|
||||
assert.doesNotMatch(libelle('REGLAGES_NON_ECRITS', { chemin: 'reglages_locaux.json', dossier: 'E:\\data', cause: null }), /\(/);
|
||||
});
|
||||
|
||||
test("quitter un événement au support rompu nomme « Écrire ailleurs », et ce qui serait perdu", () => {
|
||||
const details = { chemin: 'Soirée des Lucioles.gtt-journal.jsonl', dossier: 'E:\\data', cause: 'EROFS' };
|
||||
for (const echec of ['ECRITURE', 'ETAT_NON_ECRIT', 'JALON_NON_ECRIT', 'VERROU_NON_ECRIT']) {
|
||||
|
|
|
|||
|
|
@ -153,6 +153,14 @@ export const TABLE = new Map([
|
|||
`Le verrou de l'événement n'a pas pu être rendu${causeEntreParentheses(cause, texte)} : ` +
|
||||
'une autre séance de ce poste le reprendra une fois ce logiciel fermé.',
|
||||
],
|
||||
// Le papier choisi que le dossier de travail refuse d'écrire dans les
|
||||
// réglages locaux : il vaut pour la séance, en mémoire.
|
||||
[
|
||||
'REGLAGES_NON_ECRITS',
|
||||
(details, texte) =>
|
||||
`Les réglages locaux ${ouEcrire(details, texte)} n'ont pas pu s'écrire : ` +
|
||||
'le papier choisi vaut pour cette séance, sans être retenu sur cette machine.',
|
||||
],
|
||||
['RIEN_A_DEFAIRE', () => "Rien à défaire : l'historique commence à cet instant."],
|
||||
['RIEN_A_REFAIRE', () => 'Rien à refaire : seul un retour se refait, tant que nul geste ne le suit.'],
|
||||
['INSTANT_NON_RESTITUABLE', ({ revision }) => `L'historique ne restitue plus l'instant ${revision}.`],
|
||||
|
|
|
|||
|
|
@ -317,7 +317,11 @@ export const TABLE = new Map([
|
|||
({ chemin, raison }, texte) => {
|
||||
const pourquoi =
|
||||
raison === 'JSON' || raison === 'FORME' ? texte(raison) : `La lecture est refusée (${raison}).`;
|
||||
return `Les réglages locaux ${cite(chemin)} ne se lisent pas. ${pourquoi} Les valeurs par défaut s'appliquent.`;
|
||||
return (
|
||||
`Les réglages locaux ${cite(chemin)} ne se lisent pas. ${pourquoi} Le fichier reste tel quel : ` +
|
||||
'le papier choisi vaut pour cette séance, sans être retenu sur cette machine, ' +
|
||||
"et le cadrage n'est pas retenu."
|
||||
);
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -70,7 +70,13 @@
|
|||
// (§ 8.5) : regarder n'écrit jamais l'événement. Le papier des planches, de
|
||||
// la machine et non du plan, s'y range aussi, dans tout mode, avec ou sans
|
||||
// événement (§ 8.5, § 11.2) ; une seule file y écrit, que le cadrage et le
|
||||
// papier partagent.
|
||||
// papier partagent. Des réglages que la lecture ne prend pas ne s'écrasent
|
||||
// jamais : le cadrage attend, et le papier choisi vaut pour la séance, en
|
||||
// mémoire, sans être retenu sur la machine. Il en va de même du papier d'une
|
||||
// écriture que le dossier refuse : ni des réglages illisibles ni un dossier
|
||||
// en lecture seule ne bloquent le choix du papier, et les sorties écrivent
|
||||
// là où le dossier l'accepte — un dossier qui refuse l'écriture refuse aussi
|
||||
// le PDF des planches.
|
||||
//
|
||||
// Chaque geste lit l'horloge une fois : l'instant de son entrée au journal
|
||||
// date aussi le placement qu'il change (§ 8.9, § 11.8).
|
||||
|
|
@ -100,7 +106,15 @@ import { FORMAT, analyser } from '../stockage/document.js';
|
|||
import { ErreurStockage } from '../stockage/erreurs.js';
|
||||
import { fil, reconstruire } from '../stockage/journal.js';
|
||||
import { SUFFIXES } from '../stockage/noms.js';
|
||||
import { avecCadrage, avecPapier, cadrageDe, creerEcrivainDiffere, ecrireReglages, lireReglages } from '../stockage/reglages_locaux.js';
|
||||
import {
|
||||
FICHIER_REGLAGES,
|
||||
avecCadrage,
|
||||
avecPapier,
|
||||
cadrageDe,
|
||||
creerEcrivainDiffere,
|
||||
ecrireReglages,
|
||||
lireReglages,
|
||||
} from '../stockage/reglages_locaux.js';
|
||||
import { avertissementsApres, examenNomme, invariantViole } from './avertissements.js';
|
||||
import { appliquerCommande, chargeNeuve, formeNommeeDe } from './commandes.js';
|
||||
import { libelle } from './libelles.js';
|
||||
|
|
@ -238,9 +252,10 @@ export function creerSeance({
|
|||
// formatLu : le format de l'état relu, null dès qu'un geste l'a écrit ;
|
||||
// retourAutomatique : le dernier retour en lecture est celui de
|
||||
// l'inactivité ; cadrage : celui de l'événement ouvert, lu ou demandé ;
|
||||
// papier : celui que portent les réglages de la racine, relu à chaque
|
||||
// changement de racine et après chaque écriture des réglages, null sans
|
||||
// racine ou sans choix.
|
||||
// papier : sur la racine du choix de la séance (choixSeance), le papier de
|
||||
// ce choix ; sur une autre, celui que portent ses réglages, à défaut le
|
||||
// papier du choix ; relu à chaque changement de racine et après chaque
|
||||
// écriture des réglages, null sans racine ou sans choix.
|
||||
const etat = {
|
||||
racine,
|
||||
ouvert: null,
|
||||
|
|
@ -377,6 +392,31 @@ export function creerSeance({
|
|||
// écriture et rendue après remettrait dans l'état le papier d'avant.
|
||||
let fileReglages = Promise.resolve();
|
||||
|
||||
// Le choix de la séance, {racine, papier} : le dernier papier choisi sur
|
||||
// des réglages que la lecture ne prend pas, ou que le dossier refuse
|
||||
// d'écrire, et la racine où il l'a été ; le fichier ne le retient donc
|
||||
// pas. Sur cette racine, il l'emporte sur le papier que le fichier porte ;
|
||||
// sur une autre, il ne vaut que là où les réglages n'en portent aucun.
|
||||
// null sans tel choix, et dès qu'une écriture le retient (retenirPapier).
|
||||
let choixSeance = null;
|
||||
|
||||
// Le papier de dossier selon le choix de la séance : celui du choix sur sa
|
||||
// racine, undefined ailleurs.
|
||||
const papierDuChoix = (dossier) =>
|
||||
choixSeance !== null && memeRacine(choixSeance.racine, dossier) ? choixSeance.papier : undefined;
|
||||
|
||||
// Un papier vient de s'écrire dans des réglages : le choix de la séance
|
||||
// s'efface, et les avis REGLAGES_ILLISIBLES et REGLAGES_NON_ECRITS du
|
||||
// fichier des réglages quittent ceux du geste, que le choix retenu
|
||||
// dément.
|
||||
function retenirPapier() {
|
||||
choixSeance = null;
|
||||
etat.avertissementsGeste = etat.avertissementsGeste.filter(
|
||||
({ code, details }) =>
|
||||
!((code === 'REGLAGES_ILLISIBLES' || code === 'REGLAGES_NON_ECRITS') && details?.chemin === FICHIER_REGLAGES),
|
||||
);
|
||||
}
|
||||
|
||||
// Met travail à son tour dans la file des réglages et rend sa promesse ;
|
||||
// la file continue après un refus.
|
||||
function enFileReglages(travail) {
|
||||
|
|
@ -394,34 +434,86 @@ export function creerSeance({
|
|||
// (REGLAGES_ILLISIBLES : lecture refusée, JSON ou forme fautifs) ne
|
||||
// s'écrasent pas des défauts, qui perdraient le papier et les cadrages :
|
||||
// le refus lève, ErreurSeance de l'avertissement, et rien ne s'écrit.
|
||||
// L'écriture faite, le papier de la séance est celui qu'elle a écrit quand
|
||||
// dossier est la racine de la séance.
|
||||
// Sur la racine du choix de la séance, le papier du choix s'écrit avec ce
|
||||
// que transformer rend, et l'écriture faite le retient (retenirPapier).
|
||||
// L'écriture faite, EtatSeance.papier est celui qu'elle a écrit, à défaut
|
||||
// celui du choix de la séance, quand dossier est la racine de la séance.
|
||||
function ecrireReglagesLocaux(dossier, transformer) {
|
||||
return enFileReglages(async () => {
|
||||
const { reglages, avertissement } = await lireReglages(fs, dossier);
|
||||
if (avertissement !== null) throw new ErreurSeance(avertissement.code, avertissement.details);
|
||||
const ecrits = transformer(reglages);
|
||||
const choisi = papierDuChoix(dossier);
|
||||
const transformes = transformer(reglages);
|
||||
const ecrits = choisi === undefined ? transformes : avecPapier(transformes, choisi);
|
||||
await ecrireReglages(fs, dossier, ecrits);
|
||||
if (memeRacine(dossier, etat.racine)) {
|
||||
etat.papier = ecrits.papier;
|
||||
publier();
|
||||
}
|
||||
if (choisi !== undefined) retenirPapier();
|
||||
const surRacine = memeRacine(dossier, etat.racine);
|
||||
if (surRacine) etat.papier = ecrits.papier ?? choixSeance?.papier ?? null;
|
||||
if (surRacine || choisi !== undefined) publier();
|
||||
});
|
||||
}
|
||||
|
||||
// À son tour dans la file des réglages, relit le papier des réglages de
|
||||
// dossier — null sans dossier, sans choix, et pour des réglages que la
|
||||
// lecture ne prend pas — et le pose dans l'état quand dossier est encore
|
||||
// la racine de la séance. Ne lève jamais ; ne publie pas : l'appelant
|
||||
// est un travail de la file des appels, qui publie en finissant.
|
||||
// À son tour dans la file des réglages, écrit papier dans les réglages
|
||||
// de dossier, le retient (retenirPapier) et rend {avertissements: []}. Un
|
||||
// papier qui ne s'écrit pas devient le choix de la séance sur dossier, et
|
||||
// un avis le dit : des réglages que la lecture ne prend pas ne s'écrivent
|
||||
// pas, REGLAGES_ILLISIBLES {chemin, raison} ; une écriture que le dossier
|
||||
// refuse (ErreurStockage) laisse le fichier intact, REGLAGES_NON_ECRITS
|
||||
// {chemin, dossier, cause}. L'avis rejoint ceux du geste — une fois, quel
|
||||
// que soit le nombre de choix — et se rend, {avertissements: [avis]}. Dans
|
||||
// tous les cas, EtatSeance.papier devient papier quand dossier est encore
|
||||
// la racine de la séance : un choix parti avant un changement de racine
|
||||
// ne se publie pas sur la nouvelle. Une autre erreur, faute du code,
|
||||
// remonte sans rien poser.
|
||||
function ecrirePapier(dossier, papier) {
|
||||
return enFileReglages(async () => {
|
||||
const { reglages, avertissement: illisibles } = await lireReglages(fs, dossier);
|
||||
let avis = null;
|
||||
if (illisibles === null) {
|
||||
try {
|
||||
await ecrireReglages(fs, dossier, avecPapier(reglages, papier));
|
||||
} catch (erreur) {
|
||||
if (!(erreur instanceof ErreurStockage)) throw erreur;
|
||||
avis = avertissement('REGLAGES_NON_ECRITS', {
|
||||
chemin: FICHIER_REGLAGES,
|
||||
dossier: dossier.chemin,
|
||||
cause: erreur.details?.cause ?? null,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
avis = avertissement(illisibles.code, illisibles.details);
|
||||
}
|
||||
if (avis === null) {
|
||||
retenirPapier();
|
||||
} else {
|
||||
choixSeance = { racine: dossier, papier };
|
||||
const meme = JSON.stringify(avis);
|
||||
etat.avertissementsGeste = [...etat.avertissementsGeste.filter((autre) => JSON.stringify(autre) !== meme), avis];
|
||||
}
|
||||
if (memeRacine(dossier, etat.racine)) etat.papier = papier;
|
||||
publier();
|
||||
return { avertissements: avis === null ? [] : [avis] };
|
||||
});
|
||||
}
|
||||
|
||||
// À son tour dans la file des réglages, pose dans l'état le papier de
|
||||
// dossier, quand dossier est encore la racine de la séance : sur la racine
|
||||
// du choix de la séance, le papier du choix, sans rien lire ; ailleurs,
|
||||
// celui des réglages, à défaut celui du choix — sans papier, et pour des
|
||||
// réglages que la lecture ne prend pas ; null sans dossier. Ne lève
|
||||
// jamais ; ne publie pas : l'appelant est un travail de la file des
|
||||
// appels, qui publie en finissant.
|
||||
function relirePapier(dossier) {
|
||||
return enFileReglages(async () => {
|
||||
let papier = null;
|
||||
if (dossier !== null) {
|
||||
try {
|
||||
papier = (await lireReglages(fs, dossier)).reglages.papier;
|
||||
} catch {
|
||||
papier = null;
|
||||
papier = papierDuChoix(dossier);
|
||||
if (papier === undefined) {
|
||||
try {
|
||||
papier = (await lireReglages(fs, dossier)).reglages.papier ?? choixSeance?.papier ?? null;
|
||||
} catch {
|
||||
papier = choixSeance?.papier ?? null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (memeRacine(dossier, etat.racine)) etat.papier = papier;
|
||||
|
|
@ -1329,23 +1421,29 @@ export function creerSeance({
|
|||
* récent : l'état et le journal ne s'écrivent pas. Le domaine est celui
|
||||
* d'avecPapier ; null, qui y efface le choix, ne se choisit pas.
|
||||
*
|
||||
* Des réglages que la lecture ne prend pas ne s'écrasent pas, et une
|
||||
* écriture que le dossier refuse laisse le fichier intact ; ni l'un ni
|
||||
* l'autre ne bloque le choix : le papier vaut pour la séance, en
|
||||
* mémoire, EtatSeance.papier le porte, et l'avertissement
|
||||
* REGLAGES_ILLISIBLES {chemin, raison}, ou REGLAGES_NON_ECRITS {chemin,
|
||||
* dossier, cause}, rejoint EtatSeance.avertissements et se rend. Sur
|
||||
* cette racine, ce papier l'emporte sur celui que le fichier porte, et
|
||||
* la première écriture des réglages qui y réussit l'écrit et retire
|
||||
* l'avertissement. Une séance neuve ne le retrouve pas.
|
||||
*
|
||||
* @param {'A4'|'Lettre'} papier
|
||||
* @returns {Promise<void>}
|
||||
* @returns {Promise<{avertissements: Array<{code: string, details: Object}>}>}
|
||||
* vide quand le papier s'est écrit
|
||||
* @throws {TypeError} un papier hors du domaine, ou une séance sans
|
||||
* dossier de travail, sans rien lire ni écrire
|
||||
* @throws {ErreurSeance} REGLAGES_ILLISIBLES {chemin, raison} : les
|
||||
* réglages ne se lisent pas, ni ne s'écrasent ; le papier de la
|
||||
* séance reste celui d'avant
|
||||
* @throws {ErreurStockage} ECRITURE, le fichier intact
|
||||
*/
|
||||
async choisirPapier(papier) {
|
||||
if (papier === null) throw new TypeError('choisirPapier : un papier attendu, reçu null');
|
||||
const choisir = (reglages) => avecPapier(reglages, papier);
|
||||
// Essayé sur des réglages vides, avecPapier lève sur un papier hors du
|
||||
// domaine avant que rien ne se lise.
|
||||
choisir({ format: 1, papier: null, cadrages: [] });
|
||||
avecPapier({ format: 1, papier: null, cadrages: [] }, papier);
|
||||
if (etat.racine === null) throw new TypeError('choisirPapier : aucun dossier de travail');
|
||||
await ecrireReglagesLocaux(etat.racine, choisir);
|
||||
return ecrirePapier(etat.racine, papier);
|
||||
},
|
||||
|
||||
// quitter.abandonner : vrai pour quitter l'événement d'un support rompu
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
// version vient du module engendré.
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { creerFichiersSimules } from '../../test/fichiers_simules.js';
|
||||
import { PanneSimulee, creerFichiersSimules } from '../../test/fichiers_simules.js';
|
||||
import { H_RETENU, personne as personneBadge, soiree, table as tableBadge } from '../../test/fixtures/badges/cas.js';
|
||||
import { describe, test } from '../../test/lanceur.js';
|
||||
import { lirePolices } from '../../test/polices.js';
|
||||
|
|
@ -3045,16 +3045,287 @@ describe('séance : le papier de la machine (§ 8.5, § 11.2, Q1)', () => {
|
|||
assert.equal(seance.etat().papier, null);
|
||||
});
|
||||
|
||||
test('des réglages illisibles refusent le choix REGLAGES_ILLISIBLES, comme le cadrage : le fichier intact, le papier inchangé', async () => {
|
||||
for (const corrompu of ['pas du json\n', '{"cadrages":[],"format":1,"papier":"A5"}\n']) {
|
||||
const { fs, seance } = await ouvertSur();
|
||||
fs.deposer('documents', FICHIER_REGLAGES, corrompu);
|
||||
await refus(seance.choisirPapier('A4'), 'REGLAGES_ILLISIBLES');
|
||||
assert.equal(fs.contenu('documents', FICHIER_REGLAGES), corrompu);
|
||||
assert.equal(seance.etat().papier, null);
|
||||
// Des réglages que la lecture ne prend pas : un format plus récent qui
|
||||
// porte un papier inconnu de cette construction, et un texte tronqué.
|
||||
const ILLISIBLES = [
|
||||
['FORME', '{"format":2,"papier":"Legal","cadrages":[]}'],
|
||||
['JSON', '{"format":1,"papier":"A4","cadr'],
|
||||
];
|
||||
// Une soirée retenue de deux personnes à une table, un tour : une planche
|
||||
// se prépare dès que le papier est connu.
|
||||
const PLAN_RETENU = [{ sieges: [[1, 2]] }];
|
||||
const chargeRetenue = () =>
|
||||
soiree({
|
||||
personnes: [personneBadge(1, 'Iris', 'Ombrelle'), personneBadge(2, 'Théo', 'Pervenche')],
|
||||
tables: [tableBadge(1, 1, 2)],
|
||||
plan: PLAN_RETENU,
|
||||
propositions: [[1, PLAN_RETENU]],
|
||||
});
|
||||
const avecPlanche = { polices: async () => lirePolices(), provenance: 'production' };
|
||||
|
||||
test('des réglages illisibles : le papier choisi vaut pour la séance, en mémoire, le fichier intact octet pour octet, et la planche se prépare sans PAPIER_REQUIS', async () => {
|
||||
for (const [raison, texte] of ILLISIBLES) {
|
||||
const fs = creerFichiersSimules();
|
||||
fs.deposerDossier('documents');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, texte);
|
||||
const { seance } = await ouvertSur(chargeRetenue(), { fs, ...avecPlanche });
|
||||
const avant = fs.compteurs.ecritures;
|
||||
const textes = evenementSurDisque(fs);
|
||||
const avis = { code: 'REGLAGES_ILLISIBLES', details: { chemin: FICHIER_REGLAGES, raison } };
|
||||
assert.deepEqual(await seance.choisirPapier('A4'), { avertissements: [avis] }, raison);
|
||||
assert.equal(seance.etat().papier, 'A4', raison);
|
||||
assert.deepEqual(seance.etat().avertissements.filter(({ code }) => code === avis.code), [avis], raison);
|
||||
const annonce = await seance.preparerPlanche();
|
||||
assert.deepEqual([annonce.refus, annonce.papier], [null, 'A4'], raison);
|
||||
await seance.choisirPapier('Lettre');
|
||||
assert.equal(seance.etat().papier, 'Lettre', raison);
|
||||
assert.equal((await seance.preparerPlanche()).papier, 'Lettre', raison);
|
||||
assert.deepEqual(seance.etat().avertissements.filter(({ code }) => code === avis.code), [avis], `${raison} : un avis, pas deux`);
|
||||
assert.deepEqual(fs.octets('documents', FICHIER_REGLAGES), new TextEncoder().encode(texte), raison);
|
||||
assert.equal(fs.compteurs.ecritures, avant, raison);
|
||||
assert.deepEqual(evenementSurDisque(fs), textes, raison);
|
||||
}
|
||||
});
|
||||
|
||||
test('des réglages illisibles : la planche se produit sur le papier de la séance, et seul le PDF s’écrit', async () => {
|
||||
const [, texte] = ILLISIBLES[1];
|
||||
const fs = creerFichiersSimules();
|
||||
fs.deposerDossier('documents');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, texte);
|
||||
const { seance } = await ouvertSur(chargeRetenue(), { fs, ...avecPlanche });
|
||||
await refus(seance.produirePlanche(), 'PAPIER_REQUIS');
|
||||
await seance.choisirPapier('A4');
|
||||
const textes = evenementSurDisque(fs);
|
||||
const { nom } = await seance.produirePlanche();
|
||||
assert.match(nom, /_A4_/);
|
||||
assert.ok(fs.octets('documents', nom).length > 0);
|
||||
assert.equal(fs.contenu('documents', FICHIER_REGLAGES), texte);
|
||||
assert.deepEqual(evenementSurDisque(fs), textes);
|
||||
});
|
||||
|
||||
test("le papier de la séance n'est pas retenu sur la machine : une séance neuve sur la même racine le demande, et un changement de racine le garde", async () => {
|
||||
const [, texte] = ILLISIBLES[0];
|
||||
const fs = creerFichiersSimules({ executable: 'E:\\soirees' });
|
||||
fs.deposerDossier('documents');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, texte);
|
||||
const { seance } = await ouvertSur(chargeEssai(), { fs });
|
||||
await seance.choisirPapier('A4');
|
||||
const neuve = monter({ fs, prefixe: 'neuve' });
|
||||
await neuve.seance.attendre();
|
||||
assert.equal(neuve.seance.etat().papier, null);
|
||||
await seance.changerRacine(DOC);
|
||||
assert.equal(seance.etat().papier, 'A4', 'relu sur la même racine illisible');
|
||||
fs.deposerDossier(PORTABLE.id);
|
||||
await seance.changerRacine(PORTABLE);
|
||||
assert.equal(seance.etat().papier, 'A4', 'une racine sans réglages : le choix de la séance');
|
||||
fs.deposer(PORTABLE.id, FICHIER_REGLAGES, '{"cadrages":[],"format":1,"papier":"Lettre"}\n');
|
||||
await seance.changerRacine(DOC);
|
||||
await seance.changerRacine(PORTABLE);
|
||||
assert.equal(seance.etat().papier, 'Lettre', 'un papier écrit sur la racine l’emporte');
|
||||
assert.equal(fs.contenu('documents', FICHIER_REGLAGES), texte);
|
||||
});
|
||||
|
||||
test('des réglages illisibles : le cadrage différé attend, rien ne s’écrit, et le choix du papier ne le change pas', async () => {
|
||||
const [, texte] = ILLISIBLES[1];
|
||||
const fs = creerFichiersSimules();
|
||||
fs.deposerDossier('documents');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, texte);
|
||||
const { seance, planificateur } = await ouvertSur(chargeEssai(), { fs });
|
||||
seance.demanderCadrage({ k: 1.5, tx: 3, ty: 4 });
|
||||
await planificateur.avancer(DELAI_REGLAGES_MS);
|
||||
await seance.choisirPapier('Lettre');
|
||||
await laisser();
|
||||
assert.equal(fs.contenu('documents', FICHIER_REGLAGES), texte);
|
||||
assert.equal(seance.etat().papier, 'Lettre');
|
||||
});
|
||||
|
||||
test('un cadrage écrit après des réglages illisibles réparés écrit avec lui le papier de la séance, qui devient retenu, et l’avis se retire', async () => {
|
||||
const [, texte] = ILLISIBLES[1];
|
||||
const fs = creerFichiersSimules();
|
||||
fs.deposerDossier('documents');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, texte);
|
||||
const { seance, planificateur } = await ouvertSur(chargeEssai(), { fs });
|
||||
await seance.choisirPapier('A4');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, '{"cadrages":[],"format":1,"papier":"Lettre"}\n');
|
||||
seance.demanderCadrage({ k: 2, tx: 0, ty: 0 });
|
||||
await planificateur.avancer(DELAI_REGLAGES_MS);
|
||||
await laisser();
|
||||
const relus = JSON.parse(fs.contenu('documents', FICHIER_REGLAGES));
|
||||
assert.deepEqual([relus.papier, relus.cadrages.length], ['A4', 1]);
|
||||
assert.equal(seance.etat().papier, 'A4');
|
||||
assert.equal(seance.etat().avertissements.some(({ code }) => code === 'REGLAGES_ILLISIBLES'), false);
|
||||
const neuve = monter({ fs, prefixe: 'neuve' });
|
||||
await neuve.seance.attendre();
|
||||
assert.equal(neuve.seance.etat().papier, 'A4', 'retenu sur la machine');
|
||||
});
|
||||
|
||||
test('un papier écrit après des réglages illisibles réparés : le fichier le porte, la séance suit le fichier, et le papier de la séance s’efface', async () => {
|
||||
const [, texte] = ILLISIBLES[1];
|
||||
const fs = creerFichiersSimules({ executable: 'E:\\soirees' });
|
||||
fs.deposerDossier('documents');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, texte);
|
||||
const { seance } = await ouvertSur(chargeEssai(), { fs });
|
||||
await seance.choisirPapier('A4');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, '{"cadrages":[],"format":1,"papier":null}\n');
|
||||
assert.deepEqual(await seance.choisirPapier('Lettre'), { avertissements: [] });
|
||||
assert.deepEqual([papierLu(fs), seance.etat().papier], ['Lettre', 'Lettre']);
|
||||
assert.equal(seance.etat().avertissements.some(({ code }) => code === 'REGLAGES_ILLISIBLES'), false, 'l’avis se retire');
|
||||
fs.deposerDossier(PORTABLE.id);
|
||||
await seance.changerRacine(PORTABLE);
|
||||
assert.equal(seance.etat().papier, null, 'le choix retenu reste à sa racine ; une racine sans réglages le demande');
|
||||
});
|
||||
|
||||
// Un dossier de travail qui refuse toute écriture (EROFS) après
|
||||
// l'ouverture : les réglages s'y lisent, ils ne s'y écrivent pas.
|
||||
const avisNonEcrits = { code: 'REGLAGES_NON_ECRITS', details: { chemin: FICHIER_REGLAGES, dossier: DOCUMENTS, cause: 'EROFS' } };
|
||||
|
||||
test("un dossier qui refuse l'écriture des réglages : le papier choisi vaut pour la séance, un avis le dit, et la planche se prépare sans PAPIER_REQUIS", async () => {
|
||||
const fs = creerFichiersSimules();
|
||||
fs.deposerDossier('documents');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, '{"cadrages":[],"format":1,"papier":null}\n');
|
||||
const { seance } = await ouvertSur(chargeRetenue(), { fs, ...avecPlanche });
|
||||
fs.pannes.ecriture('documents');
|
||||
const textes = evenementSurDisque(fs);
|
||||
const reglages = fs.contenu('documents', FICHIER_REGLAGES);
|
||||
assert.deepEqual(await seance.choisirPapier('A4'), { avertissements: [avisNonEcrits] });
|
||||
assert.equal(seance.etat().papier, 'A4');
|
||||
const annonce = await seance.preparerPlanche();
|
||||
assert.deepEqual([annonce.refus, annonce.papier], [null, 'A4']);
|
||||
assert.deepEqual(await seance.choisirPapier('Lettre'), { avertissements: [avisNonEcrits] });
|
||||
assert.equal((await seance.preparerPlanche()).papier, 'Lettre');
|
||||
assert.deepEqual(
|
||||
seance.etat().avertissements.filter(({ code }) => code === avisNonEcrits.code),
|
||||
[avisNonEcrits],
|
||||
'un avis, pas deux',
|
||||
);
|
||||
assert.equal(seance.etat().avertissements.some(({ code }) => code === 'REGLAGES_ILLISIBLES'), false);
|
||||
assert.equal(fs.contenu('documents', FICHIER_REGLAGES), reglages);
|
||||
assert.deepEqual(evenementSurDisque(fs), textes);
|
||||
});
|
||||
|
||||
test("un dossier qui refuse l'écriture des réglages : la production ne demande plus le papier, seule l'écriture du PDF est refusée", async () => {
|
||||
const fs = creerFichiersSimules();
|
||||
fs.deposerDossier('documents');
|
||||
const { seance } = await ouvertSur(chargeRetenue(), { fs, ...avecPlanche });
|
||||
fs.pannes.ecriture('documents');
|
||||
await refus(seance.produirePlanche(), 'PAPIER_REQUIS');
|
||||
const textes = evenementSurDisque(fs);
|
||||
await seance.choisirPapier('A4');
|
||||
const erreur = await refus(seance.produirePlanche(), 'PLANCHE_NON_ECRITE');
|
||||
assert.match(erreur.details.chemin, /_A4_/);
|
||||
assert.equal(fs.contenu('documents', FICHIER_REGLAGES), null);
|
||||
assert.deepEqual(evenementSurDisque(fs), textes);
|
||||
});
|
||||
|
||||
test("le papier d'une écriture refusée vaut au changement de racine, là où les réglages n'en portent pas", async () => {
|
||||
const fs = creerFichiersSimules({ executable: 'E:\\soirees' });
|
||||
const { seance } = await ouvertSur(chargeEssai(), { fs });
|
||||
fs.pannes.ecriture('documents');
|
||||
await seance.choisirPapier('Lettre');
|
||||
fs.deposerDossier(PORTABLE.id);
|
||||
await seance.changerRacine(PORTABLE);
|
||||
assert.equal(seance.etat().papier, 'Lettre');
|
||||
});
|
||||
|
||||
// Des réglages lisibles qui portent déjà un papier, sur la racine des
|
||||
// documents.
|
||||
const REGLAGES_A4 = '{"cadrages":[],"format":1,"papier":"A4"}\n';
|
||||
const sansReglages = (seance) =>
|
||||
seance.etat().avertissements.filter(({ code }) => code === 'REGLAGES_NON_ECRITS' || code === 'REGLAGES_ILLISIBLES');
|
||||
|
||||
test("une écriture refusée une fois : le papier de la séance l'emporte sur celui du fichier, le cadrage écrit ensuite l'écrit avec lui, et l'avis se retire", async () => {
|
||||
const fs = creerFichiersSimules();
|
||||
fs.deposerDossier('documents');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, REGLAGES_A4);
|
||||
const { seance, planificateur } = await ouvertSur(chargeEssai(), { fs });
|
||||
fs.pannes.renommage('documents', 11);
|
||||
const avis = { code: 'REGLAGES_NON_ECRITS', details: { chemin: FICHIER_REGLAGES, dossier: DOCUMENTS, cause: 'EBUSY' } };
|
||||
assert.deepEqual(await seance.choisirPapier('Lettre'), { avertissements: [avis] });
|
||||
assert.deepEqual([papierLu(fs), seance.etat().papier], ['A4', 'Lettre']);
|
||||
seance.demanderCadrage({ k: 1.5, tx: 3, ty: 4 });
|
||||
await planificateur.avancer(DELAI_REGLAGES_MS);
|
||||
await laisser();
|
||||
const relus = JSON.parse(fs.contenu('documents', FICHIER_REGLAGES));
|
||||
assert.deepEqual([relus.papier, relus.cadrages.length], ['Lettre', 1]);
|
||||
assert.equal(seance.etat().papier, 'Lettre');
|
||||
assert.deepEqual(sansReglages(seance), [], 'le choix est retenu : l’avis se retire');
|
||||
await seance.changerRacine(DOC);
|
||||
assert.equal(seance.etat().papier, 'Lettre');
|
||||
});
|
||||
|
||||
test("une écriture refusée une fois, puis un papier qui s'écrit : l'avis se retire de l'état", async () => {
|
||||
const fs = creerFichiersSimules();
|
||||
fs.deposerDossier('documents');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, REGLAGES_A4);
|
||||
const { seance } = await ouvertSur(chargeEssai(), { fs });
|
||||
fs.pannes.renommage('documents', 11);
|
||||
await seance.choisirPapier('Lettre');
|
||||
assert.deepEqual(sansReglages(seance).map(({ code }) => code), ['REGLAGES_NON_ECRITS']);
|
||||
assert.deepEqual(await seance.choisirPapier('Lettre'), { avertissements: [] });
|
||||
assert.deepEqual([papierLu(fs), seance.etat().papier], ['Lettre', 'Lettre']);
|
||||
assert.deepEqual(sansReglages(seance), []);
|
||||
});
|
||||
|
||||
test("un dossier qui refuse l'écriture : revenir sur la même racine garde le papier de la séance, que le fichier en porte un autre ou non ; une autre racine lit le sien", async () => {
|
||||
const fs = creerFichiersSimules({ executable: 'E:\\soirees' });
|
||||
fs.deposerDossier('documents');
|
||||
fs.deposer('documents', FICHIER_REGLAGES, REGLAGES_A4);
|
||||
const { seance, planificateur } = await ouvertSur(chargeEssai(), { fs });
|
||||
fs.pannes.ecriture('documents');
|
||||
await seance.choisirPapier('Lettre');
|
||||
await seance.changerRacine(DOC);
|
||||
assert.equal(seance.etat().papier, 'Lettre', 'la même racine');
|
||||
fs.deposerDossier(PORTABLE.id);
|
||||
fs.deposer(PORTABLE.id, FICHIER_REGLAGES, REGLAGES_A4);
|
||||
await seance.changerRacine(PORTABLE);
|
||||
assert.equal(seance.etat().papier, 'A4', 'une autre racine : son papier l’emporte');
|
||||
await seance.changerRacine(DOC);
|
||||
assert.equal(seance.etat().papier, 'Lettre', 'de retour sur la racine du choix');
|
||||
await seance.ouvrir(BASE);
|
||||
seance.demanderCadrage({ k: 2, tx: 0, ty: 0 });
|
||||
await planificateur.avancer(DELAI_REGLAGES_MS);
|
||||
await laisser();
|
||||
assert.equal(seance.etat().papier, 'Lettre', 'un cadrage refusé ne le change pas');
|
||||
assert.equal(fs.contenu('documents', FICHIER_REGLAGES), REGLAGES_A4);
|
||||
});
|
||||
|
||||
test('une écriture des réglages qui lève autre chose qu’un refus du stockage remonte de choisirPapier, sans poser de papier ni d’avis', async () => {
|
||||
const { fs, seance } = await ouvertSur();
|
||||
// Les réglages se lisent ; leur écriture lève une panne du processus.
|
||||
const ecrireAtomique = fs.ecrireAtomique;
|
||||
fs.ecrireAtomique = async (racine, chemin, texte) => {
|
||||
if (chemin === FICHIER_REGLAGES) throw new PanneSimulee();
|
||||
return ecrireAtomique(racine, chemin, texte);
|
||||
};
|
||||
await assert.rejects(seance.choisirPapier('A4'), PanneSimulee);
|
||||
assert.equal(seance.etat().papier, null);
|
||||
assert.deepEqual(sansReglages(seance), []);
|
||||
});
|
||||
|
||||
test("un papier écrit pour l'ancienne racine, dans la file pendant un changement de racine, ne se publie jamais sur la nouvelle", async () => {
|
||||
const { fs, seance } = monter({ fs: creerFichiersSimules({ executable: 'E:\\soirees' }) });
|
||||
await seance.attendre();
|
||||
// Un fichier présent : sa lecture se retient, celle d'un absent non.
|
||||
fs.deposer('documents', FICHIER_REGLAGES, '{"cadrages":[],"format":1,"papier":null}\n');
|
||||
fs.deposerDossier(PORTABLE.id);
|
||||
const publies = [];
|
||||
seance.subscribe(({ racine, papier }) => publies.push([racine?.id ?? null, papier]));
|
||||
const lecture = retenirLectureReglages(fs);
|
||||
const choix = seance.choisirPapier('Lettre');
|
||||
await laisser();
|
||||
assert.equal(lecture.prise(), true, 'la lecture du choix est partie');
|
||||
const changement = seance.changerRacine(PORTABLE);
|
||||
await laisser();
|
||||
assert.equal(seance.racine.id, PORTABLE.id, 'la racine a changé, le choix attend');
|
||||
lecture.liberer();
|
||||
await choix;
|
||||
await changement;
|
||||
await seance.attendre();
|
||||
assert.equal(papierLu(fs), 'Lettre', 'écrit à sa racine');
|
||||
assert.deepEqual(publies.filter(([racine, papier]) => racine === PORTABLE.id && papier !== null), []);
|
||||
assert.equal(seance.etat().papier, null);
|
||||
});
|
||||
|
||||
test("un cadrage différé et le choix du papier écrits ensemble : les deux restent, l'un après l'autre", async () => {
|
||||
const { fs, seance, planificateur } = await ouvertSur();
|
||||
seance.demanderCadrage({ k: 1.5, tx: 3, ty: 4 });
|
||||
|
|
|
|||
110
src/interface/impression/ReglagesIllisibles.navigateur.test.js
Normal file
110
src/interface/impression/ReglagesIllisibles.navigateur.test.js
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// L'aperçu avant impression sur des réglages locaux que la lecture ne prend
|
||||
// pas, ou que le dossier de travail refuse d'écrire (§ 8.5, § 11.2, § 11.7) :
|
||||
// la question du papier se pose, le choix vaut pour la séance sans que
|
||||
// reglages_locaux.json le retienne, et l'aperçu rend son document, sur le
|
||||
// papier choisi, et sa commande « Imprimer » au lieu de reposer la question.
|
||||
// Rien ne s'écrit : ni le fichier illisible, ni l'événement ; sous un dossier
|
||||
// qui refuse l'écriture, la seule tentative est celle des réglages.
|
||||
//
|
||||
// App est montée par test/navigateur/ecrans.js sur le système de fichiers
|
||||
// simulé, au cadre de 1 366 × 700.
|
||||
//
|
||||
// Le projet navigateur emploie expect, et test et describe de Vitest
|
||||
// (§ 14.8).
|
||||
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
||||
import { PAPIERS } from '../../geometrie/papiers.js';
|
||||
import { FICHIER_REGLAGES } from '../../stockage/reglages_locaux.js';
|
||||
import { fixerCadre } from '../../../test/navigateur/entrees.js';
|
||||
import { attendre, chargePrincipale, cliquer, creerContexte, ouvrirApercu } from '../../../test/navigateur/ecrans.js';
|
||||
import { creerImprimanteSimulee } from '../../../test/imprimante_simulee.js';
|
||||
import { MM_PAR_PX } from './modele.js';
|
||||
|
||||
// Images attendues au plus par étape, comme dans Apercu.navigateur.test.js.
|
||||
const IMAGES = 300;
|
||||
|
||||
// Une feuille se mesure au dixième de millimètre (§ 11.7).
|
||||
const TOLERANCE_MM = 0.1;
|
||||
|
||||
let contexte = null;
|
||||
beforeEach(async () => {
|
||||
await fixerCadre(1366, 700);
|
||||
contexte = creerContexte();
|
||||
});
|
||||
afterEach(async () => {
|
||||
const erreurs = contexte.erreurs();
|
||||
await contexte.defaire();
|
||||
expect(erreurs, 'erreurs reçues par le collecteur').toEqual([]);
|
||||
});
|
||||
|
||||
const dans = (m, selecteur) => m.racine.querySelector(selecteur);
|
||||
const feuilles = (m) => [...m.racine.querySelectorAll('[data-impression] [data-feuille]')];
|
||||
|
||||
// Des réglages que la lecture ne prend pas : un format plus récent qui porte
|
||||
// un papier inconnu de cette construction, et un texte tronqué.
|
||||
const ILLISIBLES = [
|
||||
['FORME', '{"format":2,"papier":"Legal","cadrages":[]}'],
|
||||
['JSON', '{"format":1,"papier":"A4","cadr'],
|
||||
];
|
||||
|
||||
describe('des réglages locaux illisibles ne bloquent pas l’aperçu (§ 8.5, § 11.7)', () => {
|
||||
for (const [raison, texte] of ILLISIBLES) {
|
||||
test(`${raison} : le papier choisi, l’aperçu montre le document et « Imprimer » au lieu de la question ; le fichier intact, rien ne s’écrit`, async () => {
|
||||
const imprimante = creerImprimanteSimulee();
|
||||
const m = await ouvrirApercu(contexte, chargePrincipale(), {
|
||||
papier: null,
|
||||
options: { imprimante, fichiers: [{ racine: 'documents', chemin: FICHIER_REGLAGES, texte }] },
|
||||
});
|
||||
const octets = m.fs.octets('documents', FICHIER_REGLAGES);
|
||||
expect(dans(m, '[data-ecran="apercu"] [data-papier="question"]')).not.toBeNull();
|
||||
expect(dans(m, '[data-impression]')).toBeNull();
|
||||
const avant = m.trace.length;
|
||||
|
||||
await cliquer(m, '[data-papier="question"] [data-cible="A4"] [data-commande="choisirPapier"]');
|
||||
await attendre('le document sur A4', () => feuilles(m).length > 0, IMAGES);
|
||||
expect(m.seance.etat().papier).toBe('A4');
|
||||
const boite = feuilles(m)[0].getBoundingClientRect();
|
||||
const dims = [boite.width * MM_PAR_PX, boite.height * MM_PAR_PX].sort((a, b) => a - b);
|
||||
expect(Math.abs(dims[0] - PAPIERS.A4.largeur)).toBeLessThanOrEqual(TOLERANCE_MM);
|
||||
expect(Math.abs(dims[1] - PAPIERS.A4.hauteur)).toBeLessThanOrEqual(TOLERANCE_MM);
|
||||
expect(dans(m, '[data-papier="question"]')).toBeNull();
|
||||
expect(dans(m, '[data-ecran="apercu"] [data-refus]')).toBeNull();
|
||||
const avis = m.seance.etat().avertissements.filter(({ code }) => code === 'REGLAGES_ILLISIBLES');
|
||||
expect(avis).toEqual([{ code: 'REGLAGES_ILLISIBLES', details: { chemin: FICHIER_REGLAGES, raison } }]);
|
||||
|
||||
await cliquer(m, '[data-ecran="apercu"] [data-commande="imprimer"]');
|
||||
await attendre("l'impression demandée", () => imprimante.appels().length === 1, IMAGES);
|
||||
expect(imprimante.appels()[0].papier).toBe('A4');
|
||||
expect(m.trace.slice(avant)).toEqual([]);
|
||||
expect(m.fs.octets('documents', FICHIER_REGLAGES)).toEqual(octets);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('un dossier qui refuse l’écriture des réglages ne bloque pas l’aperçu (§ 8.5, § 11.7)', () => {
|
||||
test('EROFS : le papier choisi, l’aperçu montre le document et « Imprimer » ; un avis le dit, et rien ne s’écrit', async () => {
|
||||
const imprimante = creerImprimanteSimulee();
|
||||
const m = await ouvrirApercu(contexte, chargePrincipale(), { papier: null, options: { imprimante } });
|
||||
expect(dans(m, '[data-ecran="apercu"] [data-papier="question"]')).not.toBeNull();
|
||||
m.fs.pannes.ecriture('documents');
|
||||
const avant = m.trace.length;
|
||||
|
||||
await cliquer(m, '[data-papier="question"] [data-cible="A4"] [data-commande="choisirPapier"]');
|
||||
await attendre('le document sur A4', () => feuilles(m).length > 0, IMAGES);
|
||||
expect(m.seance.etat().papier).toBe('A4');
|
||||
expect(dans(m, '[data-papier="question"]')).toBeNull();
|
||||
expect(dans(m, '[data-ecran="apercu"] [data-refus]')).toBeNull();
|
||||
const avis = m.seance.etat().avertissements.filter(({ code }) => code === 'REGLAGES_NON_ECRITS');
|
||||
expect(avis).toEqual([
|
||||
{ code: 'REGLAGES_NON_ECRITS', details: { chemin: FICHIER_REGLAGES, dossier: expect.any(String), cause: 'EROFS' } },
|
||||
]);
|
||||
|
||||
await cliquer(m, '[data-ecran="apercu"] [data-commande="imprimer"]');
|
||||
await attendre("l'impression demandée", () => imprimante.appels().length === 1, IMAGES);
|
||||
expect(imprimante.appels()[0].papier).toBe('A4');
|
||||
expect(m.trace.slice(avant)).toEqual([['ecrireAtomique', 'documents', FICHIER_REGLAGES]]);
|
||||
expect(m.fs.contenu('documents', FICHIER_REGLAGES)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
@ -11,10 +11,13 @@
|
|||
// rouvre la question. Aucun papier n'est choisi pour l'opérateur.
|
||||
//
|
||||
// Un choix passe par seance.choisirPapier, vivant dans tout mode, sur un
|
||||
// plan bloqué comme en lecture. Un refus — des réglages illisibles, une
|
||||
// écriture refusée — s'affiche sur le choix cliqué, par la commande, et
|
||||
// la question reste. Le choix écrit, onChoisi, quand il est donné, reçoit
|
||||
// le papier : la section qui demandait une production la poursuit.
|
||||
// plan bloqué comme en lecture. Des réglages illisibles ou une écriture
|
||||
// refusée ne refusent pas le choix : il vaut pour la séance, et
|
||||
// l'avertissement que rend la séance va à EtatSeance.avertissements, que
|
||||
// ce composant n'affiche pas. Un refus s'affiche sur le choix cliqué, par
|
||||
// la commande, et la question reste. Le choix fait, onChoisi, quand il est
|
||||
// donné, reçoit le papier : la section qui demandait une production la
|
||||
// poursuit.
|
||||
import { getContext } from 'svelte';
|
||||
import Commande from '../primitives/Commande.svelte';
|
||||
import { PAPIERS_OFFERTS, clesPapier } from '../impression/modele.js';
|
||||
|
|
|
|||
Loading…
Reference in a new issue