630 lines
20 KiB
TypeScript
630 lines
20 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
|
|
let runtimeRoot = "";
|
|
let readState: (typeof import("../src/lib/boss-data"))["readState"];
|
|
let getConversationHomeItems: (typeof import("../src/lib/boss-projections"))["getConversationHomeItems"];
|
|
let getConversationFolderView: (typeof import("../src/lib/boss-projections"))["getConversationFolderView"];
|
|
let formatTimestampLabel: (typeof import("../src/lib/boss-projections"))["formatTimestampLabel"];
|
|
let getConversationListItemPresentation: (typeof import("../src/components/app-ui"))["getConversationListItemPresentation"];
|
|
|
|
async function setup() {
|
|
if (runtimeRoot) return;
|
|
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-conversation-home-"));
|
|
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
|
|
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
|
|
|
|
const [data, projections, ui] = await Promise.all([
|
|
import("../src/lib/boss-data.ts"),
|
|
import("../src/lib/boss-projections.ts"),
|
|
import("../src/components/app-ui.tsx"),
|
|
]);
|
|
readState = data.readState;
|
|
getConversationHomeItems = projections.getConversationHomeItems;
|
|
getConversationFolderView = projections.getConversationFolderView;
|
|
formatTimestampLabel = projections.formatTimestampLabel;
|
|
getConversationListItemPresentation = ui.getConversationListItemPresentation;
|
|
}
|
|
|
|
test.after(async () => {
|
|
if (runtimeRoot) {
|
|
await rm(runtimeRoot, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
function buildImportedThreadProject(deviceId: string, id: string, folderName: string, codexFolderRef: string, threadName: string, threadId: string, lastMessageAt: string) {
|
|
return {
|
|
id,
|
|
name: threadName,
|
|
pinned: false,
|
|
systemPinned: false,
|
|
deviceIds: [deviceId],
|
|
preview: `最近消息:${threadName}`,
|
|
updatedAt: lastMessageAt,
|
|
lastMessageAt,
|
|
isGroup: false,
|
|
threadMeta: {
|
|
projectId: id,
|
|
threadId,
|
|
threadDisplayName: threadName,
|
|
folderName,
|
|
activityIconCount: 1,
|
|
updatedAt: lastMessageAt,
|
|
codexFolderRef,
|
|
codexThreadRef: threadId,
|
|
},
|
|
groupMembers: [],
|
|
createdByAgent: true,
|
|
collaborationMode: "development" as const,
|
|
approvalState: "not_required" as const,
|
|
unreadCount: 0,
|
|
riskLevel: "low" as const,
|
|
messages: [],
|
|
goals: [],
|
|
versions: [],
|
|
};
|
|
}
|
|
|
|
function buildThreadContextSnapshot(
|
|
projectId: string,
|
|
threadId: string,
|
|
title: string,
|
|
contextBudgetLevel: "safe" | "watch" | "urgent" | "critical",
|
|
mustFinishBeforeCompaction: boolean,
|
|
contextBudgetRemainingPct: number,
|
|
capturedAt: string,
|
|
nodeId: string,
|
|
) {
|
|
return {
|
|
snapshotId: `${projectId}-${threadId}-snapshot`,
|
|
projectId,
|
|
taskId: `${projectId}-${threadId}-task`,
|
|
threadId,
|
|
title,
|
|
summary: `${title} 的线程状态`,
|
|
nodeId,
|
|
workerId: "worker-1",
|
|
sourceKind: "worker_estimator",
|
|
status: "context_urgent",
|
|
contextBudgetRemainingPct,
|
|
contextBudgetLevel,
|
|
mustFinishBeforeCompaction,
|
|
estimatedRemainingTurns: 4,
|
|
estimatedRemainingLargeMessages: 2,
|
|
compactionCount: 0,
|
|
patchPending: false,
|
|
testsPending: false,
|
|
evidencePending: false,
|
|
checklist: [],
|
|
capturedAt,
|
|
} satisfies import("../src/lib/boss-data").ThreadContextSnapshot;
|
|
}
|
|
|
|
test("folder archives use the latest thread preview/time while subtitle and context ring come from the right threads", async () => {
|
|
await setup();
|
|
const state = await readState();
|
|
|
|
state.projects = state.projects.filter((project) => project.id === "master-agent");
|
|
state.projects.push(
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-latest",
|
|
"Boss",
|
|
"boss",
|
|
"最新线程",
|
|
"thread-latest",
|
|
"2026-04-04T12:00:00+08:00",
|
|
),
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-urgent",
|
|
"Boss",
|
|
"boss",
|
|
"优先收尾",
|
|
"thread-urgent",
|
|
"2026-04-04T11:00:00+08:00",
|
|
),
|
|
);
|
|
state.threadContextSnapshots = [
|
|
buildThreadContextSnapshot(
|
|
"boss-thread-latest",
|
|
"thread-latest",
|
|
"最新线程",
|
|
"critical",
|
|
false,
|
|
12,
|
|
"2026-04-04T12:05:00+08:00",
|
|
"node-latest",
|
|
),
|
|
buildThreadContextSnapshot(
|
|
"boss-thread-urgent",
|
|
"thread-urgent",
|
|
"优先收尾",
|
|
"critical",
|
|
true,
|
|
87,
|
|
"2026-04-04T11:05:00+08:00",
|
|
"node-urgent",
|
|
),
|
|
];
|
|
|
|
const folder = getConversationHomeItems(state).find((item) => item.conversationType === "folder_archive");
|
|
|
|
assert.ok(folder, "expected grouped folder archive item");
|
|
assert.equal(folder?.threadTitle, "Boss");
|
|
assert.equal(folder?.folderLabel, "2 个线程 · 最近:最新线程");
|
|
assert.equal(folder?.preview, "最近消息:最新线程");
|
|
assert.equal(folder?.lastMessagePreview, "最近消息:最新线程");
|
|
assert.equal(folder?.latestReplyAt, "2026-04-04T12:00:00+08:00");
|
|
assert.equal(folder?.latestReplyLabel, formatTimestampLabel("2026-04-04T12:00:00+08:00"));
|
|
assert.equal(folder?.contextBudgetIndicator.level, "critical");
|
|
assert.equal(folder?.contextBudgetIndicator.percent, 87);
|
|
assert.equal(folder?.mustFinishBeforeCompaction, true);
|
|
assert.equal(folder?.contextBudgetSourceNodeId, "node-urgent");
|
|
assert.equal(folder?.contextBudgetUpdatedAt, "2026-04-04T11:05:00+08:00");
|
|
});
|
|
|
|
test("folder archive context ring prefers more urgent contextBudgetLevel when mustFinishBeforeCompaction is equal", async () => {
|
|
await setup();
|
|
const state = await readState();
|
|
|
|
state.projects = state.projects.filter((project) => project.id === "master-agent");
|
|
state.projects.push(
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-latest",
|
|
"Boss",
|
|
"boss",
|
|
"最新线程",
|
|
"thread-latest",
|
|
"2026-04-04T12:00:00+08:00",
|
|
),
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-watch",
|
|
"Boss",
|
|
"boss",
|
|
"次要关注",
|
|
"thread-watch",
|
|
"2026-04-04T11:00:00+08:00",
|
|
),
|
|
);
|
|
state.threadContextSnapshots = [
|
|
buildThreadContextSnapshot(
|
|
"boss-thread-latest",
|
|
"thread-latest",
|
|
"最新线程",
|
|
"watch",
|
|
false,
|
|
22,
|
|
"2026-04-04T12:05:00+08:00",
|
|
"node-watch",
|
|
),
|
|
buildThreadContextSnapshot(
|
|
"boss-thread-watch",
|
|
"thread-watch",
|
|
"次要关注",
|
|
"urgent",
|
|
false,
|
|
61,
|
|
"2026-04-04T11:05:00+08:00",
|
|
"node-urgent",
|
|
),
|
|
];
|
|
|
|
const folder = getConversationHomeItems(state).find((item) => item.conversationType === "folder_archive");
|
|
|
|
assert.ok(folder, "expected grouped folder archive item");
|
|
assert.equal(folder?.contextBudgetIndicator.level, "urgent");
|
|
assert.equal(folder?.contextBudgetIndicator.percent, 61);
|
|
assert.equal(folder?.contextBudgetSourceNodeId, "node-urgent");
|
|
assert.equal(folder?.contextBudgetUpdatedAt, "2026-04-04T11:05:00+08:00");
|
|
});
|
|
|
|
test("folder archive context ring prefers newer latestReplyAt when mustFinishBeforeCompaction and contextBudgetLevel are equal", async () => {
|
|
await setup();
|
|
const state = await readState();
|
|
|
|
state.projects = state.projects.filter((project) => project.id === "master-agent");
|
|
state.projects.push(
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-older",
|
|
"Boss",
|
|
"boss",
|
|
"较早线程",
|
|
"thread-older",
|
|
"2026-04-04T10:00:00+08:00",
|
|
),
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-newer",
|
|
"Boss",
|
|
"boss",
|
|
"较新线程",
|
|
"thread-newer",
|
|
"2026-04-04T12:00:00+08:00",
|
|
),
|
|
);
|
|
state.threadContextSnapshots = [
|
|
buildThreadContextSnapshot(
|
|
"boss-thread-older",
|
|
"thread-older",
|
|
"较早线程",
|
|
"urgent",
|
|
false,
|
|
19,
|
|
"2026-04-04T10:05:00+08:00",
|
|
"node-older",
|
|
),
|
|
buildThreadContextSnapshot(
|
|
"boss-thread-newer",
|
|
"thread-newer",
|
|
"较新线程",
|
|
"urgent",
|
|
false,
|
|
19,
|
|
"2026-04-04T12:05:00+08:00",
|
|
"node-newer",
|
|
),
|
|
];
|
|
|
|
const folder = getConversationHomeItems(state).find((item) => item.conversationType === "folder_archive");
|
|
|
|
assert.ok(folder, "expected grouped folder archive item");
|
|
assert.equal(folder?.latestReplyAt, "2026-04-04T12:00:00+08:00");
|
|
assert.equal(folder?.contextBudgetIndicator.level, "urgent");
|
|
assert.equal(folder?.contextBudgetIndicator.percent, 19);
|
|
assert.equal(folder?.contextBudgetSourceNodeId, "node-newer");
|
|
assert.equal(folder?.contextBudgetUpdatedAt, "2026-04-04T12:05:00+08:00");
|
|
});
|
|
|
|
test("conversation home upgrades and downgrades a folder archive as thread count crosses 1 and 2", async () => {
|
|
await setup();
|
|
const state = await readState();
|
|
|
|
state.projects = state.projects.filter((project) => project.id === "master-agent");
|
|
state.projects.push(
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-1",
|
|
"Boss",
|
|
"boss",
|
|
"归档确认",
|
|
"thread-1",
|
|
"2026-04-04T10:00:00+08:00",
|
|
),
|
|
);
|
|
|
|
let items = getConversationHomeItems(state);
|
|
let direct = items.find((item) => item.projectId === "boss-thread-1");
|
|
|
|
assert.ok(direct, "expected a single thread to remain direct");
|
|
assert.equal(direct?.conversationType, "single_device");
|
|
|
|
state.projects.push(
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-2",
|
|
"Boss",
|
|
"boss",
|
|
"发布回滚",
|
|
"thread-2",
|
|
"2026-04-04T11:00:00+08:00",
|
|
),
|
|
);
|
|
|
|
items = getConversationHomeItems(state);
|
|
const folder = items.find((item) => item.conversationType === "folder_archive" && item.folderKey === "mac-studio:boss");
|
|
|
|
assert.ok(folder, "expected folder archive once the folder has 2 threads");
|
|
assert.equal(folder?.threadCount, 2);
|
|
assert.equal(items.some((item) => item.projectId === "boss-thread-1"), false);
|
|
assert.equal(items.some((item) => item.projectId === "boss-thread-2"), false);
|
|
|
|
state.projects = state.projects.filter((project) => project.id !== "boss-thread-2");
|
|
|
|
items = getConversationHomeItems(state);
|
|
direct = items.find((item) => item.projectId === "boss-thread-1");
|
|
|
|
assert.ok(direct, "expected a single remaining thread to downgrade back to direct");
|
|
assert.equal(direct?.conversationType, "single_device");
|
|
assert.equal(
|
|
items.some((item) => item.conversationType === "folder_archive" && item.folderKey === "mac-studio:boss"),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("conversation home groups multiple imported threads by folder while keeping single-thread projects direct", async () => {
|
|
await setup();
|
|
const state = await readState();
|
|
|
|
state.projects = state.projects.filter((project) => project.id === "master-agent");
|
|
state.projects.push(
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-1",
|
|
"Boss",
|
|
"boss",
|
|
"归档确认",
|
|
"thread-1",
|
|
"2026-03-30T11:00:00+08:00",
|
|
),
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-2",
|
|
"Boss",
|
|
"boss",
|
|
"发布回滚",
|
|
"thread-2",
|
|
"2026-03-30T12:00:00+08:00",
|
|
),
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"yuandi-thread-1",
|
|
"源地",
|
|
"yuandi",
|
|
"首页回归",
|
|
"thread-3",
|
|
"2026-03-30T10:00:00+08:00",
|
|
),
|
|
);
|
|
|
|
const homeItems = getConversationHomeItems(state);
|
|
const bossFolder = homeItems.find((item) => item.conversationType === "folder_archive");
|
|
const directThread = homeItems.find((item) => item.projectId === "yuandi-thread-1");
|
|
|
|
assert.ok(bossFolder, "expected grouped folder item for multi-thread project");
|
|
assert.equal(bossFolder?.threadTitle, "Boss");
|
|
assert.equal(bossFolder?.threadCount, 2);
|
|
assert.equal(bossFolder?.folderKey, "mac-studio:boss");
|
|
|
|
assert.ok(directThread, "expected single-thread project to stay direct");
|
|
assert.equal(directThread?.conversationType, "single_device");
|
|
assert.equal(directThread?.threadTitle, "首页回归");
|
|
|
|
const folderView = getConversationFolderView(state, "mac-studio:boss");
|
|
assert.ok(folderView, "expected folder detail view");
|
|
assert.equal(folderView?.threadCount, 2);
|
|
assert.deepEqual(
|
|
folderView?.threads.map((item) => item.threadTitle),
|
|
["发布回滚", "归档确认"],
|
|
);
|
|
});
|
|
|
|
test("folder archive homepage rows keep the project title, compact subtitle, and folder route", async () => {
|
|
await setup();
|
|
const state = await readState();
|
|
|
|
state.projects = state.projects.filter((project) => project.id === "master-agent");
|
|
state.projects.push(
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-1",
|
|
"Boss",
|
|
"boss",
|
|
"归档确认",
|
|
"thread-1",
|
|
"2026-03-30T11:00:00+08:00",
|
|
),
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-2",
|
|
"Boss",
|
|
"boss",
|
|
"发布回滚",
|
|
"thread-2",
|
|
"2026-03-30T12:00:00+08:00",
|
|
),
|
|
);
|
|
|
|
const folder = getConversationHomeItems(state).find((item) => item.conversationType === "folder_archive");
|
|
|
|
assert.ok(folder, "expected grouped folder archive item");
|
|
const presentation = getConversationListItemPresentation({
|
|
...folder!,
|
|
projectTitle: "项目标题",
|
|
threadTitle: "线程标题",
|
|
});
|
|
|
|
assert.equal(presentation.title, "项目标题");
|
|
assert.equal(presentation.subtitle, "2 个线程 · 最近:发布回滚");
|
|
assert.equal(presentation.href, "/conversations/folders/mac-studio%3Aboss");
|
|
});
|
|
|
|
test("conversation items expose context status while keeping idle activity silent", async () => {
|
|
await setup();
|
|
const state = await readState();
|
|
|
|
state.projects = state.projects.filter((project) => project.id === "master-agent");
|
|
state.masterAgentTasks = [];
|
|
state.dispatchExecutions = [];
|
|
state.projects.push(
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"boss-thread-1",
|
|
"Boss",
|
|
"boss",
|
|
"归档确认",
|
|
"thread-1",
|
|
"2026-03-30T11:00:00+08:00",
|
|
),
|
|
);
|
|
state.threadContextSnapshots = [
|
|
{
|
|
snapshotId: "snapshot-1",
|
|
workerId: "mac-studio",
|
|
projectId: "boss-thread-1",
|
|
threadId: "thread-1",
|
|
title: "归档确认",
|
|
summary: "上下文预算进入紧张区,需要尽快收尾。",
|
|
contextBudgetRemainingPct: 34,
|
|
contextBudgetLevel: "urgent",
|
|
mustFinishBeforeCompaction: false,
|
|
estimatedRemainingTurns: 8,
|
|
estimatedRemainingLargeMessages: 3,
|
|
compactionCount: 0,
|
|
patchPending: false,
|
|
testsPending: false,
|
|
evidencePending: false,
|
|
checklist: [],
|
|
capturedAt: "2026-03-30T11:00:00+08:00",
|
|
},
|
|
];
|
|
|
|
const [masterAgent, threadConversation] = getConversationHomeItems(state);
|
|
|
|
assert.equal(threadConversation.projectId, "boss-thread-1");
|
|
assert.equal(threadConversation.contextBudgetIndicator.visible, true);
|
|
assert.equal(threadConversation.contextBudgetIndicator.percent, 34);
|
|
assert.equal(threadConversation.contextBudgetIndicator.level, "urgent");
|
|
assert.equal(threadConversation.activityIconCount, 0);
|
|
assert.equal(masterAgent.activityIconCount, 0);
|
|
assert.equal(masterAgent.contextBudgetIndicator.visible, true);
|
|
assert.equal(masterAgent.contextBudgetIndicator.percent, 100);
|
|
assert.equal(masterAgent.contextBudgetIndicator.level, "safe");
|
|
});
|
|
|
|
test("conversation items keep a safe context ring even when no thread snapshot exists", async () => {
|
|
await setup();
|
|
const state = await readState();
|
|
|
|
state.projects = state.projects.filter((project) => project.id === "master-agent");
|
|
state.projects.push(
|
|
buildImportedThreadProject(
|
|
"mac-studio",
|
|
"single-thread-no-context",
|
|
"Talking",
|
|
"talking",
|
|
"调试回归",
|
|
"thread-no-context",
|
|
"2026-03-30T11:20:00+08:00",
|
|
),
|
|
);
|
|
state.threadContextSnapshots = [];
|
|
|
|
const items = getConversationHomeItems(state);
|
|
const directThread = items.find((item) => item.projectId === "single-thread-no-context");
|
|
|
|
assert.ok(directThread);
|
|
assert.equal(directThread?.contextBudgetIndicator.visible, true);
|
|
assert.equal(directThread?.contextBudgetIndicator.percent, 100);
|
|
assert.equal(directThread?.contextBudgetIndicator.level, "safe");
|
|
});
|
|
|
|
test("conversation items prefer latest observed codex activity over stale last message time", async () => {
|
|
await setup();
|
|
const state = await readState();
|
|
const baseProject = buildImportedThreadProject(
|
|
"mac-studio",
|
|
"stale-thread",
|
|
"Talking",
|
|
"talking",
|
|
"树莓派二代查询",
|
|
"thread-stale",
|
|
"2026-04-04T06:12:00+08:00",
|
|
);
|
|
|
|
state.projects = state.projects.filter((project) => project.id === "master-agent");
|
|
state.projects.push(
|
|
{
|
|
...baseProject,
|
|
threadMeta: {
|
|
...baseProject.threadMeta,
|
|
lastObservedCodexActivityAt: "2026-04-04T11:48:00+08:00",
|
|
},
|
|
},
|
|
);
|
|
|
|
const items = getConversationHomeItems(state);
|
|
const thread = items.find((item) => item.projectId === "stale-thread");
|
|
|
|
assert.ok(thread);
|
|
assert.equal(thread?.latestReplyAt, "2026-04-04T11:48:00+08:00");
|
|
assert.equal(thread?.latestReplyLabel, formatTimestampLabel("2026-04-04T11:48:00+08:00"));
|
|
});
|
|
|
|
test("default seeded conversations no longer expose Boss 移动控制台", async () => {
|
|
await setup();
|
|
const state = await readState();
|
|
|
|
const items = getConversationHomeItems(state);
|
|
|
|
assert.ok(items.some((item) => item.projectId === "master-agent"), "expected master-agent to remain available");
|
|
assert.equal(
|
|
items.some((item) => item.projectId === "boss-console" || item.threadTitle === "Boss 移动控制台"),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("readState migrates away persisted legacy boss-console conversations", async () => {
|
|
await setup();
|
|
const state = await readState();
|
|
|
|
const legacyState = structuredClone(state) as typeof state;
|
|
legacyState.projects.push({
|
|
id: "boss-console",
|
|
name: "Boss 移动控制台",
|
|
pinned: false,
|
|
systemPinned: false,
|
|
deviceIds: ["mac-studio"],
|
|
preview: "历史遗留的 boss-console 会话。",
|
|
updatedAt: "2026-04-04T12:20:00+08:00",
|
|
lastMessageAt: "2026-04-04T12:20:00+08:00",
|
|
isGroup: false,
|
|
threadMeta: {
|
|
projectId: "boss-console",
|
|
threadId: "thread-boss-console",
|
|
threadDisplayName: "Boss 移动控制台",
|
|
folderName: "Boss",
|
|
activityIconCount: 1,
|
|
updatedAt: "2026-04-04T12:20:00+08:00",
|
|
codexThreadRef: "thread-boss-console",
|
|
codexFolderRef: "boss-console",
|
|
},
|
|
groupMembers: [],
|
|
createdByAgent: true,
|
|
collaborationMode: "development",
|
|
approvalState: "not_required",
|
|
unreadCount: 0,
|
|
riskLevel: "low",
|
|
messages: [],
|
|
goals: [],
|
|
versions: [],
|
|
});
|
|
legacyState.threadContextSnapshots.push({
|
|
snapshotId: "legacy-boss-console-snapshot",
|
|
projectId: "boss-console",
|
|
taskId: "legacy-task",
|
|
threadId: "thread-boss-console",
|
|
title: "Boss 移动控制台线程",
|
|
summary: "遗留摘要",
|
|
nodeId: "mac-studio",
|
|
workerId: "worker-legacy",
|
|
sourceKind: "worker_estimator",
|
|
status: "running",
|
|
contextBudgetRemainingPct: 48,
|
|
contextBudgetLevel: "watch",
|
|
mustFinishBeforeCompaction: false,
|
|
estimatedRemainingTurns: 5,
|
|
estimatedRemainingLargeMessages: 2,
|
|
compactionCount: 0,
|
|
patchPending: false,
|
|
testsPending: false,
|
|
evidencePending: false,
|
|
checklist: [],
|
|
capturedAt: "2026-04-04T12:20:00+08:00",
|
|
});
|
|
|
|
const stateFile = process.env.BOSS_STATE_FILE;
|
|
assert.ok(stateFile, "expected test state file path");
|
|
await writeFile(stateFile, JSON.stringify(legacyState, null, 2), "utf8");
|
|
|
|
const migratedState = await readState();
|
|
assert.equal(migratedState.projects.some((item) => item.id === "boss-console"), false);
|
|
assert.equal(migratedState.threadContextSnapshots.some((item) => item.projectId === "boss-console"), false);
|
|
});
|