Files
MeshChatX/tests/frontend/AppIdentitySwitch.test.js
T

163 lines
5.7 KiB
JavaScript

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import App from "../../meshchatx/src/frontend/components/App.vue";
import ToastUtils from "../../meshchatx/src/frontend/js/ToastUtils";
import GlobalEmitter from "../../meshchatx/src/frontend/js/GlobalEmitter";
import GlobalState from "../../meshchatx/src/frontend/js/GlobalState";
vi.mock("../../meshchatx/src/frontend/js/ToastUtils", () => ({
default: {
success: vi.fn(),
error: vi.fn(),
warning: vi.fn(),
info: vi.fn(),
},
}));
vi.mock("../../meshchatx/src/frontend/js/GlobalEmitter", () => ({
default: {
emit: vi.fn(),
on: vi.fn(),
off: vi.fn(),
},
}));
function makeCtx() {
return {
identitySwitchDedupeHash: null,
identitySwitchDedupeAt: 0,
isSwitchingIdentity: true,
getConfig: vi.fn().mockResolvedValue(undefined),
updateRingtonePlayer: vi.fn().mockResolvedValue(undefined),
getAppInfo: vi.fn().mockResolvedValue(undefined),
$t: (key) => key,
};
}
describe("App.vue applyIdentitySwitched", () => {
beforeEach(() => {
vi.clearAllMocks();
GlobalState.unreadConversationsCount = 3;
});
afterEach(() => {
GlobalState.unreadConversationsCount = 0;
vi.useRealTimers();
});
it("applies identity switch, resets unread, and clears overlay", async () => {
const ctx = makeCtx();
await App.methods.applyIdentitySwitched.call(ctx, {
identity_hash: "h1",
display_name: "User One",
});
expect(ToastUtils.success).toHaveBeenCalledWith("identities.switched");
expect(GlobalState.unreadConversationsCount).toBe(0);
expect(ctx.getConfig).toHaveBeenCalledTimes(1);
expect(ctx.updateRingtonePlayer).toHaveBeenCalledTimes(1);
expect(ctx.getAppInfo).toHaveBeenCalledTimes(1);
expect(ctx.isSwitchingIdentity).toBe(false);
expect(GlobalEmitter.emit).toHaveBeenCalledWith(
"identity-switched",
expect.objectContaining({
identity_hash: "h1",
display_name: "User One",
})
);
});
it("dedupes rapid duplicate applies for the same hash (WS + HTTP race)", async () => {
const ctx = makeCtx();
await App.methods.applyIdentitySwitched.call(ctx, {
identity_hash: "same",
display_name: "First",
});
await App.methods.applyIdentitySwitched.call(ctx, {
identity_hash: "same",
display_name: "Second",
});
expect(ctx.getConfig).toHaveBeenCalledTimes(1);
expect(ToastUtils.success).toHaveBeenCalledTimes(1);
expect(GlobalEmitter.emit).toHaveBeenCalledTimes(1);
});
it("applies again for a different identity hash", async () => {
const ctx = makeCtx();
await App.methods.applyIdentitySwitched.call(ctx, {
identity_hash: "h1",
display_name: "A",
});
await App.methods.applyIdentitySwitched.call(ctx, {
identity_hash: "h2",
display_name: "B",
});
expect(ctx.getConfig).toHaveBeenCalledTimes(2);
expect(ToastUtils.success).toHaveBeenCalledTimes(2);
});
it("no-ops when identity_hash is empty", async () => {
const ctx = makeCtx();
await App.methods.applyIdentitySwitched.call(ctx, {
identity_hash: "",
display_name: "X",
});
expect(ctx.getConfig).not.toHaveBeenCalled();
expect(GlobalEmitter.emit).not.toHaveBeenCalled();
});
it("no-ops when identity_hash is missing", async () => {
const ctx = makeCtx();
await App.methods.applyIdentitySwitched.call(ctx, {
display_name: "X",
});
expect(ctx.getConfig).not.toHaveBeenCalled();
});
it("re-applies same hash after dedupe window expires", async () => {
vi.useFakeTimers();
vi.setSystemTime(1_000_000);
const ctx = makeCtx();
await App.methods.applyIdentitySwitched.call(ctx, {
identity_hash: "h1",
display_name: "A",
});
expect(ctx.getConfig).toHaveBeenCalledTimes(1);
vi.setSystemTime(1_000_000 + 10_001);
vi.clearAllMocks();
await App.methods.applyIdentitySwitched.call(ctx, {
identity_hash: "h1",
display_name: "B",
});
expect(ctx.getConfig).toHaveBeenCalledTimes(1);
expect(ToastUtils.success).toHaveBeenCalledTimes(1);
expect(GlobalEmitter.emit).toHaveBeenCalledTimes(1);
});
it("performance: dedupe path skips async work for many duplicate applies", async () => {
const ctx = makeCtx();
await App.methods.applyIdentitySwitched.call(ctx, {
identity_hash: "hot",
display_name: "A",
});
vi.clearAllMocks();
const t0 = performance.now();
const n = 2000;
for (let i = 0; i < n; i++) {
await App.methods.applyIdentitySwitched.call(ctx, {
identity_hash: "hot",
display_name: "A",
});
}
expect(performance.now() - t0).toBeLessThan(1500);
expect(ctx.getConfig).not.toHaveBeenCalled();
expect(GlobalEmitter.emit).not.toHaveBeenCalled();
});
it("onIdentitySwitchedApplyShell delegates to this.applyIdentitySwitched", async () => {
const inner = vi.fn().mockResolvedValue(undefined);
const ctx = { applyIdentitySwitched: inner };
App.methods.onIdentitySwitchedApplyShell.call(ctx, { identity_hash: "x", display_name: "Y" });
await Promise.resolve();
expect(inner).toHaveBeenCalledWith({ identity_hash: "x", display_name: "Y" });
});
});