summaryrefslogtreecommitdiff
path: root/spec/frontend/content_editor/services/code_block_language_loader_spec.js
blob: 905c1685b94d17c9cdb2f62ab36b96312266ef32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import codeBlockLanguageBlocker from '~/content_editor/services/code_block_language_loader';
import waitForPromises from 'helpers/wait_for_promises';
import { backtickInputRegex } from '~/content_editor/extensions/code_block_highlight';

describe('content_editor/services/code_block_language_loader', () => {
  let languageLoader;
  let lowlight;

  beforeEach(() => {
    lowlight = {
      languages: [],
      registerLanguage: jest
        .fn()
        .mockImplementation((language) => lowlight.languages.push(language)),
      registered: jest.fn().mockImplementation((language) => lowlight.languages.includes(language)),
    };
    languageLoader = codeBlockLanguageBlocker;
    languageLoader.lowlight = lowlight;
  });

  describe('findLanguageBySyntax', () => {
    it.each`
      syntax          | language
      ${'javascript'} | ${{ syntax: 'javascript', label: 'Javascript' }}
      ${'js'}         | ${{ syntax: 'javascript', label: 'Javascript' }}
      ${'jsx'}        | ${{ syntax: 'javascript', label: 'Javascript' }}
    `('returns a language by syntax and its variants', ({ syntax, language }) => {
      expect(languageLoader.findLanguageBySyntax(syntax)).toMatchObject(language);
    });

    it('returns Custom (syntax) if the language does not exist', () => {
      expect(languageLoader.findLanguageBySyntax('foobar')).toMatchObject({
        syntax: 'foobar',
        label: 'Custom (foobar)',
      });
    });

    it('returns plaintext if no syntax is passed', () => {
      expect(languageLoader.findLanguageBySyntax('')).toMatchObject({
        syntax: 'plaintext',
        label: 'Plain text',
      });
    });
  });

  describe('filterLanguages', () => {
    it('filters languages by the given search term', () => {
      expect(languageLoader.filterLanguages('ts')).toEqual([
        { label: 'Device Tree', syntax: 'dts' },
        { label: 'Kotlin', syntax: 'kotlin', variants: 'kt, kts' },
        { label: 'TypeScript', syntax: 'typescript', variants: 'ts, tsx' },
      ]);
    });
  });

  describe('loadLanguages', () => {
    it('loads highlight.js language packages identified by a list of languages', async () => {
      const languages = ['javascript', 'ruby'];

      await languageLoader.loadLanguages(languages);

      languages.forEach((language) => {
        expect(lowlight.registerLanguage).toHaveBeenCalledWith(language, expect.any(Function));
      });
    });

    describe('when language is already registered', () => {
      it('does not load the language again', async () => {
        const languages = ['javascript'];

        await languageLoader.loadLanguages(languages);
        await languageLoader.loadLanguages(languages);

        expect(lowlight.registerLanguage).toHaveBeenCalledTimes(1);
      });
    });
  });

  describe('loadLanguagesFromDOM', () => {
    it('loads highlight.js language packages identified by pre tags in a DOM fragment', async () => {
      const parser = new DOMParser();
      const { body } = parser.parseFromString(
        `
      <pre lang="javascript"></pre>
      <pre lang="ruby"></pre>
      `,
        'text/html',
      );

      await languageLoader.loadLanguagesFromDOM(body);

      expect(lowlight.registerLanguage).toHaveBeenCalledWith('javascript', expect.any(Function));
      expect(lowlight.registerLanguage).toHaveBeenCalledWith('ruby', expect.any(Function));
    });
  });

  describe('loadLanguageFromInputRule', () => {
    it('loads highlight.js language packages identified from the input rule', async () => {
      const match = new RegExp(backtickInputRegex).exec('```js ');
      const attrs = languageLoader.loadLanguageFromInputRule(match);

      await waitForPromises();

      expect(attrs).toEqual({ language: 'javascript' });
      expect(lowlight.registerLanguage).toHaveBeenCalledWith('javascript', expect.any(Function));
    });
  });

  describe('isLanguageLoaded', () => {
    it('returns true when a language is registered', async () => {
      const language = 'javascript';

      expect(languageLoader.isLanguageLoaded(language)).toBe(false);

      await languageLoader.loadLanguages([language]);

      expect(languageLoader.isLanguageLoaded(language)).toBe(true);
    });
  });
});