* fix(app): keep focused agent timelines live Window focus was conflated with app visibility, so switching OS windows could remove the selected timeline subscription. Separate those signals, grace every visibility-driven removal, and cover retained history until authoritative catch-up completes. * fix(app): keep timeline catch-up failures non-blocking * fix(app): surface timeline catch-up failures * fix(app): preserve file preview refocus * fix(app): preserve optimistic catch-up flow
135 lines
3.9 KiB
TypeScript
135 lines
3.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
EMPTY_FOCUS_CLAIM_STATE,
|
|
canRequestFocusClaim,
|
|
reconcileFocusClaim,
|
|
settleFocusClaim,
|
|
type FocusClaimState,
|
|
} from "./terminal-pane-focus-claim";
|
|
|
|
function request(
|
|
state: FocusClaimState,
|
|
input: { key: string | null; canRequest: boolean },
|
|
): FocusClaimState {
|
|
return reconcileFocusClaim(state, input).state;
|
|
}
|
|
|
|
describe("terminal pane focus claim", () => {
|
|
it("waits for both the client and renderer before requesting a claim", () => {
|
|
const withoutClient = canRequestFocusClaim({
|
|
isWorkspaceFocused: true,
|
|
isAppActivelyVisible: true,
|
|
isClientReady: false,
|
|
isConnected: true,
|
|
isRendererReady: true,
|
|
});
|
|
const withoutRenderer = canRequestFocusClaim({
|
|
isWorkspaceFocused: true,
|
|
isAppActivelyVisible: true,
|
|
isClientReady: true,
|
|
isConnected: true,
|
|
isRendererReady: false,
|
|
});
|
|
|
|
expect([withoutClient, withoutRenderer]).toEqual([false, false]);
|
|
});
|
|
|
|
it("does not deliver a requested claim after the host disconnects", () => {
|
|
const disconnected = canRequestFocusClaim({
|
|
isWorkspaceFocused: true,
|
|
isAppActivelyVisible: true,
|
|
isClientReady: true,
|
|
isConnected: false,
|
|
isRendererReady: true,
|
|
});
|
|
|
|
expect(disconnected).toBe(false);
|
|
});
|
|
|
|
it("claims once per continuous pane-focus period after send", () => {
|
|
const firstRequest = reconcileFocusClaim(EMPTY_FOCUS_CLAIM_STATE, {
|
|
key: "ws:term-1",
|
|
canRequest: true,
|
|
});
|
|
const sent = settleFocusClaim(firstRequest.state, {
|
|
key: "ws:term-1",
|
|
sent: true,
|
|
});
|
|
const repeated = reconcileFocusClaim(sent, {
|
|
key: "ws:term-1",
|
|
canRequest: true,
|
|
});
|
|
|
|
expect(firstRequest.shouldRequest).toBe(true);
|
|
expect(repeated).toEqual({
|
|
state: { claimedKey: "ws:term-1", requestedKey: null },
|
|
shouldRequest: false,
|
|
});
|
|
});
|
|
|
|
it("defers until the claim can be requested", () => {
|
|
const unavailable = reconcileFocusClaim(EMPTY_FOCUS_CLAIM_STATE, {
|
|
key: "ws:term-1",
|
|
canRequest: false,
|
|
});
|
|
const available = reconcileFocusClaim(unavailable.state, {
|
|
key: "ws:term-1",
|
|
canRequest: true,
|
|
});
|
|
|
|
expect(unavailable.shouldRequest).toBe(false);
|
|
expect(available.shouldRequest).toBe(true);
|
|
});
|
|
|
|
it("retries when readiness changes before the requested claim lands", () => {
|
|
const requested = request(EMPTY_FOCUS_CLAIM_STATE, {
|
|
key: "ws:term-1",
|
|
canRequest: true,
|
|
});
|
|
const hiddenBeforeDelivery = request(requested, {
|
|
key: "ws:term-1",
|
|
canRequest: false,
|
|
});
|
|
const visibleAgain = reconcileFocusClaim(hiddenBeforeDelivery, {
|
|
key: "ws:term-1",
|
|
canRequest: true,
|
|
});
|
|
|
|
expect(visibleAgain).toEqual({
|
|
state: { claimedKey: null, requestedKey: "ws:term-1" },
|
|
shouldRequest: true,
|
|
});
|
|
});
|
|
|
|
it("retries a requested claim that was not sent", () => {
|
|
const requested = request(EMPTY_FOCUS_CLAIM_STATE, {
|
|
key: "ws:term-1",
|
|
canRequest: true,
|
|
});
|
|
const dropped = settleFocusClaim(requested, {
|
|
key: "ws:term-1",
|
|
sent: false,
|
|
});
|
|
const retry = reconcileFocusClaim(dropped, {
|
|
key: "ws:term-1",
|
|
canRequest: true,
|
|
});
|
|
|
|
expect(retry.shouldRequest).toBe(true);
|
|
});
|
|
|
|
it("re-arms after pane blur or terminal change", () => {
|
|
const requested = request(EMPTY_FOCUS_CLAIM_STATE, {
|
|
key: "ws:term-1",
|
|
canRequest: true,
|
|
});
|
|
const sent = settleFocusClaim(requested, { key: "ws:term-1", sent: true });
|
|
const blurred = request(sent, { key: null, canRequest: true });
|
|
const refocused = reconcileFocusClaim(blurred, { key: "ws:term-1", canRequest: true });
|
|
const changed = reconcileFocusClaim(sent, { key: "ws:term-2", canRequest: true });
|
|
|
|
expect(refocused.shouldRequest).toBe(true);
|
|
expect(changed.shouldRequest).toBe(true);
|
|
});
|
|
});
|