Commit 804d5ea7 authored by Jacques Erasmus's avatar Jacques Erasmus Committed by Miguel Rincon

Fix copy as GFM in refactored blob viewer

Fixed the copy as GFM in the refactored viewer
parent 27be56f2
......@@ -35,16 +35,20 @@ export default {
},
highlightedContent() {
let highlightedContent;
let { language } = this;
if (this.hljs) {
if (!this.language) {
highlightedContent = this.hljs.highlightAuto(this.content).value;
if (!language) {
const hljsHighlightAuto = this.hljs.highlightAuto(this.content);
highlightedContent = hljsHighlightAuto.value;
language = hljsHighlightAuto.language;
} else if (this.languageDefinition) {
highlightedContent = this.hljs.highlight(this.content, { language: this.language }).value;
}
}
return wrapLines(highlightedContent);
return wrapLines(highlightedContent, language);
},
},
watch: {
......@@ -110,7 +114,7 @@ export default {
data-qa-selector="blob_viewer_file_content"
>
<line-numbers :lines="lineNumbers" />
<pre class="code gl-pb-0!"><code v-safe-html="highlightedContent"></code>
<pre class="code highlight gl-pb-0!"><code v-safe-html="highlightedContent"></code>
</pre>
</div>
</template>
export const wrapLines = (content) => {
export const wrapLines = (content, language) => {
const isValidLanguage = /^[a-z\d\-_]+$/.test(language); // To prevent the possibility of a vulnerability we only allow languages that contain alphanumeric characters ([a-z\d), dashes (-) or underscores (_).
return (
content &&
content
.split('\n')
.map((line, i) => {
let formattedLine;
const idAttribute = `id="LC${i + 1}"`;
const attributes = `id="LC${i + 1}" lang="${isValidLanguage ? language : ''}"`;
if (line.includes('<span class="hljs') && !line.includes('</span>')) {
/**
......@@ -14,9 +16,9 @@ export const wrapLines = (content) => {
* example (before): <span class="hljs-code">```bash
* example (after): <span id="LC67" class="hljs-code">```bash
*/
formattedLine = line.replace(/(?=class="hljs)/, `${idAttribute} `);
formattedLine = line.replace(/(?=class="hljs)/, `${attributes} `);
} else {
formattedLine = `<span ${idAttribute} class="line">${line}</span>`;
formattedLine = `<span ${attributes} class="line">${line}</span>`;
}
return formattedLine;
......
......@@ -7,10 +7,6 @@ RSpec.describe 'Copy as GFM', :js do
include RepoHelpers
include ActionView::Helpers::JavaScriptHelper
before do
stub_feature_flags(refactor_blob_viewer: false) # This stub will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/350454
end
describe 'Copying rendered GFM' do
before do
@feat = MarkdownFeature.new
......@@ -764,8 +760,8 @@ RSpec.describe 'Copy as GFM', :js do
context 'selecting one word of text' do
it 'copies as inline code' do
verify(
'.line[id="LC9"] .no',
'`RuntimeError`'
'.line[id="LC10"]',
'`end`'
)
end
end
......@@ -834,6 +830,7 @@ RSpec.describe 'Copy as GFM', :js do
end
def verify(selector, gfm, target: nil)
expect(page).to have_selector('.js-syntax-highlight')
html = html_for_selector(selector)
output_gfm = html_to_gfm(html, 'transformCodeSelection', target: target)
wait_for_requests
......
......@@ -7,6 +7,7 @@ import SourceViewer from '~/vue_shared/components/source_viewer/source_viewer.vu
import { ROUGE_TO_HLJS_LANGUAGE_MAP } from '~/vue_shared/components/source_viewer/constants';
import LineNumbers from '~/vue_shared/components/line_numbers.vue';
import waitForPromises from 'helpers/wait_for_promises';
import * as sourceViewerUtils from '~/vue_shared/components/source_viewer/utils';
jest.mock('highlight.js/lib/core');
Vue.use(VueRouter);
......@@ -36,6 +37,7 @@ describe('Source Viewer component', () => {
beforeEach(() => {
hljs.highlight.mockImplementation(() => ({ value: highlightedContent }));
hljs.highlightAuto.mockImplementation(() => ({ value: highlightedContent }));
jest.spyOn(sourceViewerUtils, 'wrapLines');
return createComponent();
});
......@@ -73,6 +75,10 @@ describe('Source Viewer component', () => {
expect(findLoadingIcon().exists()).toBe(true);
});
it('calls the wrapLines helper method with highlightedContent and mappedLanguage', () => {
expect(sourceViewerUtils.wrapLines).toHaveBeenCalledWith(highlightedContent, mappedLanguage);
});
it('renders Line Numbers', () => {
expect(findLineNumbers().props('lines')).toBe(1);
});
......
......@@ -2,12 +2,25 @@ import { wrapLines } from '~/vue_shared/components/source_viewer/utils';
describe('Wrap lines', () => {
it.each`
input | output
${'line 1'} | ${'<span id="LC1" class="line">line 1</span>'}
${'line 1\nline 2'} | ${`<span id="LC1" class="line">line 1</span>\n<span id="LC2" class="line">line 2</span>`}
${'<span class="hljs-code">line 1\nline 2</span>'} | ${`<span id="LC1" class="hljs-code">line 1\n<span id="LC2" class="line">line 2</span></span>`}
${'<span class="hljs-code">```bash'} | ${'<span id="LC1" class="hljs-code">```bash'}
`('returns lines wrapped in spans containing line numbers', ({ input, output }) => {
expect(wrapLines(input)).toBe(output);
content | language | output
${'line 1'} | ${'javascript'} | ${'<span id="LC1" lang="javascript" class="line">line 1</span>'}
${'line 1\nline 2'} | ${'html'} | ${`<span id="LC1" lang="html" class="line">line 1</span>\n<span id="LC2" lang="html" class="line">line 2</span>`}
${'<span class="hljs-code">line 1\nline 2</span>'} | ${'html'} | ${`<span id="LC1" lang="html" class="hljs-code">line 1\n<span id="LC2" lang="html" class="line">line 2</span></span>`}
${'<span class="hljs-code">```bash'} | ${'bash'} | ${'<span id="LC1" lang="bash" class="hljs-code">```bash'}
${'<span class="hljs-code">```bash'} | ${'valid-language1'} | ${'<span id="LC1" lang="valid-language1" class="hljs-code">```bash'}
${'<span class="hljs-code">```bash'} | ${'valid_language2'} | ${'<span id="LC1" lang="valid_language2" class="hljs-code">```bash'}
`('returns lines wrapped in spans containing line numbers', ({ content, language, output }) => {
expect(wrapLines(content, language)).toBe(output);
});
it.each`
language
${'invalidLanguage>'}
${'"invalidLanguage"'}
${'<invalidLanguage'}
`('returns lines safely without XSS language is not valid', ({ language }) => {
expect(wrapLines('<span class="hljs-code">```bash', language)).toBe(
'<span id="LC1" lang="" class="hljs-code">```bash',
);
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment