MM-54468 Fix code blocks being misaligned in search results (#25950)

Этот коммит содержится в:
Harrison Healey
2024-01-17 16:17:06 -05:00
коммит произвёл GitHub
родитель 2d71258834
Коммит c4cc2db0ff
3 изменённых файлов: 52 добавлений и 1268 удалений

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@@ -1,205 +1,94 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import type {ReactWrapper} from 'enzyme';
import {mount} from 'enzyme';
import React from 'react'; import React from 'react';
import {act} from 'react-dom/test-utils';
import {IntlProvider} from 'react-intl';
import {Provider as ReduxProvider} from 'react-redux';
import mockStore from 'tests/test_store'; import {act, renderWithContext, screen} from 'tests/react_testing_utils';
import CodeBlock from './code_block'; import CodeBlock from './code_block';
const actImmediate = (wrapper: ReactWrapper) => const actImmediate = () =>
act( act(
() => () =>
new Promise<void>((resolve) => { new Promise<void>((resolve) => {
setImmediate(() => { setImmediate(() => {
wrapper.update();
resolve(); resolve();
}); });
}), }),
); );
describe('codeBlock', () => { describe('codeBlock', () => {
const state = { test('should render typescript code block with syntax highlighting', async () => {
plugins: {components: {CodeBlockAction: []}},
};
const store = mockStore(state);
test('should render typescript code block before syntax highlighting', async () => {
const language = 'typescript'; const language = 'typescript';
const input = `\`\`\`${language} const input = `const myFunction = () => {
const myFunction = () => {
console.log('This is a meaningful function'); console.log('This is a meaningful function');
}; };`;
\`\`\`
`;
const wrapper = mount( const {container} = renderWithContext(
<ReduxProvider store={store}> <CodeBlock
<IntlProvider locale='en'> code={input}
<CodeBlock language={language}
code={input} />,
language={language}
/>
</IntlProvider>
</ReduxProvider>,
); );
await actImmediate(wrapper);
const languageHeader = wrapper.find('span.post-code__language').text(); expect(screen.getByText('TypeScript')).toBeInTheDocument();
const lineNumbersDiv = wrapper.find('.post-code__line-numbers').exists(); expect(container.querySelector('.post-code__line-numbers')).toBeInTheDocument();
expect(languageHeader).toEqual('TypeScript'); expect(container.querySelector('.hljs-keyword')).not.toBeInTheDocument();
expect(lineNumbersDiv).toBeTruthy();
expect(wrapper).toMatchSnapshot(); // Wait for highlight.js to finish loading
}); await actImmediate();
test('should render typescript code block after syntax highlighting', async () => { expect(screen.getByText('TypeScript')).toBeInTheDocument();
const language = 'typescript'; expect(container.querySelector('.post-code__line-numbers')).toBeInTheDocument();
const input = `\`\`\`${language}
const myFunction = () => {
console.log('This is a meaningful function');
};
\`\`\`
`;
const wrapper = mount( expect(container.querySelector('.hljs-keyword')).toBeInTheDocument();
<ReduxProvider store={store}>
<IntlProvider locale='en'>
<CodeBlock
code={input}
language={language}
/>
</IntlProvider>
</ReduxProvider>,
);
await actImmediate(wrapper);
const languageHeader = wrapper.find('span.post-code__language').text();
const lineNumbersDiv = wrapper.find('.post-code__line-numbers').exists();
expect(languageHeader).toEqual('TypeScript');
expect(lineNumbersDiv).toBeTruthy();
expect(wrapper).toMatchSnapshot();
});
test('should render html code block with proper indentation before syntax highlighting', async () => {
const language = 'html';
const input = `\`\`\`${language}
<div className='myClass'>
<a href='https://randomgibberishurl.com'>ClickMe</a>
</div>
\`\`\`
`;
const wrapper = mount(
<ReduxProvider store={store}>
<IntlProvider locale='en'>
<CodeBlock
code={input}
language={language}
/>
</IntlProvider>
</ReduxProvider>,
);
await actImmediate(wrapper);
const languageHeader = wrapper.find('span.post-code__language').text();
const lineNumbersDiv = wrapper.find('.post-code__line-numbers').exists();
expect(languageHeader).toEqual('HTML, XML');
expect(lineNumbersDiv).toBeTruthy();
expect(wrapper).toMatchSnapshot();
});
test('should render html code block with proper indentation after syntax highlighting', async () => {
const language = 'html';
const input = `\`\`\`${language}
<div className='myClass'>
<a href='https://randomgibberishurl.com'>ClickMe</a>
</div>
\`\`\`
`;
const wrapper = mount(
<ReduxProvider store={store}>
<IntlProvider locale='en'>
<CodeBlock
code={input}
language={language}
/>
</IntlProvider>
</ReduxProvider>,
);
await actImmediate(wrapper);
const languageHeader = wrapper.find('span.post-code__language').text();
const lineNumbersDiv = wrapper.find('.post-code__line-numbers').exists();
expect(languageHeader).toEqual('HTML, XML');
expect(lineNumbersDiv).toBeTruthy();
expect(wrapper).toMatchSnapshot();
}); });
test('should render unknown language before syntax highlighting', async () => { test('should render unknown language before syntax highlighting', async () => {
const language = 'unknownLanguage'; const language = 'unknownLanguage';
const input = `\`\`\`${language} const input = `this is my unknown language
this is my unknown language it shouldn't highlight, it's just garbage`;
it shouldn't highlight, it's just garbage
\`\`\`
`;
const wrapper = mount( const {container} = renderWithContext(
<ReduxProvider store={store}> <CodeBlock
<IntlProvider locale='en'> code={input}
<CodeBlock language={language}
code={input} />,
language={language}
/>
</IntlProvider>
</ReduxProvider>,
); );
await actImmediate(wrapper);
const languageHeader = wrapper.find('span.post-code__language').exists(); expect(screen.queryByText('unknownLanguage')).toBeFalsy();
const lineNumbersDiv = wrapper.find('.post-code__line-numbers').exists(); expect(container.querySelector('.post-code__line-numbers')).toBeFalsy();
expect(languageHeader).toBeFalsy(); // Wait for highlight.js to finish loading
expect(lineNumbersDiv).toBeFalsy(); await actImmediate();
expect(wrapper).toMatchSnapshot();
expect(screen.queryByText('unknownLanguage')).toBeFalsy();
expect(container.querySelector('.post-code__line-numbers')).toBeFalsy();
}); });
test('should render unknown language after syntax highlighting', async () => { test('MM-54468 should not add an extra space before highlighted code in search results', async () => {
const language = 'unknownLanguage'; const language = '';
const input = `\`\`\`${language} const input = 'foo foo foo foo';
this is my unknown language const searchedInput = '<span class="search-highlight">foo</span> <span class="search-highlight">foo</span> ' +
it shouldn't highlight, it's just garbage '<span class="search-highlight">foo</span> <span class="search-highlight">foo</span>';
\`\`\`
`;
const wrapper = mount( const {container} = renderWithContext(
<ReduxProvider store={store}> <CodeBlock
<IntlProvider locale='en'> code={input}
<CodeBlock language={language}
code={input} searchedContent={searchedInput}
language={language} />,
/>
</IntlProvider>
</ReduxProvider>,
); );
await actImmediate(wrapper);
const languageHeader = wrapper.find('span.post-code__language').exists(); // There shouldn't be a space between the overlay with search highlighting and the code below
const lineNumbersDiv = wrapper.find('.post-code__line-numbers').exists(); expect(container.querySelector('code')).toHaveTextContent('foo foo foo foofoo foo foo foo');
expect(container.querySelector('code')).not.toHaveTextContent('foo foo foo foo foo foo foo foo');
expect(languageHeader).toBeFalsy(); // Wait for highlight.js to finish loading
expect(lineNumbersDiv).toBeFalsy(); await actImmediate();
expect(wrapper).toMatchSnapshot();
expect(container.querySelector('code')).toHaveTextContent('foo foo foo foofoo foo foo foo');
expect(container.querySelector('code')).not.toHaveTextContent('foo foo foo foo foo foo foo foo');
}); });
}); });

Просмотреть файл

@@ -66,7 +66,7 @@ const CodeBlock: React.FC<Props> = ({code, language, searchedContent}: Props) =>
let htmlContent = content; let htmlContent = content;
if (searchedContent) { if (searchedContent) {
htmlContent = `${searchedContent} ${content}`; htmlContent = searchedContent + content;
} }
const codeBlockActions = useSelector((state: GlobalState) => state.plugins.components.CodeBlockAction); const codeBlockActions = useSelector((state: GlobalState) => state.plugins.components.CodeBlockAction);