Skip to content

Commit 3226d1a

Browse files
committed
chore: run it back
1 parent b02d0bc commit 3226d1a

File tree

2 files changed

+336
-360
lines changed

2 files changed

+336
-360
lines changed

__tests__/codeMirror.test.js

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
/* eslint-disable testing-library/no-node-access */
2+
/* eslint-disable testing-library/no-container */
3+
import { promises as fs } from 'fs';
4+
import path from 'path';
5+
6+
// eslint-disable-next-line testing-library/no-manual-cleanup
7+
import { render, screen, cleanup } from '@testing-library/react';
8+
import { globSync } from 'glob';
9+
10+
import syntaxHighlighter, { uppercase, canonical } from '../src';
11+
12+
const fixtures = globSync(path.join(__dirname, '/__fixtures__/*'));
13+
14+
test('should highlight a block of code', () => {
15+
render(syntaxHighlighter('var a = 1;', 'javascript'));
16+
17+
expect(screen.getByTestId('SyntaxHighlighter').outerHTML).toBe(
18+
'<div class="cm-s-neo" data-testid="SyntaxHighlighter"><span class="cm-keyword">var</span> <span class="cm-def">a</span> <span class="cm-operator">=</span> <span class="cm-number">1</span>;</div>',
19+
);
20+
});
21+
22+
test('should work when passed a non-string value', () => {
23+
expect(() => syntaxHighlighter(false, 'text')).not.toThrow();
24+
});
25+
26+
test('should sanitize plain text language', () => {
27+
render(syntaxHighlighter('& < > " \' /', 'text'));
28+
expect(screen.getByText('& < > " \' /')).toBeVisible();
29+
});
30+
31+
test('should sanitize mode', () => {
32+
render(syntaxHighlighter('&', 'json'));
33+
expect(screen.getByText('&')).toBeVisible();
34+
35+
render(syntaxHighlighter('<', 'json'));
36+
expect(screen.getByText('<')).toBeVisible();
37+
});
38+
39+
test('should concat the same style items', () => {
40+
// This is testing the `accum += text;` line
41+
render(syntaxHighlighter('====', 'javascript'));
42+
expect(screen.getByText('====')).toBeVisible();
43+
});
44+
45+
test('should work with modes', () => {
46+
render(syntaxHighlighter('{ "a": 1 }', 'json'));
47+
48+
expect(screen.getByTestId('SyntaxHighlighter').outerHTML).toBe(
49+
'<div class="cm-s-neo" data-testid="SyntaxHighlighter">{ <span class="cm-property">"a"</span>: <span class="cm-number">1</span> }</div>',
50+
);
51+
});
52+
53+
test('should keep trailing json bracket if highlightMode is enabled', () => {
54+
render(syntaxHighlighter('{ "a": 1 }', 'json', { highlightMode: true }));
55+
56+
expect(screen.getByTestId('CodeMirror').outerHTML).toBe(
57+
'<div class="CodeMirror" data-testid="CodeMirror"><div class="cm-linerow "><span class="cm-lineNumber">1</span>{ <span class="cm-property">"a"</span>: <span class="cm-number">1</span> }</div></div>',
58+
);
59+
});
60+
61+
test('should have a dark theme', () => {
62+
render(syntaxHighlighter('{ "a": 1 }', 'json', { dark: true }));
63+
expect(screen.getByTestId('SyntaxHighlighter')).toHaveClass('cm-s-material-palenight');
64+
});
65+
66+
describe('variable substitution', () => {
67+
it('should tokenize variables (double quotes)', () => {
68+
render(syntaxHighlighter('"<<apiKey>>"', 'json', { tokenizeVariables: true }));
69+
expect(screen.getByText('APIKEY')).toBeVisible();
70+
});
71+
72+
it('should tokenize variables (single quotes)', () => {
73+
render(syntaxHighlighter("'<<apiKey>>'", 'json', { tokenizeVariables: true }));
74+
expect(screen.getByText('APIKEY')).toBeVisible();
75+
});
76+
77+
it('should keep enclosing characters around the variable', () => {
78+
render(syntaxHighlighter("'<<apiKey>>'", 'json', { tokenizeVariables: true }));
79+
expect(screen.getByTestId('SyntaxHighlighter')).toHaveTextContent("'APIKEY'");
80+
});
81+
82+
it('should tokenize variables outside of quotes', () => {
83+
render(syntaxHighlighter('<<apiKey>>', 'json', { tokenizeVariables: true }));
84+
expect(screen.getByText('APIKEY')).toBeVisible();
85+
});
86+
87+
it('should tokenize variables outside of quotes over multiple lines', () => {
88+
const codeBlock = `
89+
const foo = <<apiKey>>;
90+
const bar = <<name>>;
91+
92+
fetch({ foo, bar, baz: <<token>> });
93+
`;
94+
95+
render(syntaxHighlighter(codeBlock, 'json', { tokenizeVariables: true }));
96+
expect(screen.getByTestId('SyntaxHighlighter').textContent).toMatchSnapshot();
97+
});
98+
99+
it('should tokenize multiple variables per line', () => {
100+
render(syntaxHighlighter('<<apiKey>> <<name>>', 'json', { tokenizeVariables: true }));
101+
expect(screen.getByTestId('SyntaxHighlighter')).toHaveTextContent('APIKEY NAME');
102+
});
103+
104+
it.each(['\\<<wat>>', '<<wat\\>>', '\\<<wat\\>>'])('should NOT tokenize escaped variables %s', code => {
105+
render(syntaxHighlighter(code, 'json', { tokenizeVariables: true }));
106+
expect(screen.getByTestId('SyntaxHighlighter')).toHaveTextContent('<<wat>>');
107+
});
108+
});
109+
110+
describe('variable substitution { mdx: true }', () => {
111+
it('should tokenize variables (double quotes)', () => {
112+
render(syntaxHighlighter('"{user.apiKey}"', 'json', { tokenizeVariables: true }, { mdx: true }));
113+
expect(screen.getByText('APIKEY')).toBeVisible();
114+
});
115+
116+
it('should tokenize variables (single quotes)', () => {
117+
render(syntaxHighlighter("'{user.apiKey}'", 'json', { tokenizeVariables: true }, { mdx: true }));
118+
expect(screen.getByText('APIKEY')).toBeVisible();
119+
});
120+
121+
it('should keep enclosing characters around the variable', () => {
122+
render(syntaxHighlighter("'{user.apiKey}'", 'json', { tokenizeVariables: true }, { mdx: true }));
123+
expect(screen.getByTestId('SyntaxHighlighter')).toHaveTextContent("'APIKEY'");
124+
});
125+
126+
it('should tokenize variables outside of quotes', () => {
127+
render(syntaxHighlighter('{user.apiKey}', 'json', { tokenizeVariables: true }, { mdx: true }));
128+
expect(screen.getByText('APIKEY')).toBeVisible();
129+
});
130+
131+
it('should tokenize variables outside of quotes over multiple lines', () => {
132+
const codeBlock = `
133+
const foo = {user.apiKey};
134+
const bar = {user.name};
135+
136+
fetch({ foo, bar, baz: {user.token} });
137+
`;
138+
139+
render(syntaxHighlighter(codeBlock, 'json', { tokenizeVariables: true }, { mdx: true }));
140+
expect(screen.getByTestId('SyntaxHighlighter').textContent).toMatchInlineSnapshot(`
141+
"
142+
const foo = APIKEY;
143+
const bar = NAME;
144+
145+
fetch({ foo, bar, baz: TOKEN });
146+
"
147+
`);
148+
});
149+
150+
it('should tokenize multiple variables per line', () => {
151+
render(syntaxHighlighter('{user.apiKey} {user.name}', 'json', { tokenizeVariables: true }, { mdx: true }));
152+
expect(screen.getByTestId('SyntaxHighlighter')).toHaveTextContent('APIKEY NAME');
153+
});
154+
155+
it('should not tokenize bracket style', () => {
156+
render(syntaxHighlighter('<<wat>>', 'json', { tokenizeVariables: true }, { mdx: true }));
157+
expect(screen.getByTestId('SyntaxHighlighter')).toHaveTextContent('<<wat>>');
158+
});
159+
160+
it.each(['\\{user.wat}', '{user.wat\\}', '\\{user.wat\\}'])('should NOT tokenize escaped variables %s', code => {
161+
render(syntaxHighlighter(code, 'json', { tokenizeVariables: true }, { mdx: true }));
162+
expect(screen.getByTestId('SyntaxHighlighter')).toHaveTextContent('{user.wat}');
163+
});
164+
});
165+
166+
describe('Supported languages', () => {
167+
const languages = fixtures.map(fixture => {
168+
return [uppercase(path.basename(fixture)), fixture];
169+
});
170+
171+
describe.each(languages)('%s', (language, fixtureDir) => {
172+
let testCase;
173+
174+
// eslint-disable-next-line global-require, import/no-dynamic-require
175+
const instructions = require(path.join(fixtureDir, 'index.js'));
176+
177+
beforeEach(async () => {
178+
testCase = await fs.readFile(path.join(fixtureDir, `sample.${instructions.mode.primary}`), 'utf8');
179+
});
180+
181+
it('should have a properly formatted instruction set', () => {
182+
expect(instructions).toBeDefined();
183+
expect(instructions).toStrictEqual({
184+
language: expect.any(String),
185+
mode: expect.objectContaining({
186+
primary: expect.any(String),
187+
aliases: expect.any(Object),
188+
}),
189+
});
190+
});
191+
192+
it('should syntax highlight an example', () => {
193+
render(syntaxHighlighter(testCase, instructions.mode.primary));
194+
expect(screen.getByTestId('SyntaxHighlighter').outerHTML).toMatchSnapshot();
195+
});
196+
197+
if (Object.keys(instructions.mode.aliases).length > 0) {
198+
const aliases = Object.keys(instructions.mode.aliases).map(alias => [alias, instructions.mode.aliases[alias]]);
199+
200+
describe('Mode aliases', () => {
201+
describe.each(aliases)('%s', (alias, aliasName) => {
202+
it('should support the mode alias', () => {
203+
render(syntaxHighlighter(testCase, instructions.mode.primary));
204+
const highlighted = screen.getByTestId('SyntaxHighlighter').outerHTML;
205+
cleanup();
206+
207+
render(syntaxHighlighter(testCase, alias));
208+
expect(screen.getByTestId('SyntaxHighlighter').outerHTML).toBe(highlighted);
209+
});
210+
211+
it('should uppercase the mode alias', () => {
212+
expect(uppercase(alias)).toBe(aliasName);
213+
});
214+
215+
if ('canonical' in instructions.mode) {
216+
it('should have a canonical directive set up', () => {
217+
expect(canonical(alias)).toBe(instructions.mode.canonical);
218+
});
219+
} else {
220+
it('should have a canonical directive set up off the primary mode', () => {
221+
expect(canonical(alias)).toBe(instructions.mode.primary);
222+
});
223+
}
224+
});
225+
});
226+
}
227+
228+
if (instructions.mode.primary === 'html') {
229+
it('should highlight handlebars templates', () => {
230+
const code = '<p>{{firstname}} {{lastname}}</p>';
231+
const { container } = render(syntaxHighlighter(code, 'handlebars'));
232+
233+
expect(container.querySelector('.cm-bracket')).toBeVisible();
234+
});
235+
} else if (instructions.mode.primary === 'php') {
236+
it('should highlight if missing an opening `<?php` tag', () => {
237+
const code = 'echo "Hello World";';
238+
const { container } = render(syntaxHighlighter(code, 'php'));
239+
240+
expect(container.querySelector('.cm-keyword')).toBeVisible();
241+
});
242+
}
243+
});
244+
});
245+
246+
describe('highlight mode', () => {
247+
const code = `curl --request POST
248+
--url <<url>>
249+
--header 'authorization: Bearer 123'
250+
--header 'content-type: application/json'`;
251+
252+
const defaultRender = () =>
253+
render(
254+
syntaxHighlighter(code, 'curl', {
255+
dark: true,
256+
highlightMode: true,
257+
tokenizeVariables: true,
258+
ranges: [
259+
[
260+
{ ch: 0, line: 0 },
261+
{ ch: 0, line: 1 },
262+
],
263+
],
264+
}),
265+
);
266+
267+
it('should return line numbers by default', () => {
268+
const { container } = defaultRender();
269+
expect(container.querySelector('.cm-lineNumber')).toBeVisible();
270+
});
271+
272+
it('should highlight variables', () => {
273+
defaultRender();
274+
expect(screen.getByText('URL')).toBeVisible();
275+
});
276+
277+
it('should highlight based on range input', () => {
278+
const { container } = defaultRender();
279+
expect(container.querySelector('.cm-linerow.cm-highlight')).toHaveTextContent('1curl --request POST');
280+
});
281+
282+
it('should add an overlay to non-highlighted in lines when ranges are applied', () => {
283+
const { container } = defaultRender();
284+
expect(container.querySelectorAll('.cm-linerow.cm-overlay')).toHaveLength(3);
285+
});
286+
});
287+
288+
describe('runmode', () => {
289+
const code =
290+
'CURL *hnd = curl_easy_init();\n\nurl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET");\n\ncurl_easy_setopt(hnd, CURLOPT_URL, "http://httpbin.orgpet/");';
291+
292+
it('should display the correct number of lines with multiple linebreaks', () => {
293+
render(
294+
syntaxHighlighter(code, 'c', {
295+
dark: true,
296+
highlightMode: true,
297+
tokenizeVariables: true,
298+
ranges: [
299+
[
300+
{ ch: 0, line: 0 },
301+
{ ch: 0, line: 1 },
302+
],
303+
],
304+
}),
305+
);
306+
307+
expect(screen.getAllByText(/\d/)).toHaveLength(5);
308+
});
309+
});
310+
311+
describe('code folding', () => {
312+
beforeAll(() => {
313+
// Prevents tests from throwing `TypeError: range(...).getBoundingClientRect is not a function`
314+
// Ref: https://github.com/jsdom/jsdom/issues/3002
315+
document.createRange = () => {
316+
const range = new Range();
317+
318+
range.getBoundingClientRect = jest.fn();
319+
320+
range.getClientRects = jest.fn(() => ({
321+
item: () => null,
322+
length: 0,
323+
}));
324+
325+
return range;
326+
};
327+
});
328+
329+
it('renders folders in the gutter', () => {
330+
const { container } = render(
331+
syntaxHighlighter('{ "a": { "b": { "c": 1 } }', 'json', { foldGutter: true, readOnly: true }),
332+
);
333+
334+
expect(container.querySelector('.CodeMirror-foldgutter')).toBeVisible();
335+
});
336+
});

0 commit comments

Comments
 (0)