diff --git a/package.json b/package.json index c05c3b4..7cbb64d 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@vue/tsconfig": "^0.7.0", "jsdom": "^26.1.0", "npm-run-all2": "^8.0.4", + "resize-observer-polyfill": "^1.5.1", "sass-embedded": "^1.92.1", "typescript": "~5.8.0", "vite": "^7.0.6", diff --git a/src/App.vue b/src/App.vue index 789d792..9feede6 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,35 +1,11 @@ - - - - - - Home - - - - - About - - - - - + diff --git a/src/components/HeaderNavBar.vue b/src/components/HeaderNavBar.vue new file mode 100644 index 0000000..e6e0ec8 --- /dev/null +++ b/src/components/HeaderNavBar.vue @@ -0,0 +1,32 @@ + + + + + + + + + Home + + + + + About + + + + + + \ No newline at end of file diff --git a/tests/components/HeardeNavBar.spec.ts b/tests/components/HeardeNavBar.spec.ts new file mode 100644 index 0000000..4b57f0a --- /dev/null +++ b/tests/components/HeardeNavBar.spec.ts @@ -0,0 +1,138 @@ +import { describe, it, expect, beforeEach } from "vitest" +import { mount } from "@vue/test-utils" +import { createRouter, createWebHistory } from "vue-router" +import { createVuetify } from "vuetify" +import * as components from 'vuetify/components' +import * as directives from 'vuetify/directives' +import HeaderNavBar from "../../src/components/HeaderNavBar.vue" + +global.ResizeObserver = require('resize-observer-polyfill'); + +// Mock routes for testing +const routes = [ + { path: "/", component: { template: "Home" } }, + { path: "/about", component: { template: "About" } } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}); + +const vuetify = createVuetify({ + components, + directives, +}) + +describe("HeaderNavBar", () => { + let wrapper: any + + beforeEach(() => { + wrapper = mount({ + template: ` + + + + + + `, + }, { + global: { + components: { + HeaderNavBar, + }, + plugins: [router, vuetify] + } + }) + }) + + it("renders the component", () => { + expect(wrapper.exists()).toBe(true) + expect(wrapper.findComponent({ name: "VAppBar" }).exists()).toBe(true) + }) + + it("renders both navigation buttons", () => { + const homeButton = wrapper.find("[data-role='homeNavigation']"); + const aboutButton = wrapper.find("[data-role='aboutNavigation']"); + + expect(homeButton.exists()).toBe(true); + expect(aboutButton.exists()).toBe(true); + expect(homeButton.text()).toContain("Home"); + expect(aboutButton.text()).toContain("About"); + }); + + it("has correct icons on buttons", () => { + const homeButton = wrapper.find("[data-role='homeNavigation']"); + const aboutButton = wrapper.find("[data-role='aboutNavigation']"); + + expect(homeButton.find("i").attributes("class")).toContain("mdi-home-outline"); + expect(aboutButton.find("i").attributes("class")).toContain("mdi-chat-question-outline"); + }); + + it("has correct RouterLink paths", () => { + const routerLinks = wrapper.findAllComponents({ name: "RouterLink" }); + + expect(routerLinks).toHaveLength(2); + expect(routerLinks[0].props("to")).toBe("/"); + expect(routerLinks[1].props("to")).toBe("/about"); + }); + + it("applies correct button variants based on active route", async () => { + // Navigate to home route + await router.push("/") + await wrapper.vm.$nextTick() + + // Check home button is elevated (active) and about is outlined (inactive) + const homeButton = wrapper.find("[data-role='homeNavigation']"); + const aboutButton = wrapper.find("[data-role='aboutNavigation']"); + + expect(homeButton.find("button").attributes("class")).toContain("elevated"); + expect(aboutButton.find("button").attributes("class")).toContain("outlined"); + + // // Navigate to about route + await router.push("/about"); + await wrapper.vm.$nextTick(); + + // // Check about button is now elevated and home is outlined + expect(homeButton.find("button").attributes("class")).toContain("outlined"); + expect(aboutButton.find("button").attributes("class")).toContain("elevated"); + }); + + it("has correct button styling classes", () => { + const routerLinks = wrapper.findAllComponents({ name: "RouterLink" }); + + routerLinks.forEach((link: any) => { + expect(link.classes()).toContain("mx-2") + expect(link.classes()).toContain("text-decoration-none") + }); + + const buttons = wrapper.findAllComponents({ name: "v-btn" }); + buttons.forEach((button: any) => { + expect(button.attributes("class")).toContain("v-btn--size-large"); + expect(button.attributes("class")).toContain("-primary"); + }); + }); + + it("has proper app bar structure", () => { + const appBar = wrapper.findComponent({ name: "VAppBar" }); + const container = wrapper.findComponent({ name: "VContainer" }); + + expect(appBar.attributes("fixed")).toBe(""); + expect(appBar.attributes("app")).toBe(""); + expect(container.classes()).toContain("d-flex"); + expect(container.classes()).toContain("align-center"); + expect(container.classes()).toContain("pa-0"); + }); + + it("maintains accessibility attributes", () => { + const homeButton = wrapper.find("[data-role='homeNavigation']"); + expect(homeButton.attributes("data-role")).toBe('homeNavigation'); + + // Check that buttons are actual button elements for screen readers + const buttons = wrapper.findAll("button") + expect(buttons).toHaveLength(2) + buttons.forEach((button: any) => { + expect(button.element.tagName).toBe("BUTTON"); + }) + }) +})