diff --git a/__tests__/components/button.spec.tsx b/__tests__/components/button.spec.tsx new file mode 100644 index 0000000..257a8e3 --- /dev/null +++ b/__tests__/components/button.spec.tsx @@ -0,0 +1,58 @@ +import { render, screen } from "@testing-library/react" + +import { Button } from "@/components/Button/Button" + +describe("Button component", () => { + it("renders with default intent and size", () => { + render() + const btn = screen.getByRole("link", { name: /default button/i }) + expect(btn).toBeInTheDocument() + expect(btn).toHaveClass("bg-blue-400") + expect(btn).toHaveClass("text-lg") + }) + + it("renders secondary intent correctly", () => { + render() + const btn = screen.getByRole("link", { name: /secondary button/i }) + expect(btn).toHaveClass("bg-transparent") + expect(btn).toHaveClass("text-blue-400") + }) + + it("renders small size correctly", () => { + render() + const btn = screen.getByRole("link", { name: /small button/i }) + expect(btn).toHaveClass("text-sm") + expect(btn).toHaveClass("min-w-20") + }) + + it("applies underline when underline prop is true", () => { + render() + const btn = screen.getByRole("link", { name: /underlined button/i }) + expect(btn).toHaveClass("underline") + }) + + it("does not apply underline class when underline prop is false or omitted", () => { + render() + const btn = screen.getByRole("link", { name: /no underline/i }) + expect(btn).not.toHaveClass("underline") + }) + + it("merges custom className with variant classes", () => { + render() + const btn = screen.getByRole("link", { name: /custom class button/i }) + expect(btn).toHaveClass("custom-class") + expect(btn).toHaveClass("bg-blue-400") + }) + + it("forwards href and other props correctly", () => { + render() + const btn = screen.getByRole("link", { name: /link button/i }) + expect(btn).toHaveAttribute("href", "/test") + expect(btn).toHaveAttribute("rel", "noopener noreferrer") + }) + + it("renders children inside the anchor tag", () => { + render() + expect(screen.getByText("Click Me")).toBeInTheDocument() + }) +}) diff --git a/__tests__/components/tooltip.spec.tsx b/__tests__/components/tooltip.spec.tsx new file mode 100644 index 0000000..e74c9db --- /dev/null +++ b/__tests__/components/tooltip.spec.tsx @@ -0,0 +1,65 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react" + +import { Tooltip } from "@/components/Tooltip/Tooltip" + +describe("Tooltip", () => { + it("should render tooltip content when open is true", () => { + render( + +
Hover me
+
+ ) + + expect(screen.getByText("Hover me")).toBeInTheDocument() + + const tooltipContent = document.querySelector('[data-radix-popper-content-wrapper]') + expect(tooltipContent).toBeInTheDocument() + expect(tooltipContent).toHaveTextContent("Click me") + }) + + it("should not render tooltip content when open is false or undefined", () => { + render( + +
Hover me
+
+ ) + + expect(screen.getByText("Hover me")).toBeInTheDocument() + const tooltipContent = document.querySelector('[data-radix-popper-content-wrapper]') + expect(tooltipContent).not.toBeInTheDocument() + }) + + it("calls onOpenChange when tooltip open state changes", async () => { + const handleOpenChange = jest.fn() + + render( + + + + ) + + expect(screen.queryByText("Controlled")).not.toBeInTheDocument() + + fireEvent.mouseOver(screen.getByText("Hover me")) + + expect(screen.queryByText("Controlled")).not.toBeInTheDocument() + + handleOpenChange(true) + + expect(handleOpenChange).toHaveBeenCalledWith(true) + }) + + it("renders arrow when withArrow is true", () => { + const { container } = render( + + + + ) + + console.log(container.innerHTML) + + const arrow = document.querySelector('svg') + expect(arrow).toBeInTheDocument() + }) + +}) diff --git a/__tests__/pages/home.spec.tsx b/__tests__/pages/home.spec.tsx new file mode 100644 index 0000000..b215861 --- /dev/null +++ b/__tests__/pages/home.spec.tsx @@ -0,0 +1,64 @@ +import { render, screen } from "@testing-library/react" + +import Home, { metadata } from "@/app/page" +import { LP_GRID_ITEMS } from "lp-items" + +describe("Home", () => { + beforeEach(() => { + render() + }) + + it("renders the main heading", () => { + expect(screen.getByText("Next.js Enterprise Boilerplate")).toBeInTheDocument() + }) + + it("renders the description paragraph", () => { + expect(screen.getByText(/Jumpstart your enterprise project/)).toBeInTheDocument() + }) + + it("renders both buttons with correct text and links", () => { + const getStartedButton = screen.getByText("Get started") + const deployNowButton = screen.getByText("Deploy Now") + + expect(getStartedButton).toBeInTheDocument() + expect(deployNowButton).toBeInTheDocument() + expect(getStartedButton.closest('a')).toHaveAttribute('href', 'https://github.com/Blazity/next-enterprise') + expect(deployNowButton.closest('a')).toHaveAttribute('href', 'https://vercel.com/new/git/external?repository-url=https://github.com/Blazity/next-enterprise') + }) + + it("renders all grid items with their content", () => { + LP_GRID_ITEMS.forEach((item) => { + expect(screen.getByText(item.title)).toBeInTheDocument() + expect(screen.getByText(item.description)).toBeInTheDocument() + }) + }) + + it("renders grid items in the correct structure", () => { + const gridItems = screen.getAllByRole('heading', { level: 3 }) + expect(gridItems).toHaveLength(LP_GRID_ITEMS.length) + + gridItems.forEach((item) => { + const parent = item.closest('div') + expect(parent).toHaveClass('flex', 'flex-col', 'items-center', 'justify-center', 'text-center') + }) + }) + + it("has correct metadata configuration", () => { + expect(metadata).toEqual({ + title: "Next.js Enterprise Boilerplate", + twitter: { + card: "summary_large_image", + }, + openGraph: { + url: "https://next-enterprise.vercel.app/", + images: [ + { + width: 1200, + height: 630, + url: "https://raw.githubusercontent.com/Blazity/next-enterprise/main/.github/assets/project-logo.png", + }, + ], + }, + }) + }) +}) \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index a872374..0650f20 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,4 +1,4 @@ -import "styles/tailwind.css" +import "@/styles/tailwind.css" export default function RootLayout({ children }: { children: React.ReactNode }) { return ( diff --git a/jest.setup.js b/jest.setup.js index 401f198..9597f0e 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -1,2 +1,10 @@ // Learn more: https://github.com/testing-library/jest-dom import "@testing-library/jest-dom" + +class ResizeObserverMock { + observe() {} + unobserve() {} + disconnect() {} +} + +global.ResizeObserver = ResizeObserverMock; \ No newline at end of file diff --git a/package.json b/package.json index 12f60e5..59cd2ac 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "test-storybook": "cross-env FORCE_COLOR=1 test-storybook", "build-storybook": "cross-env FORCE_COLOR=1 storybook build", "test": "cross-env FORCE_COLOR=1 jest --passWithNoTests", + "test:watch": "cross-env FORCE_COLOR=1 jest --watch", + "test:coverage": "cross-env FORCE_COLOR=1 jest --coverage", "e2e:headless": "playwright test", "e2e:ui": "playwright test --ui", "format": "prettier --write \"**/*.{ts,tsx,md}\"", diff --git a/tsconfig.json b/tsconfig.json index 847a630..ffddf3a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,7 +23,10 @@ { "name": "next" } - ] + ], + "paths": { + "@/*": ["./*"] + } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.mjs", "jest.config.js", ".next/types/**/*.ts", "eslint.config.mjs"], "exclude": ["node_modules"]