diff --git a/__tests__/UI/Tabs.test.tsx b/__tests__/UI/Tabs.test.tsx
new file mode 100644
index 00000000..b1f75dee
--- /dev/null
+++ b/__tests__/UI/Tabs.test.tsx
@@ -0,0 +1,152 @@
+/**
+ * @jest-environment jsdom
+ */
+
+import React from "react";
+import { render, screen, fireEvent } from "@testing-library/react";
+import Tabs from "../../src/components/UI/Tabs.component";
+
+// Mock framer-motion to avoid issues with animations in tests
+jest.mock("framer-motion", () => ({
+ motion: {
+ div: "div",
+ button: "button",
+ },
+ AnimatePresence: ({ children }: { children: React.ReactNode }) => (
+ <>{children}>
+ ),
+}));
+
+const mockCVData = {
+ keyQualifications: ["Qualification 1", "Qualification 2"],
+ experience: [
+ {
+ period: "2020-2022",
+ company: "Example Company",
+ role: "Software Developer",
+ description: "Worked on various projects",
+ },
+ ],
+ education: [
+ {
+ period: "2016-2020",
+ institution: "University of Example",
+ degree: "Bachelor in Computer Science",
+ description: "Studied various aspects of computer science",
+ },
+ ],
+};
+
+const mockTabs = [
+ {
+ id: "qualifications",
+ label: "Nøkkelkvalifikasjoner",
+ content: (
+
+ {mockCVData.keyQualifications.map((qual) => (
+ -
+ {qual}
+
+ ))}
+
+ ),
+ expectedTexts: mockCVData.keyQualifications,
+ unexpectedTexts: ["Example Company", "University of Example"],
+ },
+ {
+ id: "experience",
+ label: "Erfaring",
+ content: (
+
+ {mockCVData.experience.map((exp) => (
+
+
+ {exp.period} - {exp.company}
+
+ {exp.role &&
{exp.role}
}
+
{exp.description}
+
+ ))}
+
+ ),
+ expectedTexts: [
+ "2020-2022 - Example Company",
+ "Software Developer",
+ "Worked on various projects",
+ ],
+ unexpectedTexts: ["Qualification 1"],
+ },
+ {
+ id: "education",
+ label: "Utdanning",
+ content: (
+
+ {mockCVData.education.map((edu) => (
+
+
+ {edu.period} - {edu.institution}
+
+ {edu.degree &&
{edu.degree}
}
+
{edu.description}
+
+ ))}
+
+ ),
+ expectedTexts: [
+ "2016-2020 - University of Example",
+ "Bachelor in Computer Science",
+ "Studied various aspects of computer science",
+ ],
+ unexpectedTexts: ["Qualification 1"],
+ },
+];
+
+describe("Tabs", () => {
+ const renderTabs = () => render();
+
+ const expectTextsToBePresent = (texts: string[]) => {
+ texts.forEach((text) => {
+ expect(screen.getByText(text)).toBeInTheDocument();
+ });
+ };
+
+ const expectTextsNotToBePresent = (texts: string[]) => {
+ texts.forEach((text) => {
+ expect(screen.queryByText(text)).not.toBeInTheDocument();
+ });
+ };
+
+ it("renders all CV tab labels", () => {
+ renderTabs();
+ mockTabs.forEach((tab) => {
+ expect(screen.getByRole("tab", { name: tab.label })).toBeInTheDocument();
+ });
+ });
+
+ it.each(mockTabs)("renders correct content for $label tab", (tab) => {
+ renderTabs();
+ if (tab.id !== mockTabs[0].id) {
+ fireEvent.click(screen.getByRole("tab", { name: tab.label }));
+ }
+ expectTextsToBePresent(tab.expectedTexts);
+ expectTextsNotToBePresent(tab.unexpectedTexts);
+ });
+
+ it("applies correct ARIA attributes to CV tabs", () => {
+ renderTabs();
+ mockTabs.forEach((tab, index) => {
+ const tabElement = screen.getByRole("tab", { name: tab.label });
+ expect(tabElement).toHaveAttribute(
+ "aria-selected",
+ index === 0 ? "true" : "false"
+ );
+ expect(tabElement).toHaveAttribute("aria-controls", `tabpanel-${tab.id}`);
+ });
+ });
+
+ it("renders in vertical orientation by default", () => {
+ renderTabs();
+ const tabList = screen.getByRole("tablist");
+ expect(tabList).toHaveClass("sm:flex-col");
+ });
+});
diff --git a/package.json b/package.json
index 926d7ab3..0d76c3cb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "dfweb-v4",
- "version": "1.0.2",
+ "version": "1.0.3",
"private": true,
"scripts": {
"dev": "next dev",