Skip to content

Commit da0f5e5

Browse files
test(AppTemplate): create app/template.test.tsx
1 parent 27dd627 commit da0f5e5

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)