Un terminal caché dans une grille de groupe (ou hors-écran) n'était pas repeint au retour — `visibilitychange` ne couvre que l'onglet — d'où l'écran noir avec curseur ; et son ACK ne progressait plus (callback xterm.write throttlé) → le serveur mettait le PTY en pause → sortie « par à-coups ». Ajoute un IntersectionObserver (repaint + flushAck ciblé au retour-visible) et expose Attachment.flushAck (factorisé avec flushAcks).
54 lines
2.0 KiB
TypeScript
54 lines
2.0 KiB
TypeScript
// Flush ACK ciblé (Attachment.flushAck) : relance un PTY mis en pause au retour-visible d'une cellule.
|
||
// On teste la logique d'ACK sans WebSocket réel (sendControl est public → stubbable).
|
||
import { describe, it, expect, vi } from 'vitest';
|
||
import { WsClient, Attachment, type TerminalSink } from '../src/lib/ws-client';
|
||
|
||
const sink: TerminalSink = {
|
||
write: () => {},
|
||
reset: () => {},
|
||
onDetached: () => {},
|
||
onControlChanged: () => {},
|
||
};
|
||
|
||
describe('ws-client — flush ACK ciblé (Attachment.flushAck)', () => {
|
||
it('envoie un unique ACK cumulatif du reliquat traité, puis est idempotent', () => {
|
||
const client = new WsClient();
|
||
const send = vi.spyOn(client, 'sendControl').mockImplementation(() => {});
|
||
const att = new Attachment(client, 'sid', 'interactive', sink, 80, 24);
|
||
att.channel = 2;
|
||
att.processedBytes = 1000;
|
||
|
||
att.flushAck();
|
||
expect(send).toHaveBeenCalledTimes(1);
|
||
expect(send).toHaveBeenCalledWith({ type: 'ack', channel: 2, bytes: 1000 });
|
||
expect(att.lastAckBytes).toBe(1000);
|
||
|
||
// rien de neuf traité → aucun ACK supplémentaire (n'ACK QUE du réellement traité)
|
||
att.flushAck();
|
||
expect(send).toHaveBeenCalledTimes(1);
|
||
|
||
// nouveau reliquat traité → ACK cumulatif mis à jour
|
||
att.processedBytes = 1500;
|
||
att.flushAck();
|
||
expect(send).toHaveBeenCalledTimes(2);
|
||
expect(send).toHaveBeenLastCalledWith({ type: 'ack', channel: 2, bytes: 1500 });
|
||
});
|
||
|
||
it('no-op si le canal n’est pas attaché (-1) ou si l’attachment est fermé', () => {
|
||
const client = new WsClient();
|
||
const send = vi.spyOn(client, 'sendControl').mockImplementation(() => {});
|
||
|
||
const notAttached = new Attachment(client, 'a', 'interactive', sink, 80, 24); // channel = -1
|
||
notAttached.processedBytes = 500;
|
||
notAttached.flushAck();
|
||
|
||
const closed = new Attachment(client, 'b', 'interactive', sink, 80, 24);
|
||
closed.channel = 3;
|
||
closed.processedBytes = 500;
|
||
closed.closed = true;
|
||
closed.flushAck();
|
||
|
||
expect(send).not.toHaveBeenCalled();
|
||
});
|
||
});
|