Chore: Test grafana/public/app/plugins/panel/text/TextPanel.tsx (#52244)

* move sanitize test to its own test file

* add a test for renderTextPanelMarkdown to always sanitize

* setup TextPanel tests

* add tests to always sanitize Text Panel contents and always convert correctly to html/markdown
This commit is contained in:
Polina Boneva 2022-07-15 12:06:16 +03:00 committed by GitHub
parent 13b23fd512
commit bec500b69f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 142 additions and 11 deletions

View File

@ -1,5 +1,4 @@
import { renderMarkdown } from './markdown';
import { sanitizeTextPanelContent } from './sanitize';
import { renderMarkdown, renderTextPanelMarkdown } from './markdown';
describe('Markdown wrapper', () => {
it('should be able to handle undefined value', () => {
@ -12,12 +11,8 @@ describe('Markdown wrapper', () => {
expect(str).toBe('<script>alert()</script>');
});
it('should allow whitelisted styles in text panel', () => {
const html =
'<div style="display:flex; flex-direction: column; flex-wrap: wrap; justify-content: start; gap: 2px;"><div style="flex-basis: 50%"></div></div>';
const str = sanitizeTextPanelContent(html);
expect(str).toBe(
'<div style="display:flex; flex-direction:column; flex-wrap:wrap; justify-content:start; gap:2px;"><div style="flex-basis:50%;"></div></div>'
);
it('should sanitize content in text panel by default', () => {
const str = renderTextPanelMarkdown('<script>alert()</script>');
expect(str).toBe('&lt;script&gt;alert()&lt;/script&gt;');
});
});

View File

@ -0,0 +1,12 @@
import { sanitizeTextPanelContent } from './sanitize';
describe('Sanitize wrapper', () => {
it('should allow whitelisted styles in text panel', () => {
const html =
'<div style="display:flex; flex-direction: column; flex-wrap: wrap; justify-content: start; gap: 2px;"><div style="flex-basis: 50%"></div></div>';
const str = sanitizeTextPanelContent(html);
expect(str).toBe(
'<div style="display:flex; flex-direction:column; flex-wrap:wrap; justify-content:start; gap:2px;"><div style="flex-basis:50%;"></div></div>'
);
});
});

View File

@ -0,0 +1,120 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { dateTime, LoadingState, EventBusSrv } from '@grafana/data';
import { Props, TextPanel } from './TextPanel';
import { TextMode } from './models.gen';
const replaceVariablesMock = jest.fn();
const defaultProps: Props = {
id: 1,
data: {
state: LoadingState.Done,
series: [
{
fields: [],
length: 0,
},
],
timeRange: {
from: dateTime('2022-01-01T15:55:00Z'),
to: dateTime('2022-07-12T15:55:00Z'),
raw: {
from: 'now-15m',
to: 'now',
},
},
},
timeRange: {
from: dateTime('2022-07-11T15:55:00Z'),
to: dateTime('2022-07-12T15:55:00Z'),
raw: {
from: 'now-15m',
to: 'now',
},
},
timeZone: 'utc',
transparent: false,
width: 120,
height: 120,
fieldConfig: {
defaults: {},
overrides: [],
},
renderCounter: 1,
title: 'Test Text Panel',
eventBus: new EventBusSrv(),
options: { content: '', mode: TextMode.Markdown },
onOptionsChange: jest.fn(),
onFieldConfigChange: jest.fn(),
replaceVariables: replaceVariablesMock,
onChangeTimeRange: jest.fn(),
};
const setup = (props: Props = defaultProps) => {
render(<TextPanel {...props} />);
};
describe('TextPanel', () => {
it('should render panel without content', () => {
expect(() => setup()).not.toThrow();
});
it('sanitizes content in html mode', () => {
const contentTest = '<form><p>Form tags are sanitized.</p></form>\n<script>Script tags are sanitized.</script>';
replaceVariablesMock.mockReturnValueOnce(contentTest);
const props = Object.assign({}, defaultProps, {
options: { content: contentTest, mode: TextMode.HTML },
});
setup(props);
expect(screen.getByTestId('TextPanel-converted-content').innerHTML).toEqual(
'&lt;form&gt;<p>Form tags are sanitized.</p>&lt;/form&gt;\n&lt;script&gt;Script tags are sanitized.&lt;/script&gt;'
);
});
it('sanitizes content in markdown mode', () => {
const contentTest = '<form><p>Form tags are sanitized.</p></form>\n<script>Script tags are sanitized.</script>';
replaceVariablesMock.mockReturnValueOnce(contentTest);
const props = Object.assign({}, defaultProps, {
options: { content: contentTest, mode: TextMode.Markdown },
});
setup(props);
expect(screen.getByTestId('TextPanel-converted-content').innerHTML).toEqual(
'&lt;form&gt;<p>Form tags are sanitized.</p>&lt;/form&gt;\n&lt;script&gt;Script tags are sanitized.&lt;/script&gt;'
);
});
it('converts content to markdown when in markdown mode', async () => {
const contentTest = 'We begin by a simple sentence.\n```code block```';
replaceVariablesMock.mockReturnValueOnce(contentTest);
const props = Object.assign({}, defaultProps, {
options: { content: contentTest, mode: TextMode.Markdown },
});
setup(props);
const waited = await screen.getByTestId('TextPanel-converted-content');
expect(waited.innerHTML).toEqual('<p>We begin by a simple sentence.\n<code>code block</code></p>\n');
});
it('converts content to html when in html mode', () => {
const contentTest = 'We begin by a simple sentence.\n```This is a code block\n```';
replaceVariablesMock.mockReturnValueOnce(contentTest);
const props = Object.assign({}, defaultProps, {
options: { content: contentTest, mode: TextMode.HTML },
});
setup(props);
expect(screen.getByTestId('TextPanel-converted-content').innerHTML).toEqual(
'We begin by a simple sentence.\n```This is a code block\n```'
);
});
});

View File

@ -12,7 +12,7 @@ import config from 'app/core/config';
// Types
import { PanelOptions, TextMode } from './models.gen';
interface Props extends PanelProps<PanelOptions> {}
export interface Props extends PanelProps<PanelOptions> {}
interface State {
html: string;
@ -90,7 +90,11 @@ export class TextPanel extends PureComponent<Props, State> {
const styles = getStyles();
return (
<CustomScrollbar autoHeightMin="100%">
<DangerouslySetHtmlContent html={html} className={cx('markdown-html', styles.content)} />
<DangerouslySetHtmlContent
html={html}
className={cx('markdown-html', styles.content)}
data-testid="TextPanel-converted-content"
/>
</CustomScrollbar>
);
}