Files
ng-eventually/packages/ng-e2e-helpers/src/serve.ts
Sylvain Duchesne 1271d48e9f refactor(e2e): la mécanique de test devient un paquet à part, ng-e2e-helpers
Créer un portefeuille, en obtenir le .ngw, traverser le broker : ce n'est pas du
ressort du polyfill. C'est un besoin commun au polyfill et à toute application
NextGraph — et surtout, ça SURVIT à la migration, alors que le polyfill est fait
pour disparaître. L'y laisser, c'était le faire mourir avec lui ou rendre le
polyfill indéracinable.

Le paquet n'importe rien du polyfill — vérifié mécaniquement — et déclare
Playwright et @ng-org/web en pairs, le consommateur devant maîtriser les
versions. Sa surface : attentes bornées, mesure, navigateur, profils,
portefeuille, traversée du broker, rapport d'exécution, et reconnaissance des
modes de panne connus.

La preuve qu'il est utilisable de l'extérieur : le polyfill le CONSOMME, sans
garder de copie. Restent chez lui les parcours, la barrière et les identités
virtuelles, qui lui sont propres.

Le verrou entre exécutions disparaît, remplacé par un profil par exécution. Il
ne traitait qu'un symptôme — un répertoire partagé que la création de
portefeuille effaçait. Avec un profil par exécution il n'y a plus rien à
sérialiser, les exécutions concurrentes deviennent indépendantes, et la
collision entre deux dépôts s'évanouit au lieu d'être exportée. Six exécutions :
aucun répertoire ni Chromium orphelin.

Et la connaissance descriptive est séparée du pilotage : URL, sélecteurs et
inventaire ordonné des écrans sont des données, passées DANS la page pour la
reconnaissance — donc un échec nomme le même écran que celui sur lequel on
dispatchait.

Un échec de navigateur est désormais nommé comme tel — « the actors browser
STOPPED ANSWERING » — au lieu de sortir sous le nom de l'opération innocente qui
se trouvait en vol.
2026-08-16 14:16:11 +02:00

42 lines
1.5 KiB
TypeScript

/**
* Serving an application (or a fixture page) to the browser under test, the way a deployment
* would.
*/
import * as http from "node:http";
import type { Socket } from "node:net";
/**
* Serve `handler` on an ephemeral port, and hand back a close that CLOSES.
*
* `server.close()` alone stops the listener and then waits for every keep-alive connection
* to drain on its own — a browser that is still attached keeps the server half-alive long
* after the harness believes it gone. These suites close a server while a browser is still
* pointed at it (the wallet export does exactly that), so the sockets are tracked and
* destroyed: "closed" has to mean closed, or the next thing to go wrong gets blamed on the
* suite instead of on the connection nobody hung up.
*/
export function serveOnEphemeralPort(
handler: (req: http.IncomingMessage, res: http.ServerResponse) => void,
): Promise<{ url: string; close: () => void }> {
const server = http.createServer(handler);
const open = new Set<Socket>();
server.on("connection", (socket) => {
open.add(socket);
socket.on("close", () => open.delete(socket));
});
return new Promise((resolve) => {
server.listen(0, "127.0.0.1", () => {
const port = (server.address() as { port: number }).port;
resolve({
url: `http://127.0.0.1:${port}`,
close: () => {
server.close();
for (const socket of open) socket.destroy();
open.clear();
},
});
});
});
}