gestion_table_tournante_libre/test/imprimante_simulee.js
Mathieu Benoit 8b7c732399 [ADD] platform: print bridge, atomic byte writes, badge sheet file name
Printed views and the badge sheet leave the app through the platform. Under
Electron a gtt:imprimer channel prints with the paper and orientation asked
for and reports sent, cancelled, failed or unknown; an exception is logged
to the console and never shown raw to the operator. Every file system,
real or simulated, writes bytes atomically from a copy taken at the call.
The sheet name carries event, date and imposition, and a failed print
becomes IMPRESSION_ECHEC.
Checked: red first, twelve mutations caught; 3502 node, 659 browser and 79
long tests green from the index alone.

--- FR ---

[ADD] plateforme : pont d'impression, octets atomiques, nom de planche

Les vues imprimables et la planche sortent de l'application par la
plateforme. Sous Electron, un canal gtt:imprimer imprime au papier et à
l'orientation demandés et rend envoyé, annulé, échec ou inconnu ; une
exception va à la console, jamais brute à l'opérateur. Chaque système de
fichiers, réel ou simulé, écrit des octets de façon atomique, depuis une
copie prise à l'appel. Le nom de la planche porte événement, date et
imposition, et une impression échouée devient IMPRESSION_ECHEC.
Vérifié : rouge d'abord, douze mutations attrapées ; 3502 node, 659
navigateur et 79 longues vertes depuis l'index seul.

Assisted-by: Claude Opus 5.5
2026-10-09 15:26:13 -04:00

52 lines
2.7 KiB
JavaScript

// © 2026 TechnoLibre (http://www.technolibre.ca)
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
// L'imprimante d'épreuve (§ 13.4) : la troisième implémentation de la
// frontière « impression », qui n'imprime rien. Elle juge le papier et
// l'orientation comme les deux imprimantes réelles, relève chaque demande
// admise et rend l'issue qu'on lui règle à sa création. Le montage des
// épreuves du navigateur la passe à App par défaut ; une épreuve y lit ce
// que l'aperçu a demandé d'imprimer.
import { PAPIERS } from '../src/geometrie/papiers.js';
const ORIENTATIONS = new Set(['portrait', 'paysage']);
// Les issues d'une impression ; seules celles d'un échec portent une cause.
const ISSUES = new Set(['envoye', 'annule', 'echec', 'inconnue']);
/**
* Une imprimante d'épreuve.
*
* @param {{issue?: 'envoye'|'annule'|'echec'|'inconnue', cause?: string|null}} [reglage]
* l'issue que rend chaque impression, envoyée par défaut, et sa
* cause, null par défaut, une chaîne pour un échec seulement
* @returns {{imprimer: (options: {papier: string, orientation: 'portrait'|'paysage'})
* => Promise<{issue: string, cause: string|null}>,
* appels: () => Array<{papier: string, orientation: string}>}}
* l'interface Imprimante de src/impression, et le relevé :
* imprimer rejette TypeError pour un papier hors de PAPIERS ou une
* orientation autre que portrait ou paysage, sans rien relever ;
* appels() rend une copie des demandes admises, dans l'ordre,
* papier et orientation seuls
* @throws {TypeError} un réglage hors du contrat
*/
export function creerImprimanteSimulee(reglage = {}) {
if (reglage === null || typeof reglage !== 'object') throw new TypeError("creerImprimanteSimulee : le réglage n'est pas un objet");
const { issue = 'envoye', cause = null } = reglage;
if (!ISSUES.has(issue)) throw new TypeError(`creerImprimanteSimulee : issue inconnue ${JSON.stringify(issue)}`);
if (!(cause === null || (issue === 'echec' && typeof cause === 'string'))) {
throw new TypeError(`creerImprimanteSimulee : cause ${JSON.stringify(cause)} pour l'issue ${issue}`);
}
const demandes = [];
return {
async imprimer({ papier, orientation } = {}) {
if (typeof papier !== 'string' || !Object.hasOwn(PAPIERS, papier)) {
throw new TypeError(`imprimer : papier inconnu ${JSON.stringify(papier)}`);
}
if (!ORIENTATIONS.has(orientation)) throw new TypeError(`imprimer : orientation inconnue ${JSON.stringify(orientation)}`);
demandes.push({ papier, orientation });
return { issue, cause };
},
appels: () => demandes.map((demande) => ({ ...demande })),
};
}