Skip to content

Commit 84e32c9

Browse files
beyackle2compulimbeyackleOEvgeny
authored
[Feature] Support HTML-in-Markdown (#5161)
* Added betterLinkDocumentMod * add markdownRenderHTML to styleOptions * Use new betterLinkDocumentMod and add a test * Fix asButton with aria-label and className * Update snapshots * Update entry * Use DOMParser + XMLSerializer * Revert red box * Update CHANGELOG.md * Update CHANGELOG.md Removes the redundant second changelog line * Add breaking changes * Run in JSDOM and remove empty "title" attribute * More explanation * Add HTML-in-Markdown for Adaptive Cards * Style HTML-in-Markdown for Adaptive Cards * Fix tests * Revert containerClassName * Move container to useRenderMarkdownAsHTML * Fix tests * Fix tests * Clean up * Add comment * Update entry * Fix tests * Added open in external icon * Add newline to end of XML serialization * Better test * Add HTML-in-Markdown --------- Co-authored-by: William Wong <[email protected]> Co-authored-by: Benjamin Yackley <[email protected]> Co-authored-by: Eugene Olonov <[email protected]>
1 parent 2b6e104 commit 84e32c9

File tree

58 files changed

+1377
-107
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1377
-107
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
3535
- Removed deprecated code: `connect*`, `useRenderActivity`, `useRenderActivityStatus`, `useRenderAvatar`, in PR [#5148](https://github.com/microsoft/BotFramework-WebChat/pull/5148), by [@compulim](https://github.com/compulim)
3636
- Added named exports in both CommonJS and ES Modules module format, in PR [#5148](https://github.com/microsoft/BotFramework-WebChat/pull/5148), by [@compulim](https://github.com/compulim)
3737
- Removed deprecated `useFocusSendBox()` hook, please use `useFocus('sendBox')` instead, in PR [#5150](https://github.com/microsoft/BotFramework-WebChat/pull/5150), by [@OEvgeny](https://github.com/OEvgeny)
38+
- HTML-in-Markdown is now supported. To disable this feature, set `styleOptions.markdownRenderHTML` to `false`
3839

3940
### Added
4041

@@ -65,6 +66,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6566
- Added `<ThemeProvider>` component to apply theme pack to Web Chat, by [@compulim](https://github.com/compulim), in PR [#5120](https://github.com/microsoft/BotFramework-WebChat/pull/5120)
6667
- Added `useMakeThumbnail` hook option to create a thumbnail from the file given, by [@compulim](https://github.com/compulim), in PR [#5123](https://github.com/microsoft/BotFramework-WebChat/pull/5123) and [#5122](https://github.com/microsoft/BotFramework-WebChat/pull/5122)
6768
- Added `moduleFormat` and `transpiler` build info to `<meta>` tag, in PR [#5148](https://github.com/microsoft/BotFramework-WebChat/pull/5148), by [@compulim](https://github.com/compulim)
69+
- Added support of rendering HTML-in-Markdown, in PR [#5161](https://github.com/microsoft/BotFramework-WebChat/pull/5161), by [@compulim](https://github.com/compulim), [@beyackle2](https://github.com/beyackle2), and [@OEvgeny](https://github.com/OEvgeny)
6870

6971
### Fixed
7072

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ export default function MyComponent() {
5454
}
5555
```
5656

57+
#### Support HTML-in-Markdown
58+
59+
Web Chat will now render HTML-in-Markdown. We have ported our sanitizer and accessibility fixer to work on HTML level. Both Markdown and HTML-in-Markdown will receive the same treatment and meet our security and accessibility requirements.
60+
61+
You can turn off this option by setting `styleOptions.markdownRenderHTML` to `false`.
62+
5763
### 4.16.1 notable changes
5864

5965
Web Chat now supports [Adaptive Cards schema up to 1.6](https://adaptivecards.io/explorer/). Some features in Adaptive Cards are in preview or designed to use outside of Bot Framework. Web Chat does not support these features.
Loading

__tests__/hooks/useRenderMarkdownAsHTML.js

+37-19
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,36 @@ import { timeouts } from '../constants.json';
55

66
jest.setTimeout(timeouts.test);
77

8+
const CSS_HASH_PATTERN = /webchat--css-[\d\w]*-[\d\w]*/u;
9+
810
test('renderMarkdown should use Markdown-It if not set in props', async () => {
911
const { pageObjects } = await setupWebDriver();
1012

11-
await expect(pageObjects.runHook('useRenderMarkdownAsHTML', [], fn => fn('Hello, World!'))).resolves.toBe(
12-
'<p>Hello, World!</p>\n'
13+
expect(
14+
(await pageObjects.runHook('useRenderMarkdownAsHTML', [], fn => fn('Hello, World!'))).replace(
15+
CSS_HASH_PATTERN,
16+
'webchat--css-xxxxx-xxxxx'
17+
)
18+
).toBe(
19+
'<div xmlns="http://www.w3.org/1999/xhtml" class="webchat__render-markdown webchat__render-markdown--message-activity webchat--css-xxxxx-xxxxx"><p>Hello, World!</p></div>\n'
1320
);
1421
});
1522

1623
test('renderMarkdown should use custom Markdown transform function from props', async () => {
1724
const { pageObjects } = await setupWebDriver({
1825
props: {
19-
renderMarkdown: text => text.toUpperCase()
26+
renderMarkdown: text => `<p>${text.toUpperCase()}</p>`
2027
}
2128
});
2229

23-
await expect(
24-
pageObjects.runHook('useRenderMarkdownAsHTML', [], fn => fn('Hello, World!'))
25-
).resolves.toMatchInlineSnapshot(`"HELLO, WORLD!"`);
30+
expect(
31+
(await pageObjects.runHook('useRenderMarkdownAsHTML', [], fn => fn('Hello, World!'))).replace(
32+
CSS_HASH_PATTERN,
33+
'webchat--css-xxxxx-xxxxx'
34+
)
35+
).toBe(
36+
'<div xmlns="http://www.w3.org/1999/xhtml" class="webchat__render-markdown webchat__render-markdown--message-activity webchat--css-xxxxx-xxxxx"><p>HELLO, WORLD!</p></div>\n'
37+
);
2638
});
2739

2840
test('renderMarkdown should return falsy if the custom Markdown transform function is null', async () => {
@@ -32,27 +44,33 @@ test('renderMarkdown should return falsy if the custom Markdown transform functi
3244
}
3345
});
3446

35-
await expect(pageObjects.runHook('useRenderMarkdownAsHTML', [], fn => !!fn)).resolves.toMatchInlineSnapshot(`false`);
47+
expect(await pageObjects.runHook('useRenderMarkdownAsHTML', [], fn => !!fn)).toBe(false);
3648
});
3749

3850
test('renderMarkdown should add accessibility text for external links', async () => {
3951
const { pageObjects } = await setupWebDriver();
4052

41-
await expect(
42-
pageObjects.runHook('useRenderMarkdownAsHTML', [], fn => fn('Click [here](https://aka.ms/) to find out more.'))
43-
).resolves.toMatchInlineSnapshot(`
44-
"<p>Click \u200B<a href=\\"https://aka.ms/\\" aria-label=\\"here Opens in a new window; external.\\" rel=\\"noopener noreferrer\\" target=\\"_blank\\">here<img src=\\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\\" alt class=\\"webchat__render-markdown__external-link-icon\\" title=\\"Opens in a new window; external.\\" /></a>\u200B to find out more.</p>
45-
"
46-
`);
53+
expect(
54+
(
55+
await pageObjects.runHook('useRenderMarkdownAsHTML', [], fn =>
56+
fn('Click [here](https://aka.ms/) to find out more.')
57+
)
58+
).replace(CSS_HASH_PATTERN, 'webchat--css-xxxxx-xxxxx')
59+
).toBe(
60+
`<div xmlns="http://www.w3.org/1999/xhtml" class="webchat__render-markdown webchat__render-markdown--message-activity webchat--css-xxxxx-xxxxx"><p>Click \u200B<a href="https://aka.ms/" aria-label="here Opens in a new window; external." rel="noopener noreferrer" target="_blank">here<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="" class="webchat__render-markdown__external-link-icon" title="Opens in a new window; external." /></a>\u200B to find out more.</p></div>\n`
61+
);
4762
});
4863

4964
test('renderMarkdown should add accessibility text for external links with yue', async () => {
5065
const { pageObjects } = await setupWebDriver({ props: { locale: 'yue' } });
5166

52-
await expect(
53-
pageObjects.runHook('useRenderMarkdownAsHTML', [], fn => fn('Click [here](https://aka.ms/) to find out more.'))
54-
).resolves.toMatchInlineSnapshot(`
55-
"<p>Click \u200B<a href=\\"https://aka.ms/\\" aria-label=\\"here 喺新嘅視窗開啟外部連結。\\" rel=\\"noopener noreferrer\\" target=\\"_blank\\">here<img src=\\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\\" alt class=\\"webchat__render-markdown__external-link-icon\\" title=\\"喺新嘅視窗開啟外部連結。\\" /></a>\u200B to find out more.</p>
56-
"
57-
`);
67+
expect(
68+
(
69+
await pageObjects.runHook('useRenderMarkdownAsHTML', [], fn =>
70+
fn('Click [here](https://aka.ms/) to find out more.')
71+
)
72+
).replace(CSS_HASH_PATTERN, 'webchat--css-xxxxx-xxxxx')
73+
).toBe(
74+
`<div xmlns="http://www.w3.org/1999/xhtml" class="webchat__render-markdown webchat__render-markdown--message-activity webchat--css-xxxxx-xxxxx"><p>Click \u200B<a href="https://aka.ms/" aria-label="here 喺新嘅視窗開啟外部連結。" rel="noopener noreferrer" target="_blank">here<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="" class="webchat__render-markdown__external-link-icon" title="喺新嘅視窗開啟外部連結。" /></a>\u200B to find out more.</p></div>\n`
75+
);
5876
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<!doctype html>
2+
<html lang="en-US">
3+
<head>
4+
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
5+
<script crossorigin="anonymous" src="/test-harness.js"></script>
6+
<script crossorigin="anonymous" src="/test-page-object.js"></script>
7+
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
8+
</head>
9+
<body>
10+
<main id="webchat"></main>
11+
<script>
12+
run(async function () {
13+
const { directLine, store } = testHelpers.createDirectLineEmulator();
14+
15+
WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));
16+
17+
await pageConditions.uiConnected();
18+
19+
await directLine.emulateIncomingActivity({
20+
attachments: [
21+
{
22+
content: {
23+
type: 'AdaptiveCard',
24+
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
25+
version: '1.5',
26+
body: [
27+
{
28+
style: 'heading',
29+
type: 'TextBlock',
30+
text: 'This is an Adaptive Card'
31+
},
32+
{
33+
type: 'TextBlock',
34+
text: atob(
35+
'IyMgSGVhZGVyCjxwPlRoaXMgaXMgc29tZSB0ZXh0IHdpdGggYSA8YSBocmVmPSJodHRwOi8vZXhhbXBsZS5jb20iIHRpdGxlPSJldmVuIGhhY2tlcnMgcmVzcGVjdCBhMTF5Ij5saW5rIGluIGl0PC9hPi4gQWxzbyA8c3Ryb25nPnNvbWUgdGV4dDwvc3Ryb25nPiA8ZW0+d2l0aCBmb3JtYXR0aW5nPC9lbT4uPC9wPgo8dWw+CjxsaT5Vbm9yZGVyZWQ8L2xpPgo8bGk+bGlzdDwvbGk+CjxsaT5pdGVtczwvbGk+CjwvdWw+CjxvbD4KPGxpPk9yZGVyZWQ8L2xpPgo8bGk+bGlzdDwvbGk+CjxsaT5pdGVtczwvbGk+Cjwvb2w+Cjx1bD4KPGxpPkJlbG93IGFyZSBzb21lIHVuc2FmZSB0aGluZ3MgdGhhdCBzaG91bGQgZ2V0IHNhbml0aXplZDoKPGxpPjxzY3JpcHQ+VGhpcyBpc24ndCBhbGxvd2VkITwvc2NyaXB0Pgo8bGk+PGltZyBzcmM9IiIgYWx0PSJJIGFtIGEgZHVtbXkgaW1hZ2UgdHJ5aW5nIHRvIGhhY2sgeW91IiBvbmVycm9yPWFsZXJ0KDEpIC8+CjxsaT48c3ZnIGFyaWEtbGFiZWw9Im1hbGljaW91cyBzdmcgaGVyZSI+PGcvb25sb2FkPWFsZXJ0KDIpLy88cD4KPGxpPjxwPmFiYzxpZnJhbWUvL3NyYz1qQXZhJlRhYjtzY3JpcHQ6YWxlcnQoMyk+ZGVmPC9wPgo8bGk+PG1hdGg+PG1pLy94bGluazpocmVmPSJkYXRhOngsPHNjcmlwdD5hbGVydCg0KTwvc2NyaXB0Pgo8bGk+CjwvdWw+'
36+
),
37+
wrap: true
38+
}
39+
],
40+
actions: [
41+
{
42+
type: 'Action.Submit',
43+
title: 'Submit card'
44+
}
45+
]
46+
},
47+
contentType: 'application/vnd.microsoft.card.adaptive'
48+
}
49+
],
50+
type: 'message'
51+
});
52+
53+
await pageConditions.numActivitiesShown(1);
54+
55+
const [firstActivityElement] = pageElements.activities();
56+
57+
const link = firstActivityElement.querySelector('[href="http://example.com"]');
58+
const linkImage = link.querySelector('.webchat__render-markdown__external-link-icon');
59+
60+
expect(link.getAttribute('aria-label')).toBe('link in it Opens in a new window; external.');
61+
expect(link.getAttribute('target')).toBe('_blank');
62+
expect(link.getAttribute('rel')).toBe('noopener noreferrer');
63+
expect(linkImage.title).toBe('Opens in a new window; external.');
64+
65+
await host.snapshot();
66+
});
67+
</script>
68+
</body>
69+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */
2+
3+
describe('markdownRenderHTML when unset', () => {
4+
test('should render sanitized HTML in Adaptive Cards', () => runHTML('markdownRenderHTML/default.adaptiveCards'));
5+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<!doctype html>
2+
<html lang="en-US">
3+
<head>
4+
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
5+
<script crossorigin="anonymous" src="/test-harness.js"></script>
6+
<script crossorigin="anonymous" src="/test-page-object.js"></script>
7+
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
8+
</head>
9+
<body>
10+
<main id="webchat"></main>
11+
<script>
12+
run(async function () {
13+
const { directLine, store } = testHelpers.createDirectLineEmulator();
14+
15+
WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));
16+
17+
await pageConditions.uiConnected();
18+
19+
await directLine.emulateIncomingActivity({
20+
entities: [
21+
{
22+
'@context': 'https://schema.org',
23+
'@id': '',
24+
'@type': 'Message',
25+
citation: [
26+
{
27+
'@type': 'Claim',
28+
appearance: {
29+
'@type': 'DigitalDocument',
30+
url: 'https://example.com/1/',
31+
usageInfo: {
32+
'@id': '_:1',
33+
'@type': 'CreativeWork',
34+
description:
35+
'Nisi quis ut sint elit est nulla enim eiusmod. Deserunt commodo pariatur nostrud culpa aliquip esse pariatur exercitation nulla do proident. Est qui eiusmod aliquip deserunt labore consequat fugiat. Ullamco reprehenderit nostrud eiusmod nisi nulla esse id. Reprehenderit aliqua quis consectetur sit cupidatat fugiat Lorem ex labore. Eiusmod velit laborum quis tempor incididunt excepteur culpa esse nulla.',
36+
keywords: ['encrypted-content'],
37+
name: 'Sit veniam do irure velit est et quis ut Lorem reprehenderit commodo cillum occaecat',
38+
pattern: {
39+
'@type': 'DefinedTerm',
40+
inDefinedTermSet: 'https://www.w3.org/TR/css-color-4/',
41+
name: 'color',
42+
termCode: 'orange'
43+
}
44+
}
45+
},
46+
position: 1
47+
},
48+
{
49+
'@type': 'Claim',
50+
appearance: {
51+
'@type': 'DigitalDocument',
52+
text: 'Incididunt amet dolore anim commodo fugiat occaecat elit nulla do consequat. Quis incididunt occaecat labore adipisicing. Cillum sunt velit consequat irure ipsum ullamco sint ea aute. Sunt et eu ut enim aliqua cupidatat non adipisicing dolore commodo dolor magna enim. Commodo reprehenderit excepteur ad nostrud ex id aliquip deserunt eiusmod. Esse non labore nulla voluptate.',
53+
usageInfo: {
54+
'@type': 'CreativeWork',
55+
name: 'Velit exercitation',
56+
pattern: {
57+
'@type': 'DefinedTerm',
58+
inDefinedTermSet: 'https://www.w3.org/TR/css-color-4/',
59+
name: 'color',
60+
termCode: 'Yellow'
61+
}
62+
}
63+
},
64+
position: 3
65+
},
66+
{
67+
'@type': 'Claim',
68+
appearance: {
69+
'@type': 'DigitalDocument',
70+
text: atob(
71+
'IyMgSGVhZGVyCjxwPlRoaXMgaXMgc29tZSB0ZXh0IHdpdGggYSA8YSBocmVmPSJodHRwOi8vZXhhbXBsZS5jb20iIHRpdGxlPSJldmVuIGhhY2tlcnMgcmVzcGVjdCBhMTF5Ij5saW5rIGluIGl0PC9hPi4gQWxzbyA8c3Ryb25nPnNvbWUgdGV4dDwvc3Ryb25nPiA8ZW0+d2l0aCBmb3JtYXR0aW5nPC9lbT4uPC9wPgo8dWw+CjxsaT5Vbm9yZGVyZWQ8L2xpPgo8bGk+bGlzdDwvbGk+CjxsaT5pdGVtczwvbGk+CjwvdWw+CjxvbD4KPGxpPk9yZGVyZWQ8L2xpPgo8bGk+bGlzdDwvbGk+CjxsaT5pdGVtczwvbGk+Cjwvb2w+Cjx1bD4KPGxpPkJlbG93IGFyZSBzb21lIHVuc2FmZSB0aGluZ3MgdGhhdCBzaG91bGQgZ2V0IHNhbml0aXplZDoKPGxpPjxzY3JpcHQ+VGhpcyBpc24ndCBhbGxvd2VkITwvc2NyaXB0Pgo8bGk+PGltZyBzcmM9IiIgYWx0PSJJIGFtIGEgZHVtbXkgaW1hZ2UgdHJ5aW5nIHRvIGhhY2sgeW91IiBvbmVycm9yPWFsZXJ0KDEpIC8+CjxsaT48c3ZnIGFyaWEtbGFiZWw9Im1hbGljaW91cyBzdmcgaGVyZSI+PGcvb25sb2FkPWFsZXJ0KDIpLy88cD4KPGxpPjxwPmFiYzxpZnJhbWUvL3NyYz1qQXZhJlRhYjtzY3JpcHQ6YWxlcnQoMyk+ZGVmPC9wPgo8bGk+PG1hdGg+PG1pLy94bGluazpocmVmPSJkYXRhOngsPHNjcmlwdD5hbGVydCg0KTwvc2NyaXB0Pgo8bGk+CjwvdWw+'
72+
)
73+
},
74+
position: 4
75+
}
76+
],
77+
type: 'https://schema.org/Message',
78+
usageInfo: { '@id': '_:1' }
79+
}
80+
],
81+
text: 'Ipsum[1] dolore[2] cupidatat[3] magna[4] consectetur[5] do tempor est excepteur.\n\n[1]: https://example.com/1/ "Sint amet id officia dolor ex eiusmod ipsum ipsum magna fugiat"\n[2]: https://example.com/2/ "Laboris cupidatat voluptate"\n[3]: _:3 "Velit nulla culpa eu ea consectetur consectetur dolore velit"\n[4]: _:4 "Adipisicing enim nulla"\n[5]: https://example.com/5',
82+
type: 'message'
83+
});
84+
85+
const [firstActivityElement] = pageElements.activities();
86+
87+
const linkDefinitions = firstActivityElement.querySelectorAll('.webchat__link-definitions__list-item');
88+
89+
expect(linkDefinitions).toHaveProperty('length', 5);
90+
91+
linkDefinitions[3].querySelector('button').click();
92+
93+
const link = document.querySelector('dialog [href="http://example.com"]');
94+
const linkImage = link.querySelector('.webchat__render-markdown__external-link-icon');
95+
96+
expect(link.getAttribute('aria-label')).toBe('link in it Opens in a new window; external.');
97+
expect(link.getAttribute('target')).toBe('_blank');
98+
expect(link.getAttribute('rel')).toBe('noopener noreferrer');
99+
expect(linkImage.title).toBe('Opens in a new window; external.');
100+
101+
await host.snapshot();
102+
});
103+
</script>
104+
</body>
105+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */
2+
3+
describe('markdownRenderHTML when unset', () => {
4+
test('should render sanitized HTML in citation modal', () => runHTML('markdownRenderHTML/default.citationModal'));
5+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!doctype html>
2+
<html lang="en-US">
3+
<head>
4+
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
5+
<script crossorigin="anonymous" src="/test-harness.js"></script>
6+
<script crossorigin="anonymous" src="/test-page-object.js"></script>
7+
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
8+
</head>
9+
<body>
10+
<main id="webchat"></main>
11+
<script>
12+
run(async function () {
13+
const { directLine, store } = testHelpers.createDirectLineEmulator();
14+
15+
WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));
16+
17+
await pageConditions.uiConnected();
18+
19+
await directLine.emulateIncomingActivity({
20+
text: atob(
21+
'IyMgSGVhZGVyCjxwPlRoaXMgaXMgc29tZSB0ZXh0IHdpdGggYSA8YSBocmVmPSJodHRwOi8vZXhhbXBsZS5jb20iIHRpdGxlPSJldmVuIGhhY2tlcnMgcmVzcGVjdCBhMTF5Ij5saW5rIGluIGl0PC9hPi4gQWxzbyA8c3Ryb25nPnNvbWUgdGV4dDwvc3Ryb25nPiA8ZW0+d2l0aCBmb3JtYXR0aW5nPC9lbT4uPC9wPgo8dWw+CjxsaT5Vbm9yZGVyZWQ8L2xpPgo8bGk+bGlzdDwvbGk+CjxsaT5pdGVtczwvbGk+CjwvdWw+CjxvbD4KPGxpPk9yZGVyZWQ8L2xpPgo8bGk+bGlzdDwvbGk+CjxsaT5pdGVtczwvbGk+Cjwvb2w+Cjx1bD4KPGxpPkJlbG93IGFyZSBzb21lIHVuc2FmZSB0aGluZ3MgdGhhdCBzaG91bGQgZ2V0IHNhbml0aXplZDoKPGxpPjxzY3JpcHQ+VGhpcyBpc24ndCBhbGxvd2VkITwvc2NyaXB0Pgo8bGk+PGltZyBzcmM9IiIgYWx0PSJJIGFtIGEgZHVtbXkgaW1hZ2UgdHJ5aW5nIHRvIGhhY2sgeW91IiBvbmVycm9yPWFsZXJ0KDEpIC8+CjxsaT48c3ZnIGFyaWEtbGFiZWw9Im1hbGljaW91cyBzdmcgaGVyZSI+PGcvb25sb2FkPWFsZXJ0KDIpLy88cD4KPGxpPjxwPmFiYzxpZnJhbWUvL3NyYz1qQXZhJlRhYjtzY3JpcHQ6YWxlcnQoMyk+ZGVmPC9wPgo8bGk+PG1hdGg+PG1pLy94bGluazpocmVmPSJkYXRhOngsPHNjcmlwdD5hbGVydCg0KTwvc2NyaXB0Pgo8bGk+CjwvdWw+'
22+
),
23+
type: 'message'
24+
});
25+
26+
await pageConditions.numActivitiesShown(1);
27+
28+
const [firstActivityElement] = pageElements.activities();
29+
30+
const link = firstActivityElement.querySelector('[href="http://example.com"]');
31+
const linkImage = link.querySelector('.webchat__render-markdown__external-link-icon');
32+
33+
expect(link.getAttribute('aria-label')).toBe('link in it Opens in a new window; external.');
34+
expect(link.getAttribute('target')).toBe('_blank');
35+
expect(link.getAttribute('rel')).toBe('noopener noreferrer');
36+
expect(linkImage.title).toBe('Opens in a new window; external.');
37+
38+
await host.snapshot();
39+
});
40+
</script>
41+
</body>
42+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */
2+
3+
describe('markdownRenderHTML when unset', () => {
4+
test('should render sanitized HTML in message activity', () => runHTML('markdownRenderHTML/default.messageActivity'));
5+
});

0 commit comments

Comments
 (0)