Added default layout to project
* Added default layout for the site * App is in light mode by default. * App is in light mode by default.
This commit is contained in:
parent
9a4dd4b721
commit
d1967f718e
36 changed files with 1660 additions and 39 deletions
11
tests/layouts/Default.test.ts
Normal file
11
tests/layouts/Default.test.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { mount, type VueWrapper } from "@vue/test-utils";
|
||||
import DefaultLayout from "~/layouts/Default.vue";
|
||||
|
||||
describe("Default.vue", () => {
|
||||
const wrapper: VueWrapper = mount(DefaultLayout, {});
|
||||
|
||||
it("loads without crashing", () => {
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
55
tests/layouts/default/Footer.test.ts
Normal file
55
tests/layouts/default/Footer.test.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { mount, type VueWrapper } from "@vue/test-utils";
|
||||
import Footer from "~/layouts/default/Footer.vue";
|
||||
|
||||
describe("Footer.vue", () => {
|
||||
const RealDate = Date;
|
||||
|
||||
const mockDate = (year: number) => {
|
||||
globalThis.Date = class extends RealDate {
|
||||
constructor() {
|
||||
super();
|
||||
return new RealDate(`${year}-01-01`);
|
||||
}
|
||||
|
||||
static now() {
|
||||
return new RealDate(`${year}-01-01`).getTime();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.Date = RealDate;
|
||||
});
|
||||
|
||||
it("loads without crashing", () => {
|
||||
const wrapper: VueWrapper = mount(Footer, {});
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
expect(wrapper.find(".layout-footer").exists()).toBe(true);
|
||||
expect(wrapper.find(".layout-footer").classes()).toContain("font-bold");
|
||||
});
|
||||
|
||||
it("displays only 'Glowing Fiesta 2025' when current year is 2025", () => {
|
||||
mockDate(2025);
|
||||
|
||||
const wrapper = mount(Footer);
|
||||
expect(wrapper.text()).toBe("Glowing Fiesta 2025");
|
||||
expect(wrapper.text()).not.toContain(" - ");
|
||||
});
|
||||
|
||||
it("displays 'Glowing Fiesta 2025 - 2034' when current year is 2034", () => {
|
||||
mockDate(2034);
|
||||
|
||||
const wrapper = mount(Footer);
|
||||
expect(wrapper.text()).toBe("Glowing Fiesta 2025 - 2034");
|
||||
});
|
||||
|
||||
it("has proper structure / content", () => {
|
||||
const wrapper = mount(Footer);
|
||||
const footer = wrapper.find("footer");
|
||||
|
||||
expect(footer.exists()).toBe(true);
|
||||
expect(footer.element.tagName).toBe("FOOTER");
|
||||
expect(wrapper.findAll("div")).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
11
tests/layouts/default/Sidebar.test.ts
Normal file
11
tests/layouts/default/Sidebar.test.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { mount, type VueWrapper } from "@vue/test-utils";
|
||||
import Sidebar from "~/layouts/default/Sidebar.vue";
|
||||
|
||||
describe("Sidebar.vue", () => {
|
||||
it("loads without crashing", () => {
|
||||
const wrapper: VueWrapper = mount(Sidebar, {});
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
expect(wrapper.find(".layout-sidebar").exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
155
tests/layouts/default/Topbar.test.ts
Normal file
155
tests/layouts/default/Topbar.test.ts
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { mount, type VueWrapper } from "@vue/test-utils";
|
||||
import Topbar from "~/layouts/default/Topbar.vue";
|
||||
|
||||
describe("Topbar.vue", () => {
|
||||
let wrapper: VueWrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
document.documentElement.className = "";
|
||||
document.body.innerHTML = '<div class="layout-wrapper"></div>';
|
||||
wrapper = mount(Topbar);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (wrapper) {
|
||||
wrapper.unmount();
|
||||
}
|
||||
});
|
||||
|
||||
describe("Component Rendering", () => {
|
||||
it("loads without crashing", () => {
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
expect(wrapper.find(".layout-topbar").exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("renders the logo image and text", () => {
|
||||
const logo = wrapper.find(".layout-topbar-logo");
|
||||
expect(logo.exists()).toBe(true);
|
||||
expect(logo.find("img").attributes("alt")).toBe("Two stick silhouettes admiring fireworks in the sky");
|
||||
expect(logo.text()).toContain("Glowing Fiesta");
|
||||
});
|
||||
|
||||
it("renders the menu toggle button", () => {
|
||||
const menuButton = wrapper.find(".layout-menu-button");
|
||||
expect(menuButton.exists()).toBe(true);
|
||||
expect(menuButton.find(".pi-bars").exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("renders the dark mode toggle button", () => {
|
||||
const darkModeButton = wrapper.find(".layout-topbar-action-highlight");
|
||||
expect(darkModeButton.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("renders the topbar menu items", () => {
|
||||
const menuButtons = wrapper.findAll(".layout-topbar-menu button");
|
||||
expect(menuButtons).toHaveLength(3);
|
||||
expect(menuButtons[0].text()).toContain("Calendar");
|
||||
expect(menuButtons[1].text()).toContain("Messages");
|
||||
expect(menuButtons[2].text()).toContain("Profile");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Dark Mode Toggle", () => {
|
||||
it("starts with sun icon (light mode)", () => {
|
||||
const darkModeButton = wrapper.find(".layout-topbar-action-highlight");
|
||||
expect(darkModeButton.find(".pi-sun").exists()).toBe(true);
|
||||
expect(darkModeButton.find(".pi-moon").exists()).toBe(false);
|
||||
});
|
||||
|
||||
it("toggles to moon icon when clicked", async () => {
|
||||
const darkModeButton = wrapper.find(".layout-topbar-action-highlight");
|
||||
await darkModeButton.trigger("click");
|
||||
|
||||
expect(darkModeButton.find(".pi-moon").exists()).toBe(true);
|
||||
expect(darkModeButton.find(".pi-sun").exists()).toBe(false);
|
||||
});
|
||||
|
||||
it("removes app-dark class when toggled off", async () => {
|
||||
expect(document.documentElement.classList.contains("app-dark")).toBe(false);
|
||||
const darkModeButton = wrapper.find(".layout-topbar-action-highlight");
|
||||
|
||||
// Toggle on
|
||||
await darkModeButton.trigger("click");
|
||||
expect(document.documentElement.classList.contains("app-dark")).toBe(true);
|
||||
|
||||
// Toggle off
|
||||
await darkModeButton.trigger("click");
|
||||
expect(document.documentElement.classList.contains("app-dark")).toBe(false);
|
||||
});
|
||||
|
||||
it("updates isDarkTheme ref when toggled", async () => {
|
||||
const darkModeButton = wrapper.find(".layout-topbar-action-highlight");
|
||||
|
||||
expect(wrapper.vm.isDarkTheme).toBe(false);
|
||||
|
||||
await darkModeButton.trigger("click");
|
||||
expect(wrapper.vm.isDarkTheme).toBe(true);
|
||||
|
||||
await darkModeButton.trigger("click");
|
||||
expect(wrapper.vm.isDarkTheme).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Menu Toggle", () => {
|
||||
it("toggles layout-static-inactive class on layout wrapper", async () => {
|
||||
const menuButton = wrapper.find(".layout-menu-button");
|
||||
const layoutWrapper = document.querySelector(".layout-wrapper");
|
||||
|
||||
expect(layoutWrapper?.classList.contains("layout-static-inactive")).toBe(false);
|
||||
|
||||
await menuButton.trigger("click");
|
||||
expect(layoutWrapper?.classList.contains("layout-static-inactive")).toBe(true);
|
||||
|
||||
await menuButton.trigger("click");
|
||||
expect(layoutWrapper?.classList.contains("layout-static-inactive")).toBe(false);
|
||||
});
|
||||
|
||||
it("handles missing layout wrapper gracefully", async () => {
|
||||
document.body.innerHTML = "";
|
||||
|
||||
const menuButton = wrapper.find(".layout-menu-button");
|
||||
|
||||
// Should not throw error
|
||||
expect(() => menuButton.trigger("click")).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Logo Link", () => {
|
||||
it("links to home page", () => {
|
||||
const logoLink = wrapper.find(".layout-topbar-logo");
|
||||
expect(logoLink.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("has correct image source", () => {
|
||||
const img = wrapper.find(".layout-topbar-logo img");
|
||||
expect(img.attributes("src")).toContain("logo.png");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Topbar Actions", () => {
|
||||
it("renders Calendar action button with icon", () => {
|
||||
const buttons = wrapper.findAll(".layout-topbar-menu button");
|
||||
const calendarButton = buttons[0];
|
||||
|
||||
expect(calendarButton.find(".pi-calendar").exists()).toBe(true);
|
||||
expect(calendarButton.text()).toContain("Calendar");
|
||||
});
|
||||
|
||||
it("renders Messages action button with icon", () => {
|
||||
const buttons = wrapper.findAll(".layout-topbar-menu button");
|
||||
const messagesButton = buttons[1];
|
||||
|
||||
expect(messagesButton.find(".pi-inbox").exists()).toBe(true);
|
||||
expect(messagesButton.text()).toContain("Messages");
|
||||
});
|
||||
|
||||
it("renders Profile action button with icon", () => {
|
||||
const buttons = wrapper.findAll(".layout-topbar-menu button");
|
||||
const profileButton = buttons[2];
|
||||
|
||||
expect(profileButton.find(".pi-user").exists()).toBe(true);
|
||||
expect(profileButton.text()).toContain("Profile");
|
||||
});
|
||||
});
|
||||
});
|
||||
11
tests/pages/config/Account.test.ts
Normal file
11
tests/pages/config/Account.test.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { mount, type VueWrapper } from "@vue/test-utils";
|
||||
import AccountPage from "~/pages/config/Account.vue";
|
||||
|
||||
describe("Account.vue", () => {
|
||||
const wrapper: VueWrapper = mount(AccountPage, {});
|
||||
|
||||
it("loads without crashing", () => {
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
11
tests/pages/config/Settings.test.ts
Normal file
11
tests/pages/config/Settings.test.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { mount, type VueWrapper } from "@vue/test-utils";
|
||||
import SettingsPage from "~/pages/config/Settings.vue";
|
||||
|
||||
describe("Settings.vue", () => {
|
||||
const wrapper: VueWrapper = mount(SettingsPage, {});
|
||||
|
||||
it("loads without crashing", () => {
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { mount, type VueWrapper } from "@vue/test-utils";
|
||||
import IndexPage from "~/app/pages/index.vue";
|
||||
import IndexPage from "~/pages/index.vue";
|
||||
|
||||
describe("pages/index.vue", () => {
|
||||
const wrapper: VueWrapper = mount(IndexPage, {});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { vi } from "vitest";
|
|||
import { config } from "@vue/test-utils";
|
||||
import PrimeVue from "primevue/config";
|
||||
import Button from "primevue/button";
|
||||
import Ripple from "primevue/ripple";
|
||||
|
||||
Object.defineProperty(global, "import", {
|
||||
value: {
|
||||
|
|
@ -13,5 +14,11 @@ Object.defineProperty(global, "import", {
|
|||
});
|
||||
|
||||
config.global.plugins = [PrimeVue];
|
||||
config.global.stubs = { NuxtPage: true };
|
||||
config.global.stubs = {
|
||||
NuxtLayout: true,
|
||||
NuxtPage: true,
|
||||
Divider: true,
|
||||
NuxtLink: { template: "<a><slot /></a>" },
|
||||
};
|
||||
config.global.components = { Button };
|
||||
config.global.directives = { Ripple };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue