Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor #380

Merged
merged 3 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 22 additions & 88 deletions __tests__/Index/IndexContent.test.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,27 @@
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import IndexContent from "../../src/components/Index/IndexContent.component";

// Mock the BounceInScroll component
jest.mock(
"../../src/components/Animations/BounceInScroll.component",
() =>
({ children }) => <div data-testid="bounce-in-scroll">{children}</div>,
);

// Mock the Button component
jest.mock(
"../../src/components/UI/Button.component",
() =>
({ onClick, children }) => <button onClick={onClick}>{children}</button>,
);

// Mock the PortableText component
jest.mock("@portabletext/react", () => ({
PortableText: ({ value }) => (
<div data-testid="portable-text">
{value.map((block) => (
<div key={block._key}>
{block.children.map((child) => (
<span key={child._key}>{child.text}</span>
// Mock the Section component
jest.mock("../../src/components/Index/Section.component", () => {
return function MockSection({ title, text }) {
return (
<div data-testid="mock-section">
<h2 data-testid="sanity-title">{title}</h2>
<div data-testid="portable-text">
{text.map((block) => (
<div key={block._key}>
{block.children.map((child) => (
<span key={child._key}>{child.text}</span>
))}
</div>
))}
</div>
))}
</div>
),
}));
</div>
);
};
});

const mockContent = [
{
Expand Down Expand Up @@ -67,19 +58,13 @@ const mockContent = [
];

describe("IndexContent Component", () => {
const originalNodeEnv = process.env.NODE_ENV;

beforeEach(() => {
process.env.NODE_ENV = "development";
});

afterEach(() => {
process.env.NODE_ENV = originalNodeEnv;
});

test("renders IndexContent with given content", () => {
render(<IndexContent pageContent={mockContent} />);

// Check if the sections are rendered
const sections = screen.getAllByTestId("mock-section");
expect(sections).toHaveLength(2);

// Check if the titles are rendered
const titles = screen.getAllByTestId("sanity-title");
expect(titles[0]).toHaveTextContent("Test Title 1");
Expand All @@ -91,60 +76,9 @@ describe("IndexContent Component", () => {
expect(portableTexts[1]).toHaveTextContent("Italic Text");
});

test("renders error trigger buttons in development environment", () => {
render(<IndexContent pageContent={mockContent} />);
const errorButtons = screen.getAllByText("Utløs Testfeil");
expect(errorButtons).toHaveLength(2);
});

test("does not render error trigger buttons in production environment", () => {
process.env.NODE_ENV = "production";
render(<IndexContent pageContent={mockContent} />);
const errorButtons = screen.queryAllByText("Utløs Testfeil");
expect(errorButtons).toHaveLength(0);
});

test("throws error when error button is clicked", () => {
render(<IndexContent pageContent={mockContent} />);
const errorButton = screen.getAllByText("Utløs Testfeil")[0];

expect(() => {
fireEvent.click(errorButton);
}).toThrow("En uventet feil har oppstått");
});

test("throws error when no content is provided", () => {
expect(() => {
render(<IndexContent pageContent={[]} />);
}).toThrow("Ingen innhold tilgjengelig");
});

test("renders BounceInScroll component", () => {
render(<IndexContent pageContent={mockContent} />);
const bounceInScrollComponents = screen.getAllByTestId("bounce-in-scroll");
expect(bounceInScrollComponents).toHaveLength(2);
});

test("handles invalid section data", () => {
const consoleErrorSpy = jest
.spyOn(console, "error")
.mockImplementation(() => {});

const invalidContent = [
{
id: "3",
title: "",
text: [],
},
];

render(<IndexContent pageContent={invalidContent} />);

expect(screen.queryByTestId("sanity-section")).not.toBeInTheDocument();
expect(consoleErrorSpy).toHaveBeenCalledWith(
expect.stringContaining("Ugyldig seksjon data"),
);

consoleErrorSpy.mockRestore();
});
});
81 changes: 1 addition & 80 deletions src/components/Index/IndexContent.component.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
"use client";

import Link from "next/link";
import { PortableText } from "@portabletext/react";
import type { PortableTextMarkComponentProps } from "@portabletext/react";
import BounceInScroll from "../Animations/BounceInScroll.component";
import Button from "../UI/Button.component";
import { useState } from "react";
import Section from "./Section.component";

interface IChild {
_key: string;
Expand All @@ -28,80 +23,6 @@ interface IContent {
title: string;
}

const myPortableTextComponents = {
marks: {
bold: ({ children }: PortableTextMarkComponentProps) => <b>{children}</b>,
italic: ({ children }: PortableTextMarkComponentProps) => <i>{children}</i>,
code: ({ children }: PortableTextMarkComponentProps) => (
<span className="mt-4 text-lg block">{children}</span>
),
link: ({ text, value }: PortableTextMarkComponentProps) => (
<Link
className="glitch underline text-lg font-bold text-green-400"
href={value?.href || "#"}
data-text={text}
>
{text}
</Link>
),
},
};

/**
* Section component that renders a single content section
* @param {IContent} props - The props for the Section component
* @param {string} props.text - The text content of the section
* @param {string} props.title - The title of the section
* @returns {JSX.Element | null} The rendered Section component or null if invalid data
*/
const Section = ({ text, title }: IContent) => {
const [shouldError, setShouldError] = useState(false);

if (!title || !text) {
console.error(
`Ugyldig seksjon data: tittel=${title}, tekst=${JSON.stringify(text)}`,
);
return null;
}

if (shouldError) {
throw new Error("En uventet feil har oppstått");
}

return (
<section
aria-label={title}
data-testid="sanity-section"
className="md:py-6"
>
<div className="p-6 text-lg rounded h-full">
<BounceInScroll viewAmount={0}>
<h2
data-testid="sanity-title"
data-cy={title}
className="text-3xl text-center text-slate-200"
>
{title}
</h2>
<div className="flex justify-center">
<div className="mt-4 text-lg text-left md:max-w-3xl">
<PortableText
value={text}
components={myPortableTextComponents}
/>
</div>
</div>
{process.env.NODE_ENV === "development" && (
<Button onClick={() => setShouldError(true)} type="button">
Utløs Testfeil
</Button>
)}
</BounceInScroll>
</div>
</section>
);
};

/**
* IndexContent component that renders multiple content sections
* @param {Object} props - The props for the IndexContent component
Expand Down
86 changes: 86 additions & 0 deletions src/components/Index/Section.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"use client";

import { PortableText } from "@portabletext/react";
import { useState } from "react";

import BounceInScroll from "../Animations/BounceInScroll.component";
import Button from "../UI/Button.component";

import { myPortableTextComponents } from "@/utils/portableTextComponents";

interface IChild {
_key: string;
_type: string;
marks: string[];
text: string;
}

interface IText {
_key: string;
_type: string;
children: IChild[];
markDefs: string[];
style: string;
}

interface IContent {
text: IText[];
title: string;
}

/**
* Section component that renders a single content section
* @param {IContent} props - The props for the Section component
* @param {string} props.text - The text content of the section
* @param {string} props.title - The title of the section
* @returns {JSX.Element | null} The rendered Section component or null if invalid data
*/
const Section = ({ text, title }: IContent) => {
const [shouldError, setShouldError] = useState(false);

if (!title || !text) {
console.error(
`Ugyldig seksjon data: tittel=${title}, tekst=${JSON.stringify(text)}`
);
return null;
}

if (shouldError) {
throw new Error("En uventet feil har oppstått");
}

return (
<section
aria-label={title}
data-testid="sanity-section"
className="md:py-6"
>
<div className="p-6 text-lg rounded h-full">
<BounceInScroll viewAmount={0}>
<h2
data-testid="sanity-title"
data-cy={title}
className="text-3xl text-center text-slate-200"
>
{title}
</h2>
<div className="flex justify-center">
<div className="mt-4 text-lg text-left md:max-w-3xl">
<PortableText
value={text}
components={myPortableTextComponents}
/>
</div>
</div>
{process.env.NODE_ENV === "development" && (
<Button onClick={() => setShouldError(true)} type="button">
Utløs Testfeil
</Button>
)}
</BounceInScroll>
</div>
</section>
);
};

export default Section;
21 changes: 21 additions & 0 deletions src/utils/portableTextComponents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Link from "next/link";
import type { PortableTextMarkComponentProps } from "@portabletext/react";

export const myPortableTextComponents = {
marks: {
bold: ({ children }: PortableTextMarkComponentProps) => <b>{children}</b>,
italic: ({ children }: PortableTextMarkComponentProps) => <i>{children}</i>,
code: ({ children }: PortableTextMarkComponentProps) => (
<span className="mt-4 text-lg block">{children}</span>
),
link: ({ text, value }: PortableTextMarkComponentProps) => (
<Link
className="glitch underline text-lg font-bold text-green-400"
href={value?.href || "#"}
data-text={text}
>
{text}
</Link>
),
},
};