Skip to content

Commit 5b030e4

Browse files
authored
test(AppTemplate): create app/template.test.tsx (JhaSourav07#3081)
## Description Creates `app/template.test.tsx` with 5 test cases covering the root Next.js `Template` component. Framer-motion is mocked using `vi.hoisted()` to avoid hoisting issues, allowing prop inspection without a real animation environment. **Tests:** 1. Renders children inside the motion wrapper 2. Verifies correct `initial` and `animate` props (`opacity`, `y`, `duration`, `ease`) 3. Asserts nested DOM structure is preserved completely (section, heading, paragraph, button) 4. Verifies component remounts when `key` changes, simulating Next.js page navigation 5. Verifies correct `exit` props (`opacity: 0, y: -8`) Fixes JhaSourav07#2289 ## Pillar - [ ] 🎨 Pillar 1 — New Theme Design - [ ] 📐 Pillar 2 — Geometric SVG Improvement - [ ] 🕐 Pillar 3 — Timezone Logic Optimization - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview N/A ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally (`localhost:3000/api/streak?user=YOUR_USERNAME`). - [x] I have run `npm run format` and `npm run lint` locally and resolved all errors (CI will fail otherwise). - [x] My commits follow the Conventional Commits format (e.g., `feat(themes): ...`, `fix(calculate): ...`). - [x] I have updated `README.md` if I added a new theme or URL parameter. - [x] I have started the repo. - [x] I have made sure that i have only one commit to merge in this PR. - [x] The SVG output matches the CommitPulse "premium quality" aesthetic standard (no raw elements, smooth animations, correct fonts). - [x] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support.
2 parents a120dcf + 137ca29 commit 5b030e4

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

app/template.test.tsx

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import React from 'react';
2+
import { cleanup, render, screen } from '@testing-library/react';
3+
import { afterEach, describe, expect, it, vi } from 'vitest';
4+
import Template from './template';
5+
6+
type MotionDivProps = React.HTMLAttributes<HTMLDivElement> & {
7+
children?: React.ReactNode;
8+
initial?: object;
9+
animate?: object;
10+
exit?: object;
11+
transition?: object;
12+
};
13+
14+
const mockMotionDiv = vi.hoisted(() =>
15+
vi.fn(
16+
({ children, initial: _i, animate: _a, exit: _e, transition: _t, ...rest }: MotionDivProps) => (
17+
<div data-testid="motion-wrapper" {...(rest as React.HTMLAttributes<HTMLDivElement>)}>
18+
{children}
19+
</div>
20+
)
21+
)
22+
);
23+
24+
vi.mock('framer-motion', () => ({
25+
motion: { div: mockMotionDiv },
26+
}));
27+
28+
afterEach(() => {
29+
cleanup();
30+
mockMotionDiv.mockClear();
31+
});
32+
33+
describe('Template', () => {
34+
it('renders children inside the motion wrapper', () => {
35+
render(
36+
<Template>
37+
<p data-testid="child">Hello CommitPulse</p>
38+
</Template>
39+
);
40+
41+
expect(screen.getByTestId('child').textContent).toBe('Hello CommitPulse');
42+
expect(screen.getByTestId('motion-wrapper')).toBeDefined();
43+
});
44+
45+
it('passes the correct initial and animate transition props to motion.div', () => {
46+
render(
47+
<Template>
48+
<span>test</span>
49+
</Template>
50+
);
51+
52+
const props = mockMotionDiv.mock.lastCall![0] as MotionDivProps;
53+
expect(props.initial).toEqual({ opacity: 0, y: 8 });
54+
expect(props.animate).toEqual({ opacity: 1, y: 0 });
55+
expect(props.transition).toEqual({ duration: 0.3, ease: 'easeInOut' });
56+
});
57+
58+
it('preserves nested DOM structure completely', () => {
59+
render(
60+
<Template>
61+
<section data-testid="section">
62+
<h1 data-testid="heading">Title</h1>
63+
<p data-testid="paragraph">Body text</p>
64+
<button data-testid="action">Click me</button>
65+
</section>
66+
</Template>
67+
);
68+
69+
const section = screen.getByTestId('section');
70+
expect(section.querySelector('[data-testid="heading"]')?.textContent).toBe('Title');
71+
expect(section.querySelector('[data-testid="paragraph"]')?.textContent).toBe('Body text');
72+
expect(section.querySelector('[data-testid="action"]')?.textContent).toBe('Click me');
73+
});
74+
75+
it('remounts and resets state when the key changes (page navigation)', () => {
76+
const { rerender } = render(
77+
<Template key="page-a">
78+
<span data-testid="page-content">Page A</span>
79+
</Template>
80+
);
81+
82+
const callsAfterFirstRender = mockMotionDiv.mock.calls.length;
83+
expect(callsAfterFirstRender).toBeGreaterThan(0);
84+
85+
rerender(
86+
<Template key="page-b">
87+
<span data-testid="page-content">Page B</span>
88+
</Template>
89+
);
90+
91+
expect(mockMotionDiv.mock.calls.length).toBeGreaterThan(callsAfterFirstRender);
92+
expect(screen.getByTestId('page-content').textContent).toBe('Page B');
93+
});
94+
95+
it('passes the correct exit transition props to motion.div', () => {
96+
render(
97+
<Template>
98+
<span>test</span>
99+
</Template>
100+
);
101+
102+
const props = mockMotionDiv.mock.lastCall![0] as MotionDivProps;
103+
expect(props.exit).toEqual({ opacity: 0, y: -8 });
104+
});
105+
});

0 commit comments

Comments
 (0)