mirror of
https://github.com/Quad4-Software/MeshChatX.git
synced 2026-08-18 09:49:09 -04:00
130 lines
5.5 KiB
JavaScript
130 lines
5.5 KiB
JavaScript
// SPDX-License-Identifier: 0BSD
|
|
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
import { mount } from "@vue/test-utils";
|
|
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import Toast from "@/components/Toast.vue";
|
|
import CallOverlay from "@/components/call/CallOverlay.vue";
|
|
import GlobalEmitter from "@/js/GlobalEmitter";
|
|
|
|
const src = (rel) => readFileSync(resolve(__dirname, "../../meshchatx/src/frontend", rel), "utf8");
|
|
|
|
describe("layout UX oracle-light", () => {
|
|
it("App shell drawer sits in middle pane without fake pt-16 header gap", () => {
|
|
const app = src("components/App.vue");
|
|
expect(app).toContain('class="relative flex flex-1 w-full overflow-hidden');
|
|
expect(app).toContain("absolute inset-y-0 left-0 z-70");
|
|
expect(app).toContain("absolute inset-0 z-65");
|
|
expect(app).not.toContain("pt-16 sm:pt-0");
|
|
expect(app).toContain("pt-[env(safe-area-inset-top,0px)]");
|
|
});
|
|
|
|
it("ConversationViewer message list uses overflow-y-auto and composer safe-area", () => {
|
|
const cv = src("components/messages/ConversationViewer.vue");
|
|
expect(cv).toContain("overflow-y-auto bg-white dark:bg-zinc-950");
|
|
expect(cv).not.toMatch(/id="messages"[\s\S]{0,200}overflow-y-scroll/);
|
|
expect(cv).toContain("composerChromeStyle");
|
|
expect(cv).toContain("updateKeyboardInset");
|
|
expect(cv).toContain("env(safe-area-inset-bottom");
|
|
expect(cv).toContain("flex-col gap-3 text-sm sm:flex-row sm:items-center");
|
|
});
|
|
|
|
it("MessagesPage mobile compose FAB and sheet clear safe-area and use dvh", () => {
|
|
const page = src("components/messages/MessagesPage.vue");
|
|
expect(page).toContain("bottom-[max(1.25rem,env(safe-area-inset-bottom,0px))]");
|
|
expect(page).toContain("max-h-[90dvh]");
|
|
expect(page).not.toContain("max-h-[90vh]");
|
|
});
|
|
|
|
it("MicronEditor shows tab close on touch and splits at tablet width", () => {
|
|
const page = src("components/micron-editor/MicronEditorPage.vue");
|
|
expect(page).toContain("opacity-100 lg:opacity-0 lg:group-hover:opacity-100");
|
|
expect(page).toContain("window.innerWidth < 768");
|
|
});
|
|
|
|
it("Settings page scroll includes bottom safe-area", () => {
|
|
const page = src("components/settings/SettingsPage.vue");
|
|
expect(page).toContain("pb-[max(1.5rem,env(safe-area-inset-bottom))]");
|
|
});
|
|
|
|
describe("Toast + CallOverlay stacking", () => {
|
|
let toastWrapper;
|
|
let callWrapper;
|
|
|
|
beforeEach(() => {
|
|
toastWrapper = mount(Toast, {
|
|
global: {
|
|
mocks: { $t: (msg) => msg },
|
|
stubs: {
|
|
TransitionGroup: { template: "<div><slot /></div>" },
|
|
MaterialDesignIcon: { template: '<div class="mdi-stub"></div>', props: ["iconName"] },
|
|
},
|
|
},
|
|
});
|
|
callWrapper = mount(CallOverlay, {
|
|
props: {
|
|
activeCall: {
|
|
remote_identity_hash: "abcdef0123456789abcdef0123456789",
|
|
remote_identity_name: "Peer",
|
|
status: 6,
|
|
is_incoming: false,
|
|
is_voicemail: false,
|
|
call_start_time: Date.now() / 1000,
|
|
tx_bytes: 0,
|
|
rx_bytes: 0,
|
|
},
|
|
},
|
|
global: {
|
|
mocks: {
|
|
$t: (msg) => msg,
|
|
$router: { push: vi.fn() },
|
|
},
|
|
stubs: {
|
|
MaterialDesignIcon: true,
|
|
LxmfUserIcon: true,
|
|
AudioWaveformPlayer: true,
|
|
Transition: { template: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
toastWrapper?.unmount();
|
|
callWrapper?.unmount();
|
|
});
|
|
|
|
it("mobile toast bottom clears compose FAB band", () => {
|
|
const cls = toastWrapper.find("[class*='fixed']").classes().join(" ");
|
|
expect(cls).toContain("max-sm:bottom-[calc(5.5rem+env(safe-area-inset-bottom,0px))]");
|
|
});
|
|
|
|
it("call overlay uses safe-area bottom and responsive width", () => {
|
|
const root = callWrapper.find("[class*='fixed']");
|
|
expect(root.exists()).toBe(true);
|
|
const cls = root.classes().join(" ");
|
|
expect(cls).toContain("w-[min(20rem,calc(100%-1.5rem))]");
|
|
expect(cls).toContain("bottom-[max(1rem,env(safe-area-inset-bottom,0px))]");
|
|
expect(cls).toContain("max-sm:bottom-[calc(5.5rem+env(safe-area-inset-bottom,0px))]");
|
|
});
|
|
});
|
|
|
|
it("toast dismiss key still works after touch-target change", async () => {
|
|
const wrapper = mount(Toast, {
|
|
global: {
|
|
mocks: { $t: (msg) => msg },
|
|
stubs: {
|
|
TransitionGroup: { template: "<div><slot /></div>" },
|
|
MaterialDesignIcon: { template: '<div class="mdi-stub"></div>', props: ["iconName"] },
|
|
},
|
|
},
|
|
});
|
|
GlobalEmitter.emit("toast", { message: "X", duration: 0 });
|
|
await wrapper.vm.$nextTick();
|
|
await wrapper.find("button").trigger("click");
|
|
await wrapper.vm.$nextTick();
|
|
expect(wrapper.text()).not.toContain("X");
|
|
wrapper.unmount();
|
|
});
|
|
});
|