From 16bd39c812daf7169d2c3276d4a4cf15af669232 Mon Sep 17 00:00:00 2001 From: Ivan Date: Sun, 26 Apr 2026 15:02:16 -0500 Subject: [PATCH] feat(tests): add unit tests for ConversationViewer's outbound propagation status handling, enhancing coverage for message state and method propagation --- ...onViewer.outboundPropagationStatus.test.js | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 tests/frontend/ConversationViewer.outboundPropagationStatus.test.js diff --git a/tests/frontend/ConversationViewer.outboundPropagationStatus.test.js b/tests/frontend/ConversationViewer.outboundPropagationStatus.test.js new file mode 100644 index 0000000..94b10aa --- /dev/null +++ b/tests/frontend/ConversationViewer.outboundPropagationStatus.test.js @@ -0,0 +1,146 @@ +import { mount } from "@vue/test-utils"; +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import ConversationViewer from "@/components/messages/ConversationViewer.vue"; +import WebSocketConnection from "@/js/WebSocketConnection"; +import GlobalState from "@/js/GlobalState"; + +describe("ConversationViewer outbound propagation status", () => { + let axiosMock; + + beforeEach(() => { + GlobalState.config.theme = "light"; + GlobalState.config.message_outbound_bubble_color = "#4f46e5"; + GlobalState.config.message_waiting_bubble_color = "#e5e7eb"; + GlobalState.config.warn_on_stranger_links = true; + GlobalState.detailedOutboundSendStatus = true; + + WebSocketConnection.connect(); + axiosMock = { + get: vi.fn().mockImplementation((url) => { + if (url.includes("/path")) return Promise.resolve({ data: { path: [] } }); + if (url.includes("/stamp-info")) return Promise.resolve({ data: { lxmf_stamp_info: {} } }); + if (url.includes("/signal-metrics")) return Promise.resolve({ data: { signal_metrics: {} } }); + return Promise.resolve({ data: {} }); + }), + post: vi.fn().mockResolvedValue({ data: {} }), + }; + window.api = axiosMock; + }); + + afterEach(() => { + GlobalState.detailedOutboundSendStatus = false; + delete window.api; + WebSocketConnection.destroy(); + }); + + const mountViewer = (extraMocks = {}) => + mount(ConversationViewer, { + props: { + selectedPeer: { destination_hash: "peerhash111111111111111111111111", display_name: "Peer" }, + myLxmfAddressHash: "myhash11111111111111111111111111", + conversations: [], + }, + global: { + directives: { "click-outside": { mounted: () => {}, unmounted: () => {} } }, + mocks: { + $t: (key, params) => { + if (params && Object.keys(params).length) { + return `${key}:${JSON.stringify(params)}`; + } + return key; + }, + $route: { meta: {} }, + $router: { push: vi.fn() }, + ...extraMocks, + }, + stubs: { + MaterialDesignIcon: true, + AddImageButton: true, + AddAudioButton: true, + SendMessageButton: true, + ConversationDropDownMenu: true, + PaperMessageModal: true, + AudioWaveformPlayer: true, + LxmfUserIcon: true, + }, + }, + }); + + it("outboundSentStatusTitle uses propagation copy when method is propagated", () => { + const wrapper = mountViewer(); + expect(wrapper.vm.outboundSentStatusTitle({ method: "propagated", state: "sent" })).toBe( + "messages.outbound_on_propagation_node" + ); + expect(wrapper.vm.outboundSentStatusTitle({ method: "direct", state: "sent" })).toBe( + "messages.outbound_sent_network" + ); + expect(wrapper.vm.outboundSentStatusTitle(null)).toBe(""); + }); + + it("outboundSendingStatusTooltip uses propagation pending strings for propagated method", () => { + const wrapper = mountViewer(); + const withProgress = wrapper.vm.outboundSendingStatusTooltip({ + method: "propagated", + state: "sending", + progress: 40, + }); + expect(withProgress).toContain("messages.outbound_pending_propagation_with_progress"); + expect(withProgress).toContain('"progress":"40"'); + + const pending = wrapper.vm.outboundSendingStatusTooltip({ + method: "propagated", + state: "sending", + progress: 0, + }); + expect(pending).toBe("messages.outbound_pending_propagation"); + + expect( + wrapper.vm.outboundSendingStatusTooltip({ + method: "propagated", + state: "outbound", + }) + ).toBe("messages.outbound_pending_propagation"); + }); + + it("outboundBubbleStatusHoverTitle uses propagation pending for outbound state", () => { + const wrapper = mountViewer(); + expect( + wrapper.vm.outboundBubbleStatusHoverTitle({ + method: "propagated", + state: "outbound", + }) + ).toBe("messages.outbound_pending_propagation"); + }); + + it("onLxmfMessageUpdated preserves merged method when websocket sends propagated handoff", () => { + const wrapper = mountViewer(); + const hash = "abc123def456789012345678901234ab"; + wrapper.vm.chatItems = [ + { + type: "lxmf_message", + is_outbound: true, + lxmf_message: { + hash, + destination_hash: "peerhash111111111111111111111111", + state: "sending", + method: "direct", + progress: 10, + content: "hi", + fields: {}, + }, + }, + ]; + + wrapper.vm.onLxmfMessageUpdated({ + hash, + state: "sent", + method: "propagated", + progress: 100, + }); + + const row = wrapper.vm.chatItems[0].lxmf_message; + expect(row.state).toBe("sent"); + expect(row.method).toBe("propagated"); + expect(row.progress).toBe(100); + }); +});