diff --git a/packages/grafana-data/src/text/markdown.test.ts b/packages/grafana-data/src/text/markdown.test.ts index b9e7052c3ef..20a5bab4611 100644 --- a/packages/grafana-data/src/text/markdown.test.ts +++ b/packages/grafana-data/src/text/markdown.test.ts @@ -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 = - '
'; - const str = sanitizeTextPanelContent(html); - expect(str).toBe( - '
' - ); + it('should sanitize content in text panel by default', () => { + const str = renderTextPanelMarkdown(''); + expect(str).toBe('<script>alert()</script>'); }); }); diff --git a/packages/grafana-data/src/text/sanitize.test.ts b/packages/grafana-data/src/text/sanitize.test.ts new file mode 100644 index 00000000000..8d9ff47d4d9 --- /dev/null +++ b/packages/grafana-data/src/text/sanitize.test.ts @@ -0,0 +1,12 @@ +import { sanitizeTextPanelContent } from './sanitize'; + +describe('Sanitize wrapper', () => { + it('should allow whitelisted styles in text panel', () => { + const html = + '
'; + const str = sanitizeTextPanelContent(html); + expect(str).toBe( + '
' + ); + }); +}); diff --git a/public/app/plugins/panel/text/TextPanel.test.tsx b/public/app/plugins/panel/text/TextPanel.test.tsx new file mode 100644 index 00000000000..99a245dff33 --- /dev/null +++ b/public/app/plugins/panel/text/TextPanel.test.tsx @@ -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(); +}; + +describe('TextPanel', () => { + it('should render panel without content', () => { + expect(() => setup()).not.toThrow(); + }); + + it('sanitizes content in html mode', () => { + const contentTest = '

Form tags are sanitized.

\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( + '<form>

Form tags are sanitized.

</form>\n<script>Script tags are sanitized.</script>' + ); + }); + + it('sanitizes content in markdown mode', () => { + const contentTest = '

Form tags are sanitized.

\n'; + replaceVariablesMock.mockReturnValueOnce(contentTest); + + const props = Object.assign({}, defaultProps, { + options: { content: contentTest, mode: TextMode.Markdown }, + }); + + setup(props); + + expect(screen.getByTestId('TextPanel-converted-content').innerHTML).toEqual( + '<form>

Form tags are sanitized.

</form>\n<script>Script tags are sanitized.</script>' + ); + }); + + 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('

We begin by a simple sentence.\ncode block

\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```' + ); + }); +}); diff --git a/public/app/plugins/panel/text/TextPanel.tsx b/public/app/plugins/panel/text/TextPanel.tsx index c30ba779ec9..8494f3a76c8 100644 --- a/public/app/plugins/panel/text/TextPanel.tsx +++ b/public/app/plugins/panel/text/TextPanel.tsx @@ -12,7 +12,7 @@ import config from 'app/core/config'; // Types import { PanelOptions, TextMode } from './models.gen'; -interface Props extends PanelProps {} +export interface Props extends PanelProps {} interface State { html: string; @@ -90,7 +90,11 @@ export class TextPanel extends PureComponent { const styles = getStyles(); return ( - + ); }