import test from "node:test"; import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; async function readSource(path: string) { return readFile(new URL(path, import.meta.url), "utf8"); } test("ProjectDetailActivity keeps a rendered project snapshot for append-only realtime message patches", async () => { const source = await readSource("../android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java"); assert.match( source, /private @Nullable JSONObject currentRenderedProjectPayload;/, "expected chat page to keep the latest rendered project payload for incremental realtime diffs", ); assert.match( source, /if \(tryAppendRealtimeMessagesPatch\(projectMessagesPayload\)\) \{\s*return true;\s*\}/, "expected chat page to try an append-only realtime patch before falling back to a full message rerender", ); assert.match( source, /private boolean tryAppendRealtimeMessagesPatch\(JSONObject projectMessagesPayload\)/, "expected chat page to expose a dedicated append-only realtime patch helper", ); assert.match( source, /appendContent\(buildMessageView\(message\)\);/, "expected append-only realtime patches to add only the new message views", ); assert.match( source, /if \(trySkipUnchangedRealtimeMessagesPatch\(projectMessagesPayload\)\) \{\s*return true;\s*\}/, "expected chat page to skip duplicate realtime message payloads before rerendering", ); assert.match( source, /private boolean trySkipUnchangedRealtimeMessagesPatch\(JSONObject projectMessagesPayload\)/, "expected chat page to expose a duplicate-payload fast path", ); assert.match( source, /private boolean hasMatchingExecutionWarnings\(JSONObject currentPayload,\s*JSONObject nextPayload\)/, "expected chat page to compare executionWarnings separately from the message list", ); assert.match( source, /private boolean hasMatchingConversationTasks\(JSONObject currentPayload,\s*JSONObject nextPayload\)/, "expected chat page to compare conversationTasks separately from the message list", ); assert.match( source, /if \(!hasMatchingExecutionWarnings\(currentRenderedProjectPayload,\s*projectMessagesPayload\)\) \{\s*return false;\s*\}/, "expected append-only realtime patches to fall back when warning payloads changed", ); assert.match( source, /if \(!hasMatchingConversationTasks\(currentRenderedProjectPayload,\s*projectMessagesPayload\)\) \{\s*return false;\s*\}/, "expected append-only realtime patches to fall back when task payloads changed", ); assert.match( source, /JSONObject conversationTask = findConversationTask\(currentRenderedProjectPayload,\s*messageId\);/, "expected each message view to look up a task summary by request message id", ); assert.match( source, /if \(tryPatchRealtimeExecutionWarnings\(projectMessagesPayload\)\) \{\s*return true;\s*\}/, "expected chat page to patch warning-only realtime changes before falling back to full rerender", ); assert.match( source, /private boolean tryPatchRealtimeExecutionWarnings\(JSONObject projectMessagesPayload\)/, "expected chat page to expose a focused warning patch helper", ); assert.match( source, /replaceMessageViewById\(messageId,\s*buildMessageView\(message\)\);/, "expected warning-only patches to rerender only the affected message view", ); assert.match( source, /private void replaceMessageViewById\(String messageId,\s*View nextMessageView\)/, "expected chat page to expose a helper for targeted message view replacement", ); assert.match( source, /wrapper\.addView\(statusRow\);/, "expected each message bubble to append a compact status row", ); assert.match( source, /List messageWarnings = buildMessageWarnings\(currentRenderedProjectPayload,\s*messageId\);/, "expected message views to gather grouped warnings for the status row", ); assert.match( source, /String currentFingerprint = buildStatusFingerprint\(messageId, currentRenderedProjectPayload\);/, "expected realtime patches to compute the current status fingerprint before replacing a message view", ); assert.match( source, /String nextFingerprint = buildStatusFingerprint\(messageId, projectMessagesPayload\);/, "expected realtime patches to compute a fingerprint before replacing a message view", ); assert.match( source, /if \(!TextUtils\.equals\(currentFingerprint,\s*nextFingerprint\)\) \{/, "expected realtime warning patches to branch on status fingerprint changes before replacing views", ); assert.match( source, /if \(hasMatchingExecutionWarnings\(currentRenderedProjectPayload,\s*projectMessagesPayload\)\s*&&\s*hasMatchingConversationTasks\(currentRenderedProjectPayload,\s*projectMessagesPayload\)\) \{\s*return false;\s*\}/, "expected status-only patch path to stay idle only when both warnings and task payloads are unchanged", ); }); test("ProjectDetailActivity suppresses intermediate layouts while rebuilding or appending chat content", async () => { const source = await readSource("../android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java"); assert.match( source, /private void runWithSuppressedContentLayout\(Runnable action\)/, "expected chat page to centralize layout suppression for bulk content updates", ); assert.match( source, /contentLayout\.suppressLayout\(true\);/, "expected chat page to pause intermediate layout passes during bulk updates", ); assert.match( source, /contentLayout\.suppressLayout\(false\);/, "expected chat page to resume layout after bulk updates", ); });