Files
NetMesh/domain/terminalOscNotifications.test.ts
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

217 lines
7.4 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import {
DEFAULT_OSC_NOTIFICATION_TITLE,
Osc99Assembler,
OscNotificationLimiter,
OscNotificationStreamScanner,
parseOsc777Payload,
parseOsc9Payload,
resolveOscNotificationPresentation,
sanitizeOscNotificationText,
shouldShowOscDesktopNotification,
} from "./terminalOscNotifications.ts";
test("parseOsc9Payload treats iTerm-style bodies as notifications", () => {
assert.deepEqual(parseOsc9Payload("Codex turn complete"), {
title: "",
body: "Codex turn complete",
protocol: "osc9",
});
assert.deepEqual(parseOsc9Payload(" approval requested "), {
title: "",
body: "approval requested",
protocol: "osc9",
});
});
test("parseOsc9Payload ignores ConEmu OSC 9;n sequences", () => {
assert.equal(parseOsc9Payload("4"), null);
assert.equal(parseOsc9Payload("4;1;50"), null);
assert.equal(parseOsc9Payload("3;/tmp"), null);
assert.equal(parseOsc9Payload(""), null);
assert.equal(parseOsc9Payload(" "), null);
});
test("parseOsc9Payload keeps bodies that merely start with digits", () => {
assert.deepEqual(parseOsc9Payload("4 tests failed"), {
title: "",
body: "4 tests failed",
protocol: "osc9",
});
});
test("parseOsc777Payload handles notify title and body", () => {
assert.deepEqual(parseOsc777Payload("notify;Nightly Tests;All suites passed"), {
title: "Nightly Tests",
body: "All suites passed",
protocol: "osc777",
});
assert.deepEqual(parseOsc777Payload("notify;Deploy;Production;complete"), {
title: "Deploy",
body: "Production;complete",
protocol: "osc777",
});
assert.deepEqual(parseOsc777Payload("notify;Only body"), {
title: "",
body: "Only body",
protocol: "osc777",
});
assert.equal(parseOsc777Payload("sleep;1"), null);
assert.equal(parseOsc777Payload("notify;"), null);
});
test("Osc99Assembler emits simple kitty notifications", () => {
const assembler = new Osc99Assembler();
assert.deepEqual(assembler.consume(";Hello world"), {
title: "Hello world",
body: "",
protocol: "osc99",
});
});
test("Osc99Assembler assembles chunked title and body", () => {
const assembler = new Osc99Assembler();
assert.equal(assembler.consume("i=1:d=0;Hello world"), null);
assert.deepEqual(assembler.consume("i=1:p=body;This is cool"), {
title: "Hello world",
body: "This is cool",
protocol: "osc99",
});
});
test("Osc99Assembler preserves whitespace across chunk boundaries", () => {
const assembler = new Osc99Assembler();
assert.equal(assembler.consume("i=1:d=0;Hello "), null);
assert.deepEqual(assembler.consume("i=1;world"), {
title: "Hello world",
body: "",
protocol: "osc99",
});
});
test("Osc99Assembler decodes base64 payloads", () => {
const assembler = new Osc99Assembler();
assert.deepEqual(assembler.consume("p=body:e=1;SGVsbG8="), {
title: "",
body: "Hello",
protocol: "osc99",
});
});
test("Osc99Assembler ignores close metadata", () => {
const assembler = new Osc99Assembler();
assert.equal(assembler.consume("i=1:d=0;pending"), null);
assert.equal(assembler.consume("i=1:p=close;"), null);
assert.deepEqual(assembler.consume("i=1:p=body;too late"), {
title: "",
body: "too late",
protocol: "osc99",
});
});
test("resolveOscNotificationPresentation prefers explicit title and body", () => {
assert.deepEqual(
resolveOscNotificationPresentation({
title: "Codex",
body: "Turn complete",
protocol: "osc9",
}, "prod-box"),
{ title: "Codex", body: "Turn complete" },
);
assert.deepEqual(
resolveOscNotificationPresentation({
title: "",
body: "Turn complete",
protocol: "osc9",
}, "prod-box"),
{ title: "prod-box", body: "Turn complete" },
);
assert.deepEqual(
resolveOscNotificationPresentation({
title: "Hello world",
body: "",
protocol: "osc99",
}, "prod-box"),
{ title: "prod-box", body: "Hello world" },
);
assert.equal(
resolveOscNotificationPresentation({
title: "",
body: "",
protocol: "osc9",
}).title,
DEFAULT_OSC_NOTIFICATION_TITLE,
);
});
test("sanitizeOscNotificationText strips controls and truncates", () => {
assert.equal(sanitizeOscNotificationText("hello\u0007world", 20), "helloworld");
assert.equal(sanitizeOscNotificationText(" a b ", 20), "a b");
assert.equal(sanitizeOscNotificationText("abcdefghij", 6), "abcde…");
});
test("shouldShowOscDesktopNotification honors mode and focus", () => {
assert.equal(shouldShowOscDesktopNotification("off", { windowFocused: false, sessionFocused: false }), false);
assert.equal(shouldShowOscDesktopNotification("always", { windowFocused: true, sessionFocused: true }), true);
assert.equal(shouldShowOscDesktopNotification("unfocused", { windowFocused: true, sessionFocused: true }), false);
assert.equal(shouldShowOscDesktopNotification("unfocused", { windowFocused: true, sessionFocused: false }), true);
assert.equal(shouldShowOscDesktopNotification("unfocused", { windowFocused: false, sessionFocused: true }), true);
assert.equal(shouldShowOscDesktopNotification(undefined, { windowFocused: true, sessionFocused: true }), true);
});
test("OscNotificationStreamScanner extracts split OSC 9 sequences and leaves other text", () => {
const scanner = new OscNotificationStreamScanner();
const first = scanner.consume("hello\x1b]9;Codex turn");
assert.deepEqual(first.notifications, []);
assert.equal(first.remainder, "hello");
const second = scanner.consume(" complete\x07world");
assert.deepEqual(second.notifications, [{
title: "",
body: "Codex turn complete",
protocol: "osc9",
}]);
assert.equal(second.remainder, "world");
});
test("OscNotificationStreamScanner keeps non-notification OSC in the remainder", () => {
const scanner = new OscNotificationStreamScanner();
const result = scanner.consume("pre\x1b]7;file://host/tmp\x07post");
assert.deepEqual(result.notifications, []);
assert.equal(result.remainder, "pre\x1b]7;file://host/tmp\x07post");
});
test("OscNotificationStreamScanner flushes an unfinished prefix", () => {
const scanner = new OscNotificationStreamScanner();
scanner.consume("\x1b]9;pending");
assert.equal(scanner.flush(), "\x1b]9;pending");
assert.equal(scanner.flush(), "");
});
test("OscNotificationStreamScanner aborts a notification OSC at a non-ST escape", () => {
const scanner = new OscNotificationStreamScanner();
const result = scanner.consume("hello\x1b]9;bad\x1b[31mRED\x07world");
assert.deepEqual(result.notifications, []);
assert.equal(result.remainder, "hello\x1b[31mRED\x07world");
});
test("OscNotificationStreamScanner aborts a notification OSC on CAN or SUB", () => {
const scanner = new OscNotificationStreamScanner();
const can = scanner.consume("pre\x1b]9;bad\x18visible\x07");
assert.deepEqual(can.notifications, []);
assert.equal(can.remainder, "previsible\x07");
const sub = scanner.consume("\x1b]777;notify;x;y\x1aafter");
assert.deepEqual(sub.notifications, []);
assert.equal(sub.remainder, "after");
});
test("OscNotificationLimiter rate-limits a noisy session", () => {
const limiter = new OscNotificationLimiter(1_000, 2, 100);
assert.equal(limiter.allow("s1", 1_000), true);
assert.equal(limiter.allow("s1", 1_050), false);
assert.equal(limiter.allow("s1", 1_200), true);
assert.equal(limiter.allow("s1", 1_400), false);
assert.equal(limiter.allow("s2", 1_400), true);
});