Skip to content

Commit 55e3231

Browse files
committed
clean up unit tests
1 parent 92aaba3 commit 55e3231

File tree

2 files changed

+155
-611
lines changed

2 files changed

+155
-611
lines changed
Lines changed: 67 additions & 282 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
// Generated by Cursor
22
// AI-assisted implementation with human review and modifications
3-
import * as React from 'react';
4-
5-
import { render, screen } from '@testing-library/react';
3+
import { renderHook } from '@testing-library/react';
64
import useAccordionShowdownExtension from '../accordion-extension';
75
import { ACCORDION_MARKDOWN_BUTTON_ID, ACCORDION_MARKDOWN_CONTENT_ID } from '../const';
86
import { marked } from 'marked';
9-
// eslint-disable-next-line @typescript-eslint/no-require-imports
10-
const DOMPurify = require('dompurify');
117

128
// Mock marked
139
jest.mock('marked', () => ({
@@ -21,298 +17,87 @@ jest.mock('dompurify', () => ({
2117
sanitize: jest.fn((html) => html),
2218
}));
2319

24-
// Test component that uses the hook
25-
interface TestComponentProps {
26-
onExtensionReady?: (extension: any) => void;
27-
}
28-
29-
const TestAccordionComponent: React.FunctionComponent<TestComponentProps> = ({ onExtensionReady }) => {
30-
const extension = useAccordionShowdownExtension();
31-
32-
React.useEffect(() => {
33-
if (onExtensionReady) {
34-
onExtensionReady(extension);
35-
}
36-
}, [extension, onExtensionReady]);
37-
38-
return (
39-
<div data-testid="test-component">
40-
<div data-testid="extension-type">{extension.type}</div>
41-
<div data-testid="extension-regex-source">{extension.regex.source}</div>
42-
<div data-testid="extension-regex-flags">{extension.regex.flags}</div>
43-
<button
44-
data-testid="test-replace-button"
45-
onClick={() => {
46-
const result = extension.replace(
47-
'[Test content]{{accordion &quot;Test Title&quot;}}',
48-
'Test content',
49-
'accordion',
50-
'&quot;Test Title&quot;',
51-
'Test Title'
52-
);
53-
const resultDiv = document.createElement('div');
54-
resultDiv.innerHTML = result;
55-
resultDiv.setAttribute('data-testid', 'replace-result');
56-
document.body.appendChild(resultDiv);
57-
}}
58-
>
59-
Test Replace
60-
</button>
61-
</div>
62-
);
63-
};
64-
6520
describe('useAccordionShowdownExtension', () => {
6621
beforeEach(() => {
6722
jest.clearAllMocks();
68-
// Clean up any previous test results
69-
const existingResults = document.querySelectorAll('[data-testid="replace-result"]');
70-
existingResults.forEach(el => el.remove());
7123
});
7224

73-
it('should return extension with correct type', () => {
74-
render(<TestAccordionComponent />);
75-
expect(screen.getByTestId('extension-type').textContent).toBe('lang');
76-
});
25+
it('should return a showdown extension with correct properties', () => {
26+
const { result } = renderHook(() => useAccordionShowdownExtension());
27+
const extension = result.current;
7728

78-
it('should return extension with global regex', () => {
79-
render(<TestAccordionComponent />);
80-
expect(screen.getByTestId('extension-regex-flags').textContent).toBe('g');
29+
expect(extension.type).toBe('lang');
30+
expect(extension.regex).toEqual(/\[(.+)]{{(accordion) (&quot;(.*?)&quot;)}}/g);
31+
expect(typeof extension.replace).toBe('function');
8132
});
8233

83-
it('should return extension with correct regex pattern for HTML-encoded quotes', () => {
84-
render(<TestAccordionComponent />);
85-
const regexSource = screen.getByTestId('extension-regex-source').textContent;
86-
expect(regexSource).toBe('\\[(.+)]{{(accordion) (&quot;(.*?)&quot;)}}');
34+
it('should match accordion syntax with HTML-encoded quotes', () => {
35+
const { result } = renderHook(() => useAccordionShowdownExtension());
36+
const { regex } = result.current;
37+
38+
const testText = '[Some content]{{accordion &quot;My Title&quot;}}';
39+
const matches = regex.exec(testText);
40+
41+
expect(matches).not.toBeNull();
42+
expect(matches![1]).toBe('Some content');
43+
expect(matches![2]).toBe('accordion');
44+
expect(matches![4]).toBe('My Title');
8745
});
8846

89-
describe('regex pattern matching', () => {
90-
let extension: any;
91-
92-
beforeEach(() => {
93-
const handleExtensionReady = (ext: any) => {
94-
extension = ext;
95-
};
96-
render(<TestAccordionComponent onExtensionReady={handleExtensionReady} />);
97-
});
98-
99-
it('should match accordion syntax with HTML-encoded quotes', () => {
100-
const testText = '[Some content here]{{accordion &quot;My Title&quot;}}';
101-
const regex = new RegExp(extension.regex);
102-
const matches = regex.exec(testText);
103-
104-
expect(matches).not.toBeNull();
105-
expect(matches![1]).toBe('Some content here');
106-
expect(matches![2]).toBe('accordion');
107-
expect(matches![3]).toBe('&quot;My Title&quot;');
108-
expect(matches![4]).toBe('My Title');
109-
});
110-
111-
it('should match multiple accordions in the same text', () => {
112-
const testText = `
113-
[First content]{{accordion &quot;First Title&quot;}}
114-
Some other text
115-
[Second content]{{accordion &quot;Second Title&quot;}}
116-
`;
117-
118-
const matches = Array.from(testText.matchAll(extension.regex));
119-
expect(matches).toHaveLength(2);
120-
expect(matches[0][4]).toBe('First Title');
121-
expect(matches[1][4]).toBe('Second Title');
122-
});
123-
124-
it('should not match accordion syntax with regular quotes', () => {
125-
const testText = '[Some content]{{accordion "My Title"}}';
126-
const matches = testText.match(extension.regex);
127-
expect(matches).toBeNull();
128-
});
129-
130-
it('should not match malformed accordion syntax', () => {
131-
const malformedCases = [
132-
'Some content]{{accordion &quot;My Title&quot;}}',
133-
'[Some content{{accordion &quot;My Title&quot;}}',
134-
'[Some content]{{accordion &quot;My Title&quot;}',
135-
'[Some content]{{accordion My Title}}',
136-
'[Some content]{{notaccordion &quot;My Title&quot;}}',
137-
];
138-
139-
malformedCases.forEach(testCase => {
140-
const matches = testCase.match(extension.regex);
141-
expect(matches).toBeNull();
142-
});
143-
});
47+
it('should not match accordion syntax with regular quotes', () => {
48+
const { result } = renderHook(() => useAccordionShowdownExtension());
49+
const { regex } = result.current;
50+
51+
const testText = '[Some content]{{accordion "My Title"}}';
52+
expect(testText.match(regex)).toBeNull();
14453
});
14554

146-
describe('HTML generation', () => {
147-
it('should generate correct accordion HTML structure when replace function is called', () => {
148-
render(<TestAccordionComponent />);
149-
150-
const testButton = screen.getByTestId('test-replace-button');
151-
testButton.click();
152-
153-
const result = document.querySelector('[data-testid="replace-result"]');
154-
expect(result).not.toBeNull();
155-
156-
// Check for PatternFly accordion classes in HTML
157-
expect(result!.innerHTML).toContain('pf-v6-c-accordion');
158-
expect(result!.innerHTML).toContain('pf-v6-c-accordion__item');
159-
expect(result!.innerHTML).toContain('pf-v6-c-accordion__toggle');
160-
expect(result!.innerHTML).toContain('pf-v6-c-accordion__expandable-content');
161-
162-
// Check for correct IDs
163-
expect(result!.innerHTML).toContain(`${ACCORDION_MARKDOWN_BUTTON_ID}-Test-Title`);
164-
expect(result!.innerHTML).toContain(`${ACCORDION_MARKDOWN_CONTENT_ID}-Test-Title`);
165-
166-
// Check that title is rendered
167-
expect(result!.textContent).toContain('Test Title');
168-
});
169-
170-
it('should call marked.parseInline and DOMPurify.sanitize during rendering', () => {
171-
let extension: any;
172-
const handleExtensionReady = (ext: any) => {
173-
extension = ext;
174-
};
175-
176-
render(<TestAccordionComponent onExtensionReady={handleExtensionReady} />);
177-
178-
// Call replace function directly
179-
extension.replace(
180-
'[**Bold text**]{{accordion &quot;Title&quot;}}',
181-
'**Bold text**',
182-
'accordion',
183-
'&quot;Title&quot;',
184-
'Title'
185-
);
186-
187-
expect(marked.parseInline).toHaveBeenCalledWith('**Bold text**');
188-
expect(DOMPurify.sanitize).toHaveBeenCalled();
189-
});
190-
191-
it('should handle titles with spaces by replacing them with dashes in IDs', () => {
192-
let extension: any;
193-
const handleExtensionReady = (ext: any) => {
194-
extension = ext;
195-
};
196-
197-
render(<TestAccordionComponent onExtensionReady={handleExtensionReady} />);
198-
199-
const result = extension.replace(
200-
'[Content]{{accordion &quot;My Test Title&quot;}}',
201-
'Content',
202-
'accordion',
203-
'&quot;My Test Title&quot;',
204-
'My Test Title'
205-
);
206-
207-
expect(result).toContain(`id="${ACCORDION_MARKDOWN_BUTTON_ID}-My-Test-Title"`);
208-
expect(result).toContain(`id="${ACCORDION_MARKDOWN_CONTENT_ID}-My-Test-Title"`);
209-
});
210-
211-
it('should handle special characters in titles', () => {
212-
let extension: any;
213-
const handleExtensionReady = (ext: any) => {
214-
extension = ext;
215-
};
216-
217-
render(<TestAccordionComponent onExtensionReady={handleExtensionReady} />);
218-
219-
const result = extension.replace(
220-
'[Content]{{accordion &quot;Title with 123 & symbols!&quot;}}',
221-
'Content',
222-
'accordion',
223-
'&quot;Title with 123 & symbols!&quot;',
224-
'Title with 123 & symbols!'
225-
);
226-
227-
expect(result).toContain(`id="${ACCORDION_MARKDOWN_BUTTON_ID}-Title-with-123-&amp;-symbols!"`);
228-
expect(result).toContain('Title with 123 &amp; symbols!');
229-
});
55+
it('should generate correct accordion HTML structure', () => {
56+
const { result } = renderHook(() => useAccordionShowdownExtension());
57+
const { replace } = result.current;
58+
59+
const html = replace(
60+
'[Test content]{{accordion &quot;Test Title&quot;}}',
61+
'Test content',
62+
'accordion',
63+
'&quot;Test Title&quot;',
64+
'Test Title'
65+
);
66+
67+
expect(html).toContain('pf-v6-c-accordion');
68+
expect(html).toContain('pf-v6-c-accordion__toggle');
69+
expect(html).toContain(`${ACCORDION_MARKDOWN_BUTTON_ID}-Test-Title`);
70+
expect(html).toContain(`${ACCORDION_MARKDOWN_CONTENT_ID}-Test-Title`);
71+
expect(html).toContain('Test Title');
23072
});
23173

232-
describe('edge cases', () => {
233-
it('should handle empty content', () => {
234-
let extension: any;
235-
const handleExtensionReady = (ext: any) => {
236-
extension = ext;
237-
};
238-
239-
render(<TestAccordionComponent onExtensionReady={handleExtensionReady} />);
240-
241-
const result = extension.replace(
242-
'[]{{accordion &quot;Empty&quot;}}',
243-
'',
244-
'accordion',
245-
'&quot;Empty&quot;',
246-
'Empty'
247-
);
248-
249-
expect(result).toContain('class="pf-v6-c-accordion"');
250-
expect(result).toContain('Empty');
251-
});
252-
253-
it('should handle content with HTML entities', () => {
254-
let extension: any;
255-
const handleExtensionReady = (ext: any) => {
256-
extension = ext;
257-
};
258-
259-
render(<TestAccordionComponent onExtensionReady={handleExtensionReady} />);
260-
261-
const result = extension.replace(
262-
'[Content with &lt;tags&gt; &amp; entities]{{accordion &quot;Title&quot;}}',
263-
'Content with &lt;tags&gt; &amp; entities',
264-
'accordion',
265-
'&quot;Title&quot;',
266-
'Title'
267-
);
268-
269-
expect(result).toContain('Content with &lt;tags&gt; &amp; entities');
270-
});
271-
272-
it('should handle very long titles', () => {
273-
let extension: any;
274-
const handleExtensionReady = (ext: any) => {
275-
extension = ext;
276-
};
277-
278-
render(<TestAccordionComponent onExtensionReady={handleExtensionReady} />);
279-
280-
const longTitle = 'This is a very long title that might cause issues with ID generation and display';
281-
const result = extension.replace(
282-
'[Content]{{accordion &quot;' + longTitle + '&quot;}}',
283-
'Content',
284-
'accordion',
285-
'&quot;' + longTitle + '&quot;',
286-
longTitle
287-
);
288-
289-
expect(result).toContain(longTitle);
290-
expect(result).toContain(`id="${ACCORDION_MARKDOWN_BUTTON_ID}-${longTitle.replace(/\s/g, '-')}"`);
291-
});
74+
it('should process content through marked and sanitize HTML', () => {
75+
const { result } = renderHook(() => useAccordionShowdownExtension());
76+
const { replace } = result.current;
77+
78+
replace(
79+
'[**Bold text**]{{accordion &quot;Title&quot;}}',
80+
'**Bold text**',
81+
'accordion',
82+
'&quot;Title&quot;',
83+
'Title'
84+
);
85+
86+
expect(marked.parseInline).toHaveBeenCalledWith('**Bold text**');
29287
});
29388

294-
describe('memoization', () => {
295-
it('should return the same extension object on subsequent renders', () => {
296-
let callCount = 0;
297-
let firstExtension: any;
298-
let secondExtension: any;
299-
300-
const handleExtensionReady = (ext: any) => {
301-
callCount++;
302-
if (callCount === 1) {
303-
firstExtension = ext;
304-
} else if (callCount === 2) {
305-
secondExtension = ext;
306-
}
307-
};
308-
309-
const { rerender } = render(<TestAccordionComponent onExtensionReady={handleExtensionReady} />);
310-
rerender(<TestAccordionComponent onExtensionReady={handleExtensionReady} />);
311-
312-
expect(callCount).toBeGreaterThanOrEqual(1);
313-
expect(firstExtension).toBeDefined();
314-
expect(firstExtension.type).toBe('lang');
315-
expect(typeof firstExtension.replace).toBe('function');
316-
});
89+
it('should handle titles with spaces in IDs', () => {
90+
const { result } = renderHook(() => useAccordionShowdownExtension());
91+
const { replace } = result.current;
92+
93+
const html = replace(
94+
'[Content]{{accordion &quot;My Test Title&quot;}}',
95+
'Content',
96+
'accordion',
97+
'&quot;My Test Title&quot;',
98+
'My Test Title'
99+
);
100+
101+
expect(html).toContain(`${ACCORDION_MARKDOWN_BUTTON_ID}-My-Test-Title`);
317102
});
318103
});

0 commit comments

Comments
 (0)