From c3aa1d7dafd6e86821b495e78f6aba63daee356d Mon Sep 17 00:00:00 2001 From: Jacques Date: Mon, 3 Oct 2022 12:18:43 +0200 Subject: [PATCH] Move BiDi char wrapper to hljs plugin Moved the BiDi char wrapper to highlight.js plugin --- .../source_viewer/components/chunk_line.vue | 32 +------------------ .../components/source_viewer/constants.js | 6 +--- .../components/source_viewer/plugins/index.js | 5 ++- .../source_viewer/plugins/wrap_bidi_chars.js | 30 +++++++++++++++++ .../components/chunk_line_spec.js | 22 ------------- .../source_viewer/plugins/index_spec.js | 8 +++-- .../plugins/wrap_bidi_chars_spec.js | 17 ++++++++++ 7 files changed, 59 insertions(+), 61 deletions(-) create mode 100644 app/assets/javascripts/vue_shared/components/source_viewer/plugins/wrap_bidi_chars.js create mode 100644 spec/frontend/vue_shared/components/source_viewer/plugins/wrap_bidi_chars_spec.js diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue index 257b9f57222031..6395dbdac5069b 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue +++ b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue @@ -1,8 +1,6 @@ diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js index e2d1a81ee2bd84..10489a4eff3dcb 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js +++ b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js @@ -1,5 +1,3 @@ -import { __ } from '~/locale'; - // Language map from Rouge::Lexer to highlight.js // Rouge::Lexer - We use it on the BE to determine the language of a source file (https://github.com/rouge-ruby/rouge/blob/master/docs/Languages.md). // Highlight.js - We use it on the FE to highlight the syntax of a source file (https://github.com/highlightjs/highlight.js/tree/main/src/languages). @@ -139,9 +137,7 @@ export const BIDI_CHARS = [ export const BIDI_CHARS_CLASS_LIST = 'unicode-bidi has-tooltip'; -export const BIDI_CHAR_TOOLTIP = __( - 'Potentially unwanted character detected: Unicode BiDi Control', -); +export const BIDI_CHAR_TOOLTIP = 'Potentially unwanted character detected: Unicode BiDi Control'; export const HLJS_COMMENT_SELECTOR = 'hljs-comment'; diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/index.js b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/index.js index 5d24a3d110bbb2..e38c9b8f66faf8 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/index.js +++ b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/index.js @@ -1,6 +1,8 @@ -import { HLJS_ON_AFTER_HIGHLIGHT } from '../constants'; import wrapComments from './wrap_comments'; import linkDependencies from './link_dependencies'; +import wrapBidiChars from './wrap_bidi_chars'; + +export const HLJS_ON_AFTER_HIGHLIGHT = 'after:highlight'; /** * Registers our plugins for Highlight.js @@ -10,6 +12,7 @@ import linkDependencies from './link_dependencies'; * @param {Object} hljs - the Highlight.js instance. */ export const registerPlugins = (hljs, fileType, rawContent) => { + hljs.addPlugin({ [HLJS_ON_AFTER_HIGHLIGHT]: wrapBidiChars }); hljs.addPlugin({ [HLJS_ON_AFTER_HIGHLIGHT]: wrapComments }); hljs.addPlugin({ [HLJS_ON_AFTER_HIGHLIGHT]: (result) => linkDependencies(result, fileType, rawContent), diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/wrap_bidi_chars.js b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/wrap_bidi_chars.js new file mode 100644 index 00000000000000..3b6cd96ef78253 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/wrap_bidi_chars.js @@ -0,0 +1,30 @@ +import { + BIDI_CHARS, + BIDI_CHARS_CLASS_LIST, + BIDI_CHAR_TOOLTIP, +} from '~/vue_shared/components/source_viewer/constants'; + +/** + * Highlight.js plugin for wrapping BIDI chars. + * This ensures potentially dangerous BIDI characters are highlighted. + * + * Plugin API: https://github.com/highlightjs/highlight.js/blob/main/docs/plugin-api.rst + * + * @param {Object} Result - an object that represents the highlighted result from Highlight.js + */ + +function wrapBidiChar(bidiChar) { + return `${bidiChar}`; +} + +export default (result) => { + let { value } = result; + BIDI_CHARS.forEach((bidiChar) => { + if (value.includes(bidiChar)) { + value = value.replace(bidiChar, wrapBidiChar(bidiChar)); + } + }); + + // eslint-disable-next-line no-param-reassign + result.value = value; // Highlight.js expects the result param to be mutated for plugins to work +}; diff --git a/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js b/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js index fd3ff9ce892efe..f661bd6747a531 100644 --- a/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js +++ b/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js @@ -1,10 +1,5 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import ChunkLine from '~/vue_shared/components/source_viewer/components/chunk_line.vue'; -import { - BIDI_CHARS, - BIDI_CHARS_CLASS_LIST, - BIDI_CHAR_TOOLTIP, -} from '~/vue_shared/components/source_viewer/constants'; const DEFAULT_PROPS = { number: 2, @@ -31,7 +26,6 @@ describe('Chunk Line component', () => { const findLineLink = () => wrapper.find('.file-line-num'); const findBlameLink = () => wrapper.find('.file-line-blame'); const findContent = () => wrapper.findByTestId('content'); - const findWrappedBidiChars = () => wrapper.findAllByTestId('bidi-wrapper'); beforeEach(() => { createComponent(); @@ -40,22 +34,6 @@ describe('Chunk Line component', () => { afterEach(() => wrapper.destroy()); describe('rendering', () => { - it('wraps BiDi characters', () => { - const content = `// some content ${BIDI_CHARS.toString()} with BiDi chars`; - createComponent({ content }); - const wrappedBidiChars = findWrappedBidiChars(); - - expect(wrappedBidiChars.length).toBe(BIDI_CHARS.length); - - wrappedBidiChars.wrappers.forEach((_, i) => { - expect(wrappedBidiChars.at(i).text()).toBe(BIDI_CHARS[i]); - expect(wrappedBidiChars.at(i).attributes()).toMatchObject({ - class: BIDI_CHARS_CLASS_LIST, - title: BIDI_CHAR_TOOLTIP, - }); - }); - }); - it('renders a blame link', () => { expect(findBlameLink().attributes()).toMatchObject({ href: `${DEFAULT_PROPS.blamePath}#L${DEFAULT_PROPS.number}`, diff --git a/spec/frontend/vue_shared/components/source_viewer/plugins/index_spec.js b/spec/frontend/vue_shared/components/source_viewer/plugins/index_spec.js index 83fdc5d669d5ce..dc9954dcc627c0 100644 --- a/spec/frontend/vue_shared/components/source_viewer/plugins/index_spec.js +++ b/spec/frontend/vue_shared/components/source_viewer/plugins/index_spec.js @@ -1,6 +1,9 @@ -import { registerPlugins } from '~/vue_shared/components/source_viewer/plugins/index'; -import { HLJS_ON_AFTER_HIGHLIGHT } from '~/vue_shared/components/source_viewer/constants'; +import { + registerPlugins, + HLJS_ON_AFTER_HIGHLIGHT, +} from '~/vue_shared/components/source_viewer/plugins/index'; import wrapComments from '~/vue_shared/components/source_viewer/plugins/wrap_comments'; +import wrapBidiChars from '~/vue_shared/components/source_viewer/plugins/wrap_bidi_chars'; jest.mock('~/vue_shared/components/source_viewer/plugins/wrap_comments'); const hljsMock = { addPlugin: jest.fn() }; @@ -9,6 +12,7 @@ describe('Highlight.js plugin registration', () => { beforeEach(() => registerPlugins(hljsMock)); it('registers our plugins', () => { + expect(hljsMock.addPlugin).toHaveBeenCalledWith({ [HLJS_ON_AFTER_HIGHLIGHT]: wrapBidiChars }); expect(hljsMock.addPlugin).toHaveBeenCalledWith({ [HLJS_ON_AFTER_HIGHLIGHT]: wrapComments }); }); }); diff --git a/spec/frontend/vue_shared/components/source_viewer/plugins/wrap_bidi_chars_spec.js b/spec/frontend/vue_shared/components/source_viewer/plugins/wrap_bidi_chars_spec.js new file mode 100644 index 00000000000000..f40f8b2262724e --- /dev/null +++ b/spec/frontend/vue_shared/components/source_viewer/plugins/wrap_bidi_chars_spec.js @@ -0,0 +1,17 @@ +import wrapBidiChars from '~/vue_shared/components/source_viewer/plugins/wrap_bidi_chars'; +import { + BIDI_CHARS, + BIDI_CHARS_CLASS_LIST, + BIDI_CHAR_TOOLTIP, +} from '~/vue_shared/components/source_viewer/constants'; + +describe('Highlight.js plugin for wrapping BiDi characters', () => { + it.each(BIDI_CHARS)('wraps %s BiDi char', (bidiChar) => { + const inputValue = `// some content ${bidiChar} with BiDi chars`; + const outputValue = `// some content ${bidiChar}`; + const hljsResultMock = { value: inputValue }; + + wrapBidiChars(hljsResultMock); + expect(hljsResultMock.value).toContain(outputValue); + }); +}); -- GitLab