Files
bitvid/tests/ui/dm-set-recipient-active.test.mjs
T
thePR0M3TH3ANandClaude Opus 4.8 ab68ba51d0 dm: make a selected recipient the active conversation in the right pane
Clicking "Message" added the user to the sidebar list but the right-hand
message pane stayed on the previously-active conversation. setDirectMessageRecipient
called setFocusedDmConversation (which only tracks read-state) and never set
activeDmConversationId — the value buildDmConversationData reads to choose which
thread the right pane renders — so it kept resolving the stale id.

Set activeDmConversationId directly (mirroring handleDmConversationSelect), and
switch the mobile pane to the thread for explicit selections (not the auto
thread-default).

Regression: tests/ui/dm-set-recipient-active.test.mjs.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-23 21:01:04 -04:00

95 lines
3.2 KiB
JavaScript

// Regression: clicking the "Message" button added the user to the sidebar but
// the right-hand message pane stayed on the previously-active conversation.
// setDirectMessageRecipient relied on setFocusedDmConversation (which only
// tracks read-state) and never updated activeDmConversationId — the value
// buildDmConversationData reads to decide which thread the right pane renders.
//
// Scenario (SCN-dm-select-makes-active):
// Given a controller whose activeDmConversationId points at an old thread,
// When a new recipient is selected via setDirectMessageRecipient,
// Then activeDmConversationId becomes the new recipient's conversation id
// (so the right pane switches), and the mobile pane shows the thread.
import test from "node:test";
import assert from "node:assert/strict";
import { ProfileDirectMessageController } from "../../js/ui/profileModal/ProfileDirectMessageController.js";
// The method does `instanceof HTMLElement` guards; the stubbed DOM refs are null,
// so the constructor just needs the global to exist.
if (typeof globalThis.HTMLElement === "undefined") {
globalThis.HTMLElement = class HTMLElement {};
}
const ACTOR = "a".repeat(64);
const OLD = "b".repeat(64);
const NEW = "c".repeat(64);
function makeFakeController() {
const focusCalls = [];
return {
activeDmConversationId: `dm:${[ACTOR, OLD].sort().join(":")}`,
dmMobileView: "list",
dmPrivacyToggleTouched: true,
directMessagesCache: [],
focusCalls,
mainController: {
normalizeHexPubkey: (v) => (typeof v === "string" && v.trim() ? v.trim() : ""),
state: { setDmRecipient: () => {} },
},
helper: {
resolveActiveDmActor: () => ACTOR,
buildDmConversationId: (actor, remote) =>
`dm:${[actor, remote].sort().join(":")}`,
ensureDmRecipientData: () => {},
},
renderer: {
profileMessagesList: null,
dmAppShellContainer: null,
setMessagesAnnouncement: () => {},
renderDirectMessageConversation: () => {},
renderDmAppShell: () => {},
},
updateMessageThreadSelection() {},
setFocusedDmConversation(id) {
focusCalls.push(id);
},
};
}
test("selecting a recipient makes its conversation active in the right pane", () => {
const ctx = makeFakeController();
const expectedId = `dm:${[ACTOR, NEW].sort().join(":")}`;
const result = ProfileDirectMessageController.prototype.setDirectMessageRecipient.call(
ctx,
NEW,
{ reason: "external" },
);
assert.equal(result, NEW, "returns the normalized recipient");
assert.equal(
ctx.activeDmConversationId,
expectedId,
"activeDmConversationId must move to the selected recipient (not stay on the old thread)",
);
assert.equal(ctx.dmMobileView, "thread", "mobile pane switches to the thread");
assert.deepEqual(
ctx.focusCalls,
[expectedId],
"still tracks read-state for the focused conversation",
);
});
test("the auto thread-default does not force the mobile thread view", () => {
const ctx = makeFakeController();
ProfileDirectMessageController.prototype.setDirectMessageRecipient.call(ctx, NEW, {
reason: "thread-default",
});
assert.equal(
ctx.dmMobileView,
"list",
"thread-default keeps the inbox list visible on mobile",
);
});