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"]