138 lines
5.6 KiB
TypeScript
138 lines
5.6 KiB
TypeScript
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<JSONObject> 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",
|
|
);
|
|
});
|