Skip to content

Commit 7be63c4

Browse files
committed
chore: add tests for markdown functionality in PageHeader
1 parent 780d484 commit 7be63c4

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import React, { ReactNode } from 'react';
2+
import { render, screen, fireEvent, act } from '@testing-library/react';
3+
import '@testing-library/jest-dom/extend-expect';
4+
import { PageHeader } from './PageHeader';
5+
6+
const mockUseLayoutContext = jest.fn(() => ({
7+
activePage: {
8+
language: 'javascript',
9+
languages: ['javascript'],
10+
product: 'pubsub',
11+
page: {
12+
name: 'Test Page',
13+
link: '/docs/test-page',
14+
},
15+
tree: [],
16+
template: 'mdx' as const,
17+
},
18+
}));
19+
20+
// Mock the layout context
21+
jest.mock('src/contexts/layout-context', () => ({
22+
useLayoutContext: () => mockUseLayoutContext(),
23+
}));
24+
25+
// Mock useLocation from @reach/router
26+
jest.mock('@reach/router', () => ({
27+
useLocation: () => ({ pathname: '/docs/test-page' }),
28+
}));
29+
30+
// Mock Radix UI Tooltip to avoid act() warnings from async state updates
31+
jest.mock('@radix-ui/react-tooltip', () => ({
32+
Provider: ({ children }: { children: ReactNode }) => <div>{children}</div>,
33+
Root: ({ children }: { children: ReactNode }) => <div>{children}</div>,
34+
Trigger: ({ children, asChild }: { children: ReactNode; asChild?: boolean }) =>
35+
asChild ? children : <div>{children}</div>,
36+
Portal: ({ children }: { children: ReactNode }) => <div>{children}</div>,
37+
Content: ({ children }: { children: ReactNode }) => <div>{children}</div>,
38+
}));
39+
40+
// Mock Icon component
41+
jest.mock('@ably/ui/core/Icon', () => {
42+
return {
43+
__esModule: true,
44+
default: ({ name, size }: { name: string; size: string }) => (
45+
<span data-testid={`icon-${name}`} style={{ width: size, height: size }}>
46+
{name}
47+
</span>
48+
),
49+
};
50+
});
51+
52+
// Mock track function
53+
jest.mock('@ably/ui/core/insights', () => ({
54+
track: jest.fn(),
55+
}));
56+
57+
// Mock LanguageSelector to avoid its complexity
58+
jest.mock('../LanguageSelector', () => ({
59+
LanguageSelector: () => <div data-testid="language-selector">Language Selector</div>,
60+
}));
61+
62+
describe('PageHeader Markdown buttons', () => {
63+
beforeEach(() => {
64+
jest.clearAllMocks();
65+
});
66+
67+
afterEach(() => {
68+
jest.restoreAllMocks();
69+
jest.clearAllTimers();
70+
});
71+
72+
it('shows Markdown copy and view buttons when markdown content is available', async () => {
73+
const mockMarkdownContent = '# Mock markdown content\n\nThis is a test.';
74+
75+
// Mock successful fetch response
76+
global.fetch = jest.fn(() =>
77+
Promise.resolve({
78+
ok: true,
79+
headers: {
80+
get: (name: string) => (name === 'Content-Type' ? 'text/markdown' : null),
81+
},
82+
text: () => Promise.resolve(mockMarkdownContent),
83+
} as Response),
84+
);
85+
86+
// Mock clipboard API
87+
const mockWriteText = jest.fn();
88+
Object.assign(navigator, {
89+
clipboard: {
90+
writeText: mockWriteText,
91+
},
92+
});
93+
94+
// Use fake timers to control the setTimeout in resetCopyTooltip
95+
jest.useFakeTimers();
96+
97+
render(<PageHeader title="Test Page" intro="Test intro" />);
98+
99+
// Wait for the fetch to complete and state to update
100+
await screen.findByText('Markdown');
101+
102+
// Check that the Copy button is present
103+
const copyButton = screen.getByLabelText('Copy Markdown');
104+
expect(copyButton).toBeInTheDocument();
105+
106+
// Check that the View link is present
107+
const viewLink = screen.getByRole('link', { name: /icon-gui-eye-outline/i });
108+
expect(viewLink).toBeInTheDocument();
109+
expect(viewLink).toHaveAttribute('href', '/docs/test-page.md');
110+
111+
// Click the copy button and verify clipboard interaction
112+
act(() => {
113+
fireEvent.click(copyButton);
114+
});
115+
116+
expect(mockWriteText).toHaveBeenCalledWith(mockMarkdownContent);
117+
expect(mockWriteText).toHaveBeenCalledTimes(1);
118+
119+
// Clean up timers
120+
act(() => {
121+
jest.runOnlyPendingTimers();
122+
});
123+
jest.useRealTimers();
124+
});
125+
126+
it('does not show Markdown buttons when markdown content is null', async () => {
127+
// Suppress console.error for this test since we're testing a failure case
128+
const originalError = console.error;
129+
console.error = jest.fn();
130+
131+
// Mock failed fetch response
132+
global.fetch = jest.fn(() =>
133+
Promise.resolve({
134+
ok: false,
135+
status: 404,
136+
} as Response),
137+
);
138+
139+
render(<PageHeader title="Test Page" intro="Test intro" />);
140+
141+
// Wait a bit for any async updates
142+
await new Promise<void>((resolve) => setTimeout(resolve, 100));
143+
144+
// Check that the Markdown section is not present
145+
expect(screen.queryByText('Markdown')).not.toBeInTheDocument();
146+
expect(screen.queryByLabelText('Copy Markdown')).not.toBeInTheDocument();
147+
148+
// Restore console.error
149+
console.error = originalError;
150+
});
151+
});

0 commit comments

Comments
 (0)