MM-67725: (test) Migrate enzyme to RTL (#34966)

This commit is contained in:
sabril
2026-01-27 09:52:46 +08:00
committed by GitHub
parent a1c85007e1
commit 7b1c32e34c
70 changed files with 4160 additions and 3633 deletions
@@ -1,183 +1,69 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/FormattedMarkdownMessage should allow to disable links 1`] = `
<IntlProvider
defaultFormats={Object {}}
defaultLocale="en"
fallbackOnEmptyString={true}
formats={Object {}}
locale="en"
messages={
Object {
"test.bar": "<b>hello</b> <script>var malicious = true;</script> world!",
"test.foo": "**bold** *italic* [link](https://mattermost.com/) <br/> [link target blank](!https://mattermost.com/)",
"test.vals": "*Hi* {petName}!",
}
}
onError={[Function]}
onWarn={[Function]}
textComponent={Symbol(react.fragment)}
>
<FormattedMarkdownMessage
defaultMessage="*Hi* {petName}!"
disableLinks={true}
id="test.vals"
values={
Object {
"petName": "http://www.mattermost.com",
}
}
>
<span
dangerouslySetInnerHTML={
Object {
"__html": "<em>Hi</em> http://www.mattermost.com!",
}
}
/>
</FormattedMarkdownMessage>
</IntlProvider>
<div>
<span>
<em>
Hi
</em>
http://www.mattermost.com!
</span>
</div>
`;
exports[`components/FormattedMarkdownMessage should backup to default 1`] = `
<IntlProvider
defaultFormats={Object {}}
defaultLocale="en"
fallbackOnEmptyString={true}
formats={Object {}}
locale="en"
messages={
Object {
"test.bar": "<b>hello</b> <script>var malicious = true;</script> world!",
"test.foo": "**bold** *italic* [link](https://mattermost.com/) <br/> [link target blank](!https://mattermost.com/)",
"test.vals": "*Hi* {petName}!",
}
}
onError={[Function]}
onWarn={[Function]}
textComponent={Symbol(react.fragment)}
>
<FormattedMarkdownMessage
defaultMessage="testing default message"
id="xxx"
>
<span
dangerouslySetInnerHTML={
Object {
"__html": "testing default message",
}
}
/>
</FormattedMarkdownMessage>
</IntlProvider>
<div>
<span>
testing default message
</span>
</div>
`;
exports[`components/FormattedMarkdownMessage should escape non-BR 1`] = `
<IntlProvider
defaultFormats={Object {}}
defaultLocale="en"
fallbackOnEmptyString={true}
formats={Object {}}
locale="en"
messages={
Object {
"test.bar": "<b>hello</b> <script>var malicious = true;</script> world!",
"test.foo": "**bold** *italic* [link](https://mattermost.com/) <br/> [link target blank](!https://mattermost.com/)",
"test.vals": "*Hi* {petName}!",
}
}
onError={[Function]}
onWarn={[Function]}
textComponent={Symbol(react.fragment)}
>
<FormattedMarkdownMessage
defaultMessage=""
id="test.bar"
values={
Object {
"b": [Function],
"script": [Function],
}
}
>
<span
dangerouslySetInnerHTML={
Object {
"__html": "&lt;b&gt;hello&lt;/b&gt; &lt;script&gt;var malicious = true;&lt;/script&gt; world!",
}
}
/>
</FormattedMarkdownMessage>
</IntlProvider>
<div>
<span>
&lt;b&gt;hello&lt;/b&gt; &lt;script&gt;var malicious = true;&lt;/script&gt; world!
</span>
</div>
`;
exports[`components/FormattedMarkdownMessage should render message 1`] = `
<IntlProvider
defaultFormats={Object {}}
defaultLocale="en"
fallbackOnEmptyString={true}
formats={Object {}}
locale="en"
messages={
Object {
"test.bar": "<b>hello</b> <script>var malicious = true;</script> world!",
"test.foo": "**bold** *italic* [link](https://mattermost.com/) <br/> [link target blank](!https://mattermost.com/)",
"test.vals": "*Hi* {petName}!",
}
}
onError={[Function]}
onWarn={[Function]}
textComponent={Symbol(react.fragment)}
>
<FormattedMarkdownMessage
defaultMessage="**bold** *italic* [link](https://mattermost.com/) <br/> [link target blank](!https://mattermost.com/)"
id="test.foo"
>
<span
dangerouslySetInnerHTML={
Object {
"__html": "<strong>bold</strong> <em>italic</em> <a href=\\"https://mattermost.com/\\" rel=\\"noopener noreferrer\\" target=\\"_blank\\">link</a> &lt;br/&gt; <a href=\\"https://mattermost.com/\\" rel=\\"noopener noreferrer\\" target=\\"_blank\\">link target blank</a>",
}
}
/>
</FormattedMarkdownMessage>
</IntlProvider>
<div>
<span>
<strong>
bold
</strong>
<em>
italic
</em>
<a
href="https://mattermost.com/"
rel="noopener noreferrer"
target="_blank"
>
link
</a>
&lt;br/&gt;
<a
href="https://mattermost.com/"
rel="noopener noreferrer"
target="_blank"
>
link target blank
</a>
</span>
</div>
`;
exports[`components/FormattedMarkdownMessage values should work 1`] = `
<IntlProvider
defaultFormats={Object {}}
defaultLocale="en"
fallbackOnEmptyString={true}
formats={Object {}}
locale="en"
messages={
Object {
"test.bar": "<b>hello</b> <script>var malicious = true;</script> world!",
"test.foo": "**bold** *italic* [link](https://mattermost.com/) <br/> [link target blank](!https://mattermost.com/)",
"test.vals": "*Hi* {petName}!",
}
}
onError={[Function]}
onWarn={[Function]}
textComponent={Symbol(react.fragment)}
>
<FormattedMarkdownMessage
defaultMessage="*Hi* {petName}!"
id="test.vals"
values={
Object {
"petName": "sweetie",
}
}
>
<span
dangerouslySetInnerHTML={
Object {
"__html": "<em>Hi</em> sweetie!",
}
}
/>
</FormattedMarkdownMessage>
</IntlProvider>
<div>
<span>
<em>
Hi
</em>
sweetie!
</span>
</div>
`;
@@ -1,362 +1,386 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/GetLinkModal should have called onHide 1`] = `
<Modal
animation={true}
aria-labelledby="getLinkModalLabel"
autoFocus={true}
backdrop={true}
bsClass="modal"
dialogClassName="a11y__modal"
dialogComponentClass={[Function]}
enforceFocus={true}
keyboard={true}
manager={
ModalManager {
"add": [Function],
"containers": Array [],
"data": Array [],
"handleContainerOverflow": true,
"hideSiblingNodes": true,
"isTopModal": [Function],
"modals": Array [],
"remove": [Function],
}
}
onExited={[MockFunction]}
onHide={[Function]}
renderBackdrop={[Function]}
restoreFocus={true}
role="none"
show={true}
exports[`components/GetLinkModal should have called onHide when close button is clicked 1`] = `
<body
class="modal-open"
style="padding-right: 0px;"
>
<ModalHeader
bsClass="modal-header"
closeButton={true}
closeLabel="Close"
id="getLinkModalLabel"
<div
aria-hidden="true"
/>
<div
role="none"
>
<h2
className="modal-title"
>
title
</h2>
</ModalHeader>
<ModalBody
bsClass="modal-body"
componentClass="div"
>
<textarea
className="form-control no-resize min-height"
dir="auto"
id="linkModalTextArea"
onClick={[Function]}
readOnly={true}
value="https://mattermost.com"
<div
class="fade modal-backdrop in"
/>
</ModalBody>
<ModalFooter
bsClass="modal-footer"
componentClass="div"
>
<button
className="btn btn-tertiary"
id="linkModalCloseButton"
onClick={[Function]}
type="button"
<div
aria-labelledby="getLinkModalLabel"
class="fade in modal"
role="dialog"
style="display: block; padding-right: 0px;"
tabindex="-1"
>
<MemoizedFormattedMessage
defaultMessage="Close"
id="get_link.close"
/>
</button>
<button
className="btn btn-primary btn-success pull-left"
data-copy-btn="true"
id="linkModalCopyLink"
onClick={[Function]}
type="button"
>
<i
className="icon icon-check"
/>
<MemoizedFormattedMessage
defaultMessage="Copied"
id="get_link.clipboard"
/>
</button>
</ModalFooter>
</Modal>
<div
class="a11y__modal modal-dialog"
>
<div
class="modal-content"
role="document"
>
<div
class="modal-header"
id="getLinkModalLabel"
>
<button
aria-label="Close"
class="close"
type="button"
>
<span
aria-hidden="true"
>
×
</span>
<span
class="sr-only"
>
Close
</span>
</button>
<h2
class="modal-title"
>
title
</h2>
</div>
<div
class="modal-body"
>
<textarea
class="form-control no-resize min-height"
dir="auto"
id="linkModalTextArea"
readonly=""
>
https://mattermost.com
</textarea>
</div>
<div
class="modal-footer"
>
<button
class="btn btn-tertiary"
id="linkModalCloseButton"
type="button"
>
Close
</button>
<button
class="btn btn-primary btn-success pull-left"
data-copy-btn="true"
id="linkModalCopyLink"
type="button"
>
<i
class="icon icon-check"
/>
Copied
</button>
</div>
</div>
</div>
</div>
</div>
</body>
`;
exports[`components/GetLinkModal should have called onHide 2`] = `
<Modal
animation={true}
aria-labelledby="getLinkModalLabel"
autoFocus={true}
backdrop={true}
bsClass="modal"
dialogClassName="a11y__modal"
dialogComponentClass={[Function]}
enforceFocus={true}
keyboard={true}
manager={
ModalManager {
"add": [Function],
"containers": Array [],
"data": Array [],
"handleContainerOverflow": true,
"hideSiblingNodes": true,
"isTopModal": [Function],
"modals": Array [],
"remove": [Function],
}
}
onExited={[MockFunction]}
onHide={[Function]}
renderBackdrop={[Function]}
restoreFocus={true}
role="none"
show={true}
exports[`components/GetLinkModal should have called onHide when close button is clicked 2`] = `
<body
class="modal-open"
style="padding-right: 0px;"
>
<ModalHeader
bsClass="modal-header"
closeButton={true}
closeLabel="Close"
id="getLinkModalLabel"
<div
aria-hidden="true"
/>
<div
role="none"
>
<h2
className="modal-title"
>
title
</h2>
</ModalHeader>
<ModalBody
bsClass="modal-body"
componentClass="div"
>
<textarea
className="form-control no-resize min-height"
dir="auto"
id="linkModalTextArea"
onClick={[Function]}
readOnly={true}
value="https://mattermost.com"
<div
class="fade modal-backdrop in"
/>
</ModalBody>
<ModalFooter
bsClass="modal-footer"
componentClass="div"
>
<button
className="btn btn-tertiary"
id="linkModalCloseButton"
onClick={[Function]}
type="button"
<div
aria-labelledby="getLinkModalLabel"
class="fade in modal"
role="dialog"
style="display: block; padding-right: 0px;"
tabindex="-1"
>
<MemoizedFormattedMessage
defaultMessage="Close"
id="get_link.close"
/>
</button>
<button
className="btn btn-primary pull-left"
data-copy-btn="true"
id="linkModalCopyLink"
onClick={[Function]}
type="button"
>
<i
className="icon icon-link-variant"
/>
<MemoizedFormattedMessage
defaultMessage="Copy Link"
id="get_link.copy"
/>
</button>
</ModalFooter>
</Modal>
<div
class="a11y__modal modal-dialog"
>
<div
class="modal-content"
role="document"
>
<div
class="modal-header"
id="getLinkModalLabel"
>
<button
aria-label="Close"
class="close"
type="button"
>
<span
aria-hidden="true"
>
×
</span>
<span
class="sr-only"
>
Close
</span>
</button>
<h2
class="modal-title"
>
title
</h2>
</div>
<div
class="modal-body"
>
<textarea
class="form-control no-resize min-height"
dir="auto"
id="linkModalTextArea"
readonly=""
>
https://mattermost.com
</textarea>
</div>
<div
class="modal-footer"
>
<button
class="btn btn-tertiary"
id="linkModalCloseButton"
type="button"
>
Close
</button>
<button
class="btn btn-primary pull-left"
data-copy-btn="true"
id="linkModalCopyLink"
type="button"
>
<i
class="icon icon-link-variant"
/>
Copy Link
</button>
</div>
</div>
</div>
</div>
</div>
</body>
`;
exports[`components/GetLinkModal should match snapshot when all props is set 1`] = `
<Modal
animation={true}
aria-labelledby="getLinkModalLabel"
autoFocus={true}
backdrop={true}
bsClass="modal"
dialogClassName="a11y__modal"
dialogComponentClass={[Function]}
enforceFocus={true}
keyboard={true}
manager={
ModalManager {
"add": [Function],
"containers": Array [],
"data": Array [],
"handleContainerOverflow": true,
"hideSiblingNodes": true,
"isTopModal": [Function],
"modals": Array [],
"remove": [Function],
}
}
onExited={[MockFunction]}
onHide={[Function]}
renderBackdrop={[Function]}
restoreFocus={true}
role="none"
show={true}
<body
class="modal-open"
style="padding-right: 0px;"
>
<ModalHeader
bsClass="modal-header"
closeButton={true}
closeLabel="Close"
id="getLinkModalLabel"
<div
aria-hidden="true"
/>
<div
role="none"
>
<h2
className="modal-title"
>
title
</h2>
</ModalHeader>
<ModalBody
bsClass="modal-body"
componentClass="div"
>
<p>
help text
<br />
<br />
</p>
<textarea
className="form-control no-resize min-height"
dir="auto"
id="linkModalTextArea"
onClick={[Function]}
readOnly={true}
value="https://mattermost.com"
<div
class="fade modal-backdrop in"
/>
</ModalBody>
<ModalFooter
bsClass="modal-footer"
componentClass="div"
>
<button
className="btn btn-tertiary"
id="linkModalCloseButton"
onClick={[Function]}
type="button"
<div
aria-labelledby="getLinkModalLabel"
class="fade in modal"
role="dialog"
style="display: block; padding-right: 0px;"
tabindex="-1"
>
<MemoizedFormattedMessage
defaultMessage="Close"
id="get_link.close"
/>
</button>
<button
className="btn btn-primary pull-left"
data-copy-btn="true"
id="linkModalCopyLink"
onClick={[Function]}
type="button"
>
<i
className="icon icon-link-variant"
/>
<MemoizedFormattedMessage
defaultMessage="Copy Link"
id="get_link.copy"
/>
</button>
</ModalFooter>
</Modal>
<div
class="a11y__modal modal-dialog"
>
<div
class="modal-content"
role="document"
>
<div
class="modal-header"
id="getLinkModalLabel"
>
<button
aria-label="Close"
class="close"
type="button"
>
<span
aria-hidden="true"
>
×
</span>
<span
class="sr-only"
>
Close
</span>
</button>
<h2
class="modal-title"
>
title
</h2>
</div>
<div
class="modal-body"
>
<p>
help text
<br />
<br />
</p>
<textarea
class="form-control no-resize min-height"
dir="auto"
id="linkModalTextArea"
readonly=""
>
https://mattermost.com
</textarea>
</div>
<div
class="modal-footer"
>
<button
class="btn btn-tertiary"
id="linkModalCloseButton"
type="button"
>
Close
</button>
<button
class="btn btn-primary pull-left"
data-copy-btn="true"
id="linkModalCopyLink"
type="button"
>
<i
class="icon icon-link-variant"
/>
Copy Link
</button>
</div>
</div>
</div>
</div>
</div>
</body>
`;
exports[`components/GetLinkModal should match snapshot when helpText is not set 1`] = `
<Modal
animation={true}
aria-labelledby="getLinkModalLabel"
autoFocus={true}
backdrop={true}
bsClass="modal"
dialogClassName="a11y__modal"
dialogComponentClass={[Function]}
enforceFocus={true}
keyboard={true}
manager={
ModalManager {
"add": [Function],
"containers": Array [],
"data": Array [],
"handleContainerOverflow": true,
"hideSiblingNodes": true,
"isTopModal": [Function],
"modals": Array [],
"remove": [Function],
}
}
onExited={[MockFunction]}
onHide={[Function]}
renderBackdrop={[Function]}
restoreFocus={true}
role="none"
show={true}
<body
class="modal-open"
style="padding-right: 0px;"
>
<ModalHeader
bsClass="modal-header"
closeButton={true}
closeLabel="Close"
id="getLinkModalLabel"
<div
aria-hidden="true"
/>
<div
role="none"
>
<h2
className="modal-title"
>
title
</h2>
</ModalHeader>
<ModalBody
bsClass="modal-body"
componentClass="div"
>
<textarea
className="form-control no-resize min-height"
dir="auto"
id="linkModalTextArea"
onClick={[Function]}
readOnly={true}
value="https://mattermost.com"
<div
class="fade modal-backdrop in"
/>
</ModalBody>
<ModalFooter
bsClass="modal-footer"
componentClass="div"
>
<button
className="btn btn-tertiary"
id="linkModalCloseButton"
onClick={[Function]}
type="button"
<div
aria-labelledby="getLinkModalLabel"
class="fade in modal"
role="dialog"
style="display: block; padding-right: 0px;"
tabindex="-1"
>
<MemoizedFormattedMessage
defaultMessage="Close"
id="get_link.close"
/>
</button>
<button
className="btn btn-primary pull-left"
data-copy-btn="true"
id="linkModalCopyLink"
onClick={[Function]}
type="button"
>
<i
className="icon icon-link-variant"
/>
<MemoizedFormattedMessage
defaultMessage="Copy Link"
id="get_link.copy"
/>
</button>
</ModalFooter>
</Modal>
<div
class="a11y__modal modal-dialog"
>
<div
class="modal-content"
role="document"
>
<div
class="modal-header"
id="getLinkModalLabel"
>
<button
aria-label="Close"
class="close"
type="button"
>
<span
aria-hidden="true"
>
×
</span>
<span
class="sr-only"
>
Close
</span>
</button>
<h2
class="modal-title"
>
title
</h2>
</div>
<div
class="modal-body"
>
<textarea
class="form-control no-resize min-height"
dir="auto"
id="linkModalTextArea"
readonly=""
>
https://mattermost.com
</textarea>
</div>
<div
class="modal-footer"
>
<button
class="btn btn-tertiary"
id="linkModalCloseButton"
type="button"
>
Close
</button>
<button
class="btn btn-primary pull-left"
data-copy-btn="true"
id="linkModalCopyLink"
type="button"
>
<i
class="icon icon-link-variant"
/>
Copy Link
</button>
</div>
</div>
</div>
</div>
</div>
</body>
`;
@@ -1,225 +1,251 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/ListModal should match snapshot 1`] = `
<div>
<Modal
animation={true}
autoFocus={true}
backdrop={true}
bsClass="modal"
dialogClassName="a11y__modal more-modal more-modal--action"
dialogComponentClass={[Function]}
enforceFocus={true}
keyboard={true}
manager={
ModalManager {
"add": [Function],
"containers": Array [],
"data": Array [],
"handleContainerOverflow": true,
"hideSiblingNodes": true,
"isTopModal": [Function],
"modals": Array [],
"remove": [Function],
}
}
onExited={[Function]}
onHide={[Function]}
renderBackdrop={[Function]}
restoreFocus={true}
show={true}
<body
class="modal-open"
style="padding-right: 0px;"
>
<div
aria-hidden="true"
>
<ModalHeader
bsClass="modal-header"
closeButton={true}
closeLabel="Close"
>
<ModalTitle
bsClass="modal-title"
componentClass="h1"
>
<span
className="name"
>
list modal
</span>
</ModalTitle>
</ModalHeader>
<ModalBody
bsClass="modal-body"
componentClass="div"
<div />
</div>
<div
role="dialog"
>
<div
class="fade modal-backdrop in"
/>
<div
class="fade in modal"
role="dialog"
style="display: block; padding-right: 0px;"
tabindex="-1"
>
<div
className="filtered-user-list"
class="a11y__modal more-modal more-modal--action modal-dialog"
>
<div
className="filter-row"
class="modal-content"
role="document"
>
<div
className="col-xs-12"
class="modal-header"
>
<label
className="hidden-label"
htmlFor="searchUsersInput"
<button
aria-label="Close"
class="close"
type="button"
>
search for name
</label>
<input
className="form-control filter-textbox"
id="searchUsersInput"
onChange={[Function]}
placeholder="search for name"
/>
<span
aria-hidden="true"
>
×
</span>
<span
class="sr-only"
>
Close
</span>
</button>
<h1
class="modal-title"
>
<span
class="name"
>
list modal
</span>
</h1>
</div>
<div
className="col-sm-12"
class="modal-body"
>
<span
className="member-count pull-left"
<div
class="filtered-user-list"
>
<MemoizedFormattedMessage
defaultMessage="{startCount, number} - {endCount, number} of {total, number} total"
id="list_modal.paginatorCount"
values={
Object {
"endCount": 0,
"startCount": 0,
"total": 0,
}
}
/>
</span>
</div>
</div>
<div
className="more-modal__list"
>
<div>
<div>
<LoadingScreen
key="loading"
position="absolute"
<div
class="filter-row"
>
<div
class="col-xs-12"
>
<label
class="hidden-label"
for="searchUsersInput"
>
search for name
</label>
<input
class="form-control filter-textbox"
id="searchUsersInput"
placeholder="search for name"
/>
</div>
<div
class="col-sm-12"
>
<span
class="member-count pull-left"
>
1 - 2 of 3 total
</span>
</div>
</div>
<div
class="more-modal__list"
>
<div>
<div
class="item"
data-testid="item-123"
>
123
</div>
<div
class="item"
data-testid="item-234"
>
234
</div>
</div>
</div>
<div
class="filter-controls"
/>
</div>
</div>
</div>
<div
className="filter-controls"
/>
</div>
</ModalBody>
</Modal>
</div>
</div>
</div>
</body>
`;
exports[`components/ListModal should match snapshot with title bar button 1`] = `
<div>
<Modal
animation={true}
autoFocus={true}
backdrop={true}
bsClass="modal"
dialogClassName="a11y__modal more-modal more-modal--action"
dialogComponentClass={[Function]}
enforceFocus={true}
keyboard={true}
manager={
ModalManager {
"add": [Function],
"containers": Array [],
"data": Array [],
"handleContainerOverflow": true,
"hideSiblingNodes": true,
"isTopModal": [Function],
"modals": Array [],
"remove": [Function],
}
}
onExited={[Function]}
onHide={[Function]}
renderBackdrop={[Function]}
restoreFocus={true}
show={true}
<body
class="modal-open"
style="padding-right: 0px;"
>
<div
aria-hidden="true"
>
<ModalHeader
bsClass="modal-header"
closeButton={true}
closeLabel="Close"
>
<ModalTitle
bsClass="modal-title"
componentClass="h1"
>
<span
className="name"
>
list modal
</span>
</ModalTitle>
</ModalHeader>
<ModalBody
bsClass="modal-body"
componentClass="div"
<div />
</div>
<div
role="dialog"
>
<div
class="fade modal-backdrop in"
/>
<div
class="fade in modal"
role="dialog"
style="display: block; padding-right: 0px;"
tabindex="-1"
>
<div
className="filtered-user-list"
class="a11y__modal more-modal more-modal--action modal-dialog"
>
<div
className="filter-row"
class="modal-content"
role="document"
>
<div
className="col-xs-12"
class="modal-header"
>
<label
className="hidden-label"
htmlFor="searchUsersInput"
<button
aria-label="Close"
class="close"
type="button"
>
search for name
</label>
<input
className="form-control filter-textbox"
id="searchUsersInput"
onChange={[Function]}
placeholder="search for name"
/>
<span
aria-hidden="true"
>
×
</span>
<span
class="sr-only"
>
Close
</span>
</button>
<h1
class="modal-title"
>
<span
class="name"
>
list modal
</span>
</h1>
<a
class="btn btn-md btn-primary"
href="#"
>
Add Foo
</a>
</div>
<div
className="col-sm-12"
class="modal-body"
>
<span
className="member-count pull-left"
<div
class="filtered-user-list"
>
<MemoizedFormattedMessage
defaultMessage="{startCount, number} - {endCount, number} of {total, number} total"
id="list_modal.paginatorCount"
values={
Object {
"endCount": 0,
"startCount": 0,
"total": 0,
}
}
/>
</span>
</div>
</div>
<div
className="more-modal__list"
>
<div>
<div>
<LoadingScreen
key="loading"
position="absolute"
<div
class="filter-row"
>
<div
class="col-xs-12"
>
<label
class="hidden-label"
for="searchUsersInput"
>
search for name
</label>
<input
class="form-control filter-textbox"
id="searchUsersInput"
placeholder="search for name"
/>
</div>
<div
class="col-sm-12"
>
<span
class="member-count pull-left"
>
1 - 2 of 3 total
</span>
</div>
</div>
<div
class="more-modal__list"
>
<div>
<div
class="item"
data-testid="item-123"
>
123
</div>
<div
class="item"
data-testid="item-234"
>
234
</div>
</div>
</div>
<div
class="filter-controls"
/>
</div>
</div>
</div>
<div
className="filter-controls"
/>
</div>
</ModalBody>
</Modal>
</div>
</div>
</div>
</body>
`;
@@ -1,29 +1,51 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/LoadingImagePreview should match snapshot 1`] = `
<div
className="view-image__loading"
>
<Memo(LoadingSpinner) />
<span
className="loader-percent"
exports[`components/LoadingImagePreview should match snapshot with progress 50% 1`] = `
<div>
<div
class="view-image__loading"
>
Loading
50%
</span>
<span
class="LoadingSpinner"
data-testid="loadingSpinner"
id="loadingSpinner"
>
<span
class="fa fa-spinner fa-fw fa-pulse spinner"
title="Loading Icon"
/>
</span>
<span
class="loader-percent"
>
Loading
50%
</span>
</div>
</div>
`;
exports[`components/LoadingImagePreview should match snapshot 2`] = `
<div
className="view-image__loading"
>
<Memo(LoadingSpinner) />
<span
className="loader-percent"
exports[`components/LoadingImagePreview should match snapshot with progress 90% 1`] = `
<div>
<div
class="view-image__loading"
>
Loading
90%
</span>
<span
class="LoadingSpinner"
data-testid="loadingSpinner"
id="loadingSpinner"
>
<span
class="fa fa-spinner fa-fw fa-pulse spinner"
title="Loading Icon"
/>
</span>
<span
class="loader-percent"
>
Loading
90%
</span>
</div>
</div>
`;
@@ -1,34 +1,55 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`component/PDFPreview should match snapshot, loading 1`] = `
<div
className="view-image__loading"
>
<Memo(LoadingSpinner) />
<div>
<div
class="view-image__loading"
>
<span
class="LoadingSpinner"
data-testid="loadingSpinner"
id="loadingSpinner"
>
<span
class="fa fa-spinner fa-fw fa-pulse spinner"
title="Loading Icon"
/>
</span>
</div>
</div>
`;
exports[`component/PDFPreview should match snapshot, not successful 1`] = `
<Connect(Component)
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "pdf",
"has_preview_image": true,
"height": 200,
"id": "file_info_id",
"mime_type": "mime_type",
"name": "name",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
fileUrl="https://pre-release.mattermost.com/api/v4/files/ips59w4w9jnfbrs3o94m1dbdie"
/>
<div>
<div
class="file-details__container"
>
<a
class="file-details__preview"
href="https://pre-release.mattermost.com/api/v4/files/ips59w4w9jnfbrs3o94m1dbdie"
>
<span
class="file-details__preview-helper"
/>
<img
alt="file preview"
src=""
/>
</a>
<div
class="file-details"
>
<div
class="file-details__name"
>
name
</div>
<div
class="file-details__info"
>
File type PDF, Size 1B
</div>
</div>
</div>
</div>
`;
@@ -1,79 +1,81 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/ChannelInfoModal should match snapshot 1`] = `
<Modal
animation={true}
aria-labelledby="postDeletedModalLabel"
autoFocus={true}
backdrop={true}
bsClass="modal"
data-testid="postDeletedModal"
dialogClassName="a11y__modal"
dialogComponentClass={[Function]}
enforceFocus={true}
keyboard={true}
manager={
ModalManager {
"add": [Function],
"containers": Array [],
"data": Array [],
"handleContainerOverflow": true,
"hideSiblingNodes": true,
"isTopModal": [Function],
"modals": Array [],
"remove": [Function],
}
}
onExited={[MockFunction]}
onHide={[Function]}
renderBackdrop={[Function]}
restoreFocus={true}
role="none"
show={true}
exports[`components/PostDeletedModal should match snapshot 1`] = `
<body
class="modal-open"
style="padding-right: 0px;"
>
<ModalHeader
bsClass="modal-header"
closeButton={true}
closeLabel="Close"
<div
aria-hidden="true"
/>
<div
role="none"
>
<ModalTitle
bsClass="modal-title"
componentClass="h1"
id="postDeletedModalLabel"
<div
class="fade modal-backdrop in"
/>
<div
aria-labelledby="postDeletedModalLabel"
class="fade in modal"
data-testid="postDeletedModal"
role="dialog"
style="display: block; padding-right: 0px;"
tabindex="-1"
>
<MemoizedFormattedMessage
defaultMessage="Comment could not be posted"
id="post_delete.notPosted"
/>
</ModalTitle>
</ModalHeader>
<ModalBody
bsClass="modal-body"
componentClass="div"
>
<p>
<MemoizedFormattedMessage
defaultMessage="Someone deleted the message on which you tried to post a comment."
id="post_delete.someone"
/>
</p>
</ModalBody>
<ModalFooter
bsClass="modal-footer"
componentClass="div"
>
<button
autoFocus={true}
className="btn btn-primary"
data-testid="postDeletedModalOkButton"
onClick={[Function]}
type="button"
>
<MemoizedFormattedMessage
defaultMessage="Okay"
id="post_delete.okay"
/>
</button>
</ModalFooter>
</Modal>
<div
class="a11y__modal modal-dialog"
>
<div
class="modal-content"
role="document"
>
<div
class="modal-header"
>
<button
aria-label="Close"
class="close"
type="button"
>
<span
aria-hidden="true"
>
×
</span>
<span
class="sr-only"
>
Close
</span>
</button>
<h1
class="modal-title"
id="postDeletedModalLabel"
>
Comment could not be posted
</h1>
</div>
<div
class="modal-body"
>
<p>
Someone deleted the message on which you tried to post a comment.
</p>
</div>
<div
class="modal-footer"
>
<button
class="btn btn-primary"
data-testid="postDeletedModalOkButton"
type="button"
>
Okay
</button>
</div>
</div>
</div>
</div>
</div>
</body>
`;
@@ -9,7 +9,7 @@ import type {ContentFlaggingNotificationSettings} from '@mattermost/types/config
import ContentFlaggingNotificationSettingsSection from './notification_settings';
const renderWithIntl = (component: React.ReactElement) => {
const renderWithContext = (component: React.ReactElement) => {
return render(
<IntlProvider locale='en'>
{component}
@@ -36,14 +36,14 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
});
test('should render section title and description', () => {
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
expect(screen.getByText('Notification Settings')).toBeInTheDocument();
expect(screen.getByText('Choose who receives notifications from the System bot when content is flagged and reviewed')).toBeInTheDocument();
});
test('should render all notification setting sections', () => {
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
expect(screen.getByText('Notify when content is flagged')).toBeInTheDocument();
expect(screen.getByText('Notify when a reviewer is assigned')).toBeInTheDocument();
@@ -52,7 +52,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
});
test('should render all checkbox labels', () => {
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
// Should have multiple instances of these labels across different sections
expect(screen.getAllByText('Reviewer(s)')).toHaveLength(4);
@@ -61,7 +61,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
});
test('should set correct default checked values for checkboxes', () => {
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
expect(screen.getByTestId('flagged_reviewers')).toBeChecked();
expect(screen.getByTestId('flagged_author')).not.toBeChecked();
@@ -81,7 +81,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
});
test('should handle checkbox change when adding a target', () => {
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
const flaggedAuthorsCheckbox = screen.getByTestId('flagged_author');
fireEvent.click(flaggedAuthorsCheckbox);
@@ -97,7 +97,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
});
test('should handle checkbox change when removing a target', () => {
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
const removedAuthorCheckbox = screen.getByTestId('removed_author');
fireEvent.click(removedAuthorCheckbox);
@@ -113,14 +113,14 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
});
test('should disable flagged_reviewers checkbox', () => {
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
const flaggedReviewersCheckbox = screen.getByTestId('flagged_reviewers');
expect(flaggedReviewersCheckbox).toBeDisabled();
});
test('should not disable other checkboxes', () => {
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
expect(screen.getByTestId('flagged_author')).not.toBeDisabled();
expect(screen.getByTestId('assigned_reviewers')).not.toBeDisabled();
@@ -138,7 +138,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
value: {} as ContentFlaggingNotificationSettings,
};
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...propsWithoutMapping}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...propsWithoutMapping}/>);
const flaggedReviewersCheckbox = screen.getByTestId('flagged_reviewers');
fireEvent.click(flaggedReviewersCheckbox);
@@ -165,7 +165,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
} as ContentFlaggingNotificationSettings,
};
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...propsWithPartialMapping}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...propsWithPartialMapping}/>);
const assignedReviewersCheckbox = screen.getByTestId('assigned_reviewers');
fireEvent.click(assignedReviewersCheckbox);
@@ -179,7 +179,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
});
test('should not add duplicate targets', () => {
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
// Try to add 'reviewers' to flagged again (it's already there)
const flaggedReviewersCheckbox = screen.getByTestId('flagged_reviewers');
@@ -197,7 +197,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
});
test('should handle multiple checkbox changes correctly', () => {
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
// First change: add author to flagged
const flaggedAuthorsCheckbox = screen.getByTestId('flagged_author');
@@ -229,7 +229,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
});
test('should handle unchecking and rechecking the same checkbox', () => {
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...defaultProps}/>);
const removedAuthorCheckbox = screen.getByTestId('removed_author');
@@ -271,7 +271,7 @@ describe('ContentFlaggingNotificationSettingsSection', () => {
} as unknown as ContentFlaggingNotificationSettings,
};
renderWithIntl(<ContentFlaggingNotificationSettingsSection {...propsWithEmptyArrays}/>);
renderWithContext(<ContentFlaggingNotificationSettingsSection {...propsWithEmptyArrays}/>);
// All checkboxes should be unchecked
expect(screen.getByTestId('flagged_reviewers')).not.toBeChecked();
@@ -1,108 +1,134 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`Components/AlertBanner should match snapshot 1`] = `
<div
className="AlertBanner info AlertBanner--sys"
>
<div>
<div
className="AlertBanner__icon"
>
<InformationOutlineIcon
size={24}
/>
</div>
<div
className="AlertBanner__body"
class="AlertBanner info AlertBanner--sys"
>
<div
className="AlertBanner__title"
class="AlertBanner__icon"
>
title
<svg
fill="currentColor"
height="24"
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z"
/>
</svg>
</div>
<div
className="AlertBanner__message"
class="AlertBanner__body"
>
message
<div
class="AlertBanner__title"
>
title
</div>
<div
class="AlertBanner__message"
>
message
</div>
</div>
</div>
</div>
`;
exports[`Components/AlertBanner should match snapshot for app variant 1`] = `
<div
className="AlertBanner info AlertBanner--app"
>
<div>
<div
className="AlertBanner__icon"
>
<InformationOutlineIcon
size={24}
/>
</div>
<div
className="AlertBanner__body"
class="AlertBanner info AlertBanner--app"
>
<div
className="AlertBanner__title"
class="AlertBanner__icon"
>
title
<svg
fill="currentColor"
height="24"
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z"
/>
</svg>
</div>
<div
className="AlertBanner__message"
class="AlertBanner__body"
>
message
<div
class="AlertBanner__title"
>
title
</div>
<div
class="AlertBanner__message"
>
message
</div>
</div>
</div>
</div>
`;
exports[`Components/AlertBanner should match snapshot when buttons are passed 1`] = `
<div
className="AlertBanner info AlertBanner--app"
>
<div>
<div
className="AlertBanner__body"
class="AlertBanner info AlertBanner--app"
>
<div
className="AlertBanner__title"
>
title
</div>
<div
className="AlertBanner__message"
>
message
</div>
<div
className="AlertBanner__actionButtons"
class="AlertBanner__body"
>
<div
id="actionButtonLeft"
/>
class="AlertBanner__title"
>
title
</div>
<div
id="actionButtonRight"
/>
class="AlertBanner__message"
>
message
</div>
<div
class="AlertBanner__actionButtons"
>
<div
id="actionButtonLeft"
/>
<div
id="actionButtonRight"
/>
</div>
</div>
</div>
</div>
`;
exports[`Components/AlertBanner should match snapshot when icon disabled 1`] = `
<div
className="AlertBanner info AlertBanner--app"
>
<div>
<div
className="AlertBanner__body"
class="AlertBanner info AlertBanner--app"
>
<div
className="AlertBanner__title"
class="AlertBanner__body"
>
title
</div>
<div
className="AlertBanner__message"
>
message
<div
class="AlertBanner__title"
>
title
</div>
<div
class="AlertBanner__message"
>
message
</div>
</div>
</div>
</div>
@@ -1,14 +1,15 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import AlertBanner from 'components/alert_banner';
import {renderWithContext} from 'tests/react_testing_utils';
import AlertBanner from '.';
describe('Components/AlertBanner', () => {
test('should match snapshot', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<AlertBanner
mode='info'
message='message'
@@ -16,11 +17,11 @@ describe('Components/AlertBanner', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot for app variant', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<AlertBanner
mode='info'
message='message'
@@ -29,11 +30,11 @@ describe('Components/AlertBanner', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when icon disabled', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<AlertBanner
hideIcon={true}
mode='info'
@@ -43,11 +44,11 @@ describe('Components/AlertBanner', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when buttons are passed', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<AlertBanner
hideIcon={true}
mode='info'
@@ -59,6 +60,6 @@ describe('Components/AlertBanner', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,102 +1,73 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/analytics/doughnut_chart.tsx should match snapshot, loaded with data 1`] = `
<DoughnutChart
data={
Object {
"datasets": Array [
Object {
"data": Array [
1,
2,
3,
],
},
],
}
}
height={400}
title="Test"
width={600}
>
<div>
<div
className="col-sm-6"
class="col-sm-6"
>
<div
className="total-count"
class="total-count"
>
<div
className="title"
class="title"
>
Test
</div>
<div
className="content"
class="content"
>
<canvas
height={400}
width={600}
height="400"
width="600"
/>
</div>
</div>
</div>
</DoughnutChart>
</div>
`;
exports[`components/analytics/doughnut_chart.tsx should match snapshot, loaded without data 1`] = `
<DoughnutChart
height={400}
title="Test"
width={600}
>
<div>
<div
className="col-sm-6"
class="col-sm-6"
>
<div
className="total-count"
class="total-count"
>
<div
className="title"
class="title"
>
Test
</div>
<div
className="content"
class="content"
>
<FormattedMessage
defaultMessage="Loading..."
id="analytics.chart.loading"
>
<span>
Loading...
</span>
</FormattedMessage>
Loading...
</div>
</div>
</div>
</div>
`;
exports[`components/analytics/doughnut_chart.tsx should match snapshot, on loading 1`] = `
<div>
<div
class="col-sm-6"
>
<div
class="total-count"
>
<div
class="title"
>
Test
</div>
<div
class="content"
>
Loading...
</div>
</div>
</div>
</DoughnutChart>
`;
exports[`components/analytics/doughnut_chart.tsx should match snapshot, on loading 1`] = `
<div
className="col-sm-6"
>
<div
className="total-count"
>
<div
className="title"
>
Test
</div>
<div
className="content"
>
<MemoizedFormattedMessage
defaultMessage="Loading..."
id="analytics.chart.loading"
/>
</div>
</div>
</div>
`;
@@ -1,82 +1,77 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/analytics/line_chart.tsx should match snapshot, loaded with data 1`] = `
<div
className="col-sm-12"
>
<div>
<div
className="total-count by-day"
class="col-sm-12"
>
<div
className="title"
class="total-count by-day"
>
Test
</div>
<div
className="content"
>
<canvas
data-labels={
Array [
"test1",
"test2",
"test3",
]
}
data-testid="test"
height={400}
width={600}
/>
<div
class="title"
>
Test
</div>
<div
class="content"
>
<canvas
data-labels="test1,test2,test3"
data-testid="test"
height="0"
style="display: block; box-sizing: border-box; height: 0px; width: 0px;"
width="0"
/>
</div>
</div>
</div>
</div>
`;
exports[`components/analytics/line_chart.tsx should match snapshot, loaded without data 1`] = `
<div
className="col-sm-12"
>
<div>
<div
className="total-count by-day"
class="col-sm-12"
>
<div
className="title"
class="total-count by-day"
>
Test
</div>
<div
className="content"
>
<h5>
<MemoizedFormattedMessage
defaultMessage="Not enough data for a meaningful representation."
id="analytics.chart.meaningful"
/>
</h5>
<div
class="title"
>
Test
</div>
<div
class="content"
>
<h5>
Not enough data for a meaningful representation.
</h5>
</div>
</div>
</div>
</div>
`;
exports[`components/analytics/line_chart.tsx should match snapshot, on loading 1`] = `
<div
className="col-sm-12"
>
<div>
<div
className="total-count by-day"
class="col-sm-12"
>
<div
className="title"
class="total-count by-day"
>
Test
</div>
<div
className="content"
>
<MemoizedFormattedMessage
defaultMessage="Loading..."
id="analytics.chart.loading"
/>
<div
class="title"
>
Test
</div>
<div
class="content"
>
Loading...
</div>
</div>
</div>
</div>
@@ -1,78 +1,81 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/analytics/statistic_count.tsx should match snapshot, loaded 1`] = `
<div
className="grid-statistics__card"
>
<div>
<div
className="total-count"
class="grid-statistics__card"
>
<div
className="title"
data-testid="undefinedTitle"
class="total-count"
>
Test
<i
className="fa test-icon"
/>
</div>
<div
className="content"
>
4
<div
class="title"
data-testid="undefinedTitle"
>
Test
<i
class="fa test-icon"
/>
</div>
<div
class="content"
>
4
</div>
</div>
</div>
</div>
`;
exports[`components/analytics/statistic_count.tsx should match snapshot, loaded with zero value 1`] = `
<div
className="grid-statistics__card"
>
<div>
<div
className="total-count"
class="grid-statistics__card"
>
<div
className="title"
data-testid="undefinedTitle"
class="total-count"
>
Test Zero
<i
className="fa test-icon"
/>
</div>
<div
className="content"
>
0
<div
class="title"
data-testid="undefinedTitle"
>
Test Zero
<i
class="fa test-icon"
/>
</div>
<div
class="content"
>
0
</div>
</div>
</div>
</div>
`;
exports[`components/analytics/statistic_count.tsx should match snapshot, on loading 1`] = `
<div
className="grid-statistics__card"
>
<div>
<div
className="total-count"
class="grid-statistics__card"
>
<div
className="title"
data-testid="undefinedTitle"
class="total-count"
>
Test
<i
className="fa test-icon"
/>
</div>
<div
className="content"
>
<MemoizedFormattedMessage
defaultMessage="Loading..."
id="analytics.chart.loading"
/>
<div
class="title"
data-testid="undefinedTitle"
>
Test
<i
class="fa test-icon"
/>
</div>
<div
class="content"
>
Loading...
</div>
</div>
</div>
</div>
@@ -1,83 +1,75 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/analytics/table_chart.tsx should match snapshot, loaded with data 1`] = `
<div
className="col-sm-6"
>
<div>
<div
className="total-count recent-active-users"
class="col-sm-6"
>
<div
className="title"
class="total-count recent-active-users"
>
Test
</div>
<div
className="content"
>
<table>
<tbody>
<tr
key="table-entry-test1"
>
<td>
<WithTooltip
title="test-tip1"
>
<div
class="title"
>
Test
</div>
<div
class="content"
>
<table>
<tbody>
<tr>
<td>
<span>
test1
</span>
</WithTooltip>
</td>
<td>
<p>
test-value1
</p>
</td>
</tr>
<tr
key="table-entry-test2"
>
<td>
<WithTooltip
title="test-tip2"
>
</td>
<td>
<p>
test-value1
</p>
</td>
</tr>
<tr>
<td>
<span>
test2
</span>
</WithTooltip>
</td>
<td>
<p>
test-value2
</p>
</td>
</tr>
</tbody>
</table>
</td>
<td>
<p>
test-value2
</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
`;
exports[`components/analytics/table_chart.tsx should match snapshot, loaded without data 1`] = `
<div
className="col-sm-6"
>
<div>
<div
className="total-count recent-active-users"
class="col-sm-6"
>
<div
className="title"
class="total-count recent-active-users"
>
Test
</div>
<div
className="content"
>
<table>
<tbody />
</table>
<div
class="title"
>
Test
</div>
<div
class="content"
>
<table>
<tbody />
</table>
</div>
</div>
</div>
</div>
@@ -2,18 +2,17 @@
// See LICENSE.txt for license information.
import type {ChartData} from 'chart.js';
import {shallow, mount} from 'enzyme';
import React from 'react';
import DoughnutChart from 'components/analytics/doughnut_chart';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import {renderWithContext} from 'tests/react_testing_utils';
jest.mock('chart.js');
describe('components/analytics/doughnut_chart.tsx', () => {
test('should match snapshot, on loading', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<DoughnutChart
title='Test'
height={400}
@@ -21,14 +20,14 @@ describe('components/analytics/doughnut_chart.tsx', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, loaded without data', () => {
const Chart = jest.requireMock('chart.js');
const data: ChartData | undefined = undefined;
const wrapper = mountWithIntl(
const {container} = renderWithContext(
<DoughnutChart
title='Test'
height={400}
@@ -38,7 +37,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
);
expect(Chart).not.toHaveBeenCalled();
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, loaded with data', () => {
@@ -49,7 +48,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
],
};
const wrapper = mount(
const {container} = renderWithContext(
<DoughnutChart
title='Test'
height={400}
@@ -59,7 +58,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
);
expect(Chart).toHaveBeenCalledWith(expect.anything(), {data, options: {}, type: 'doughnut'});
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should create and destroy the chart on mount and unmount with data', () => {
@@ -72,7 +71,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
labels: ['test1', 'test2', 'test3'],
};
const wrapper = mountWithIntl(
const {unmount} = renderWithContext(
<DoughnutChart
title='Test'
height={400}
@@ -82,7 +81,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
);
expect(Chart).toHaveBeenCalled();
wrapper.unmount();
unmount();
});
test('should update the chart on data change', () => {
@@ -102,7 +101,7 @@ describe('components/analytics/doughnut_chart.tsx', () => {
labels: ['test1', 'test2', 'test3', 'test4'],
};
const wrapper = mountWithIntl(
const {rerender} = renderWithContext(
<DoughnutChart
title='Test'
height={400}
@@ -114,10 +113,24 @@ describe('components/analytics/doughnut_chart.tsx', () => {
expect(Chart).toHaveBeenCalled();
expect(Chart.mock.instances[0].update).not.toHaveBeenCalled();
wrapper.setProps({title: 'new title'});
rerender(
<DoughnutChart
title='new title'
height={400}
width={600}
data={oldData}
/>,
);
expect(Chart.mock.instances[0].update).not.toHaveBeenCalled();
wrapper.setProps({data: newData});
rerender(
<DoughnutChart
title='new title'
height={400}
width={600}
data={newData}
/>,
);
expect(Chart.mock.instances[0].update).toHaveBeenCalled();
});
});
@@ -1,14 +1,15 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import LineChart from 'components/analytics/line_chart';
import {renderWithContext} from 'tests/react_testing_utils';
import LineChart from './line_chart';
describe('components/analytics/line_chart.tsx', () => {
test('should match snapshot, on loading', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<LineChart
id='test'
title='Test'
@@ -17,7 +18,7 @@ describe('components/analytics/line_chart.tsx', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, loaded without data', () => {
@@ -26,7 +27,7 @@ describe('components/analytics/line_chart.tsx', () => {
labels: [],
};
const wrapper = shallow(
const {container} = renderWithContext(
<LineChart
id='test'
title='Test'
@@ -36,7 +37,7 @@ describe('components/analytics/line_chart.tsx', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, loaded with data', () => {
@@ -47,7 +48,7 @@ describe('components/analytics/line_chart.tsx', () => {
labels: ['test1', 'test2', 'test3'],
};
const wrapper = shallow(
const {container} = renderWithContext(
<LineChart
id='test'
title='Test'
@@ -57,6 +58,6 @@ describe('components/analytics/line_chart.tsx', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,25 +1,26 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import StatisticCount from 'components/analytics/statistic_count';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import StatisticCount from './statistic_count';
describe('components/analytics/statistic_count.tsx', () => {
test('should match snapshot, on loading', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<StatisticCount
title='Test'
icon='test-icon'
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, loaded', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<StatisticCount
title='Test'
icon='test-icon'
@@ -27,11 +28,11 @@ describe('components/analytics/statistic_count.tsx', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, loaded with zero value', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<StatisticCount
title='Test Zero'
icon='test-icon'
@@ -39,12 +40,12 @@ describe('components/analytics/statistic_count.tsx', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should apply formatter function when provided', () => {
const mockFormatter = (value: number) => `${value}%`;
const wrapper = shallow(
renderWithContext(
<StatisticCount
title='Test'
icon='test-icon'
@@ -54,6 +55,6 @@ describe('components/analytics/statistic_count.tsx', () => {
/>,
);
expect(wrapper.find('[data-testid="test-stat"]').text()).toBe('42%');
expect(screen.getByTestId('test-stat')).toHaveTextContent('42%');
});
});
@@ -1,24 +1,25 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import TableChart from 'components/analytics/table_chart';
import type {TableItem} from 'components/analytics/table_chart';
import {render} from 'tests/react_testing_utils';
import type {TableItem} from './table_chart';
import TableChart from './table_chart';
describe('components/analytics/table_chart.tsx', () => {
test('should match snapshot, loaded without data', () => {
const data: TableItem[] = [];
const wrapper = shallow(
const {container} = render(
<TableChart
title='Test'
data={data}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, loaded with data', () => {
@@ -27,13 +28,13 @@ describe('components/analytics/table_chart.tsx', () => {
{name: 'test2', tip: 'test-tip2', value: <p>{'test-value2'}</p>},
];
const wrapper = shallow(
const {container} = render(
<TableChart
title='Test'
data={data}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,232 +1,186 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/AtMention should match snapshot when mentioning a group followed by punctuation 1`] = `
<Fragment>
<UserGroupPopoverController
group={
Object {
"allow_reference": true,
"create_at": 1,
"delete_at": 0,
"description": "",
"display_name": "group_display_name",
"has_syncables": false,
"id": "qwerty1",
"member_count": 0,
"name": "developers",
"remote_id": "",
"scheme_admin": false,
"source": "",
"update_at": 1,
}
}
returnFocus={[Function]}
<div>
<span
aria-expanded="false"
aria-haspopup="dialog"
>
<a
className="group-mention-link"
class="group-mention-link"
role="button"
tabIndex={0}
tabindex="0"
>
@developers
</a>
</UserGroupPopoverController>
</span>
.
</Fragment>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning a group that is allowed reference 1`] = `
<Fragment>
<UserGroupPopoverController
group={
Object {
"allow_reference": true,
"create_at": 1,
"delete_at": 0,
"description": "",
"display_name": "group_display_name",
"has_syncables": false,
"id": "qwerty1",
"member_count": 0,
"name": "developers",
"remote_id": "",
"scheme_admin": false,
"source": "",
"update_at": 1,
}
}
returnFocus={[Function]}
<div>
<span
aria-expanded="false"
aria-haspopup="dialog"
>
<a
className="group-mention-link"
class="group-mention-link"
role="button"
tabIndex={0}
tabindex="0"
>
@developers
</a>
</UserGroupPopoverController>
</Fragment>
</span>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning a group that is allowed reference with group highlight disabled 1`] = `
<Fragment>
<div>
(at)-developers
</Fragment>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning a group that is not allowed reference 1`] = `
<Fragment>
<div>
(at)-marketing
</Fragment>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning all 1`] = `
<Fragment>
<div>
(at)-all
</Fragment>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning all with mixed case 1`] = `
<Fragment>
<div>
(at)-aLL
</Fragment>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning current user 1`] = `
<Fragment>
<ProfilePopoverController
returnFocus={[Function]}
src="/api/v4/users/abc1/image"
triggerComponentAs="button"
triggerComponentClass="style--none mention--highlight"
userId="abc1"
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="style--none mention--highlight"
>
<span
className="mention-link"
class="mention-link"
>
@First Last
</span>
</ProfilePopoverController>
</Fragment>
</button>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning user 1`] = `
<Fragment>
<ProfilePopoverController
returnFocus={[Function]}
src="/api/v4/users/abc2/image"
triggerComponentAs="button"
triggerComponentClass="style--none"
userId="abc2"
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="style--none"
>
<span
className="mention-link"
class="mention-link"
>
@Nick
</span>
</ProfilePopoverController>
</Fragment>
</button>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning user containing and followed by punctuation 1`] = `
<Fragment>
<ProfilePopoverController
returnFocus={[Function]}
src="/api/v4/users/abc3/image"
triggerComponentAs="button"
triggerComponentClass="style--none"
userId="abc3"
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="style--none"
>
<span
className="mention-link"
class="mention-link"
>
@Dot Matrix
</span>
</ProfilePopoverController>
</button>
.
</Fragment>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning user containing punctuation 1`] = `
<Fragment>
<ProfilePopoverController
returnFocus={[Function]}
src="/api/v4/users/abc3/image"
triggerComponentAs="button"
triggerComponentClass="style--none"
userId="abc3"
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="style--none"
>
<span
className="mention-link"
class="mention-link"
>
@Dot Matrix
</span>
</ProfilePopoverController>
</Fragment>
</button>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning user followed by punctuation 1`] = `
<Fragment>
<ProfilePopoverController
returnFocus={[Function]}
src="/api/v4/users/abc2/image"
triggerComponentAs="button"
triggerComponentClass="style--none"
userId="abc2"
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="style--none"
>
<span
className="mention-link"
class="mention-link"
>
@Nick
</span>
</ProfilePopoverController>
</button>
...
</Fragment>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning user with different teammate name display setting 1`] = `
<Fragment>
<ProfilePopoverController
returnFocus={[Function]}
src="/api/v4/users/abc2/image"
triggerComponentAs="button"
triggerComponentClass="style--none"
userId="abc2"
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="style--none"
>
<span
className="mention-link"
class="mention-link"
>
@user1
</span>
</ProfilePopoverController>
</Fragment>
</button>
</div>
`;
exports[`components/AtMention should match snapshot when mentioning user with mixed case 1`] = `
<Fragment>
<ProfilePopoverController
returnFocus={[Function]}
src="/api/v4/users/abc2/image"
triggerComponentAs="button"
triggerComponentClass="style--none"
userId="abc2"
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="style--none"
>
<span
className="mention-link"
class="mention-link"
>
@Nick
</span>
</ProfilePopoverController>
</Fragment>
</button>
</div>
`;
exports[`components/AtMention should match snapshot when not mentioning a user 1`] = `
<Fragment>
<div>
(at)-notauser
</Fragment>
</div>
`;
exports[`components/AtMention should match snapshot when not mentioning a user with mixed case 1`] = `
<Fragment>
<div>
(at)-NOTAuser
</Fragment>
</div>
`;
@@ -1,17 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {General} from 'mattermost-redux/constants';
import AtMention from 'components/at_mention/at_mention';
import {render} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
/* eslint-disable global-require */
import AtMention from './at_mention';
describe('components/AtMention', () => {
const baseProps = {
@@ -33,7 +30,7 @@ describe('components/AtMention', () => {
};
test('should match snapshot when mentioning user', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='user1'
@@ -42,11 +39,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning user with different teammate name display setting', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='user1'
@@ -56,11 +53,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning user followed by punctuation', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='user1...'
@@ -69,11 +66,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning user containing punctuation', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='userdot.'
@@ -82,11 +79,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning user containing and followed by punctuation', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='userdot..'
@@ -95,11 +92,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning user with mixed case', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='USeR1'
@@ -108,11 +105,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning current user', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='currentUser'
@@ -121,11 +118,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning all', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='all'
@@ -134,11 +131,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning all with mixed case', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='aLL'
@@ -147,11 +144,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when not mentioning a user', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='notauser'
@@ -160,11 +157,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when not mentioning a user with mixed case', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='NOTAuser'
@@ -173,11 +170,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning a group that is allowed reference', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='developers'
@@ -186,11 +183,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning a group that is allowed reference with group highlight disabled', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='developers'
@@ -200,11 +197,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning a group that is not allowed reference', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='marketing'
@@ -213,11 +210,11 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when mentioning a group followed by punctuation', () => {
const wrapper = shallow(
const {container} = render(
<AtMention
{...baseProps}
mentionName='developers.'
@@ -226,7 +223,7 @@ describe('components/AtMention', () => {
</AtMention>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
describe('fetchMissingUsers', () => {
@@ -245,7 +242,7 @@ describe('components/AtMention', () => {
});
test('when fetchMissingUsers is false, should not fetch an unloaded user on mount', () => {
shallow(
render(
<AtMention
{...baseProps}
mentionName='someuser'
@@ -1,58 +1,50 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import * as useOpenPricingModal from 'components/common/hooks/useOpenPricingModal';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import AtPlanMention from './index';
describe('components/AtPlanMention', () => {
it('should open pricing modal when plan mentioned is trial', () => {
it('should open pricing modal when plan mentioned is trial', async () => {
const openPricingModal = jest.fn();
jest.spyOn(useOpenPricingModal, 'default').mockImplementation(() => ({
openPricingModal,
isAirGapped: false,
}));
const wrapper = shallow(<AtPlanMention plan='Enterprise trial'/>);
wrapper.find('a').simulate('click', {
preventDefault: () => {
},
});
renderWithContext(<AtPlanMention plan='Enterprise trial'/>);
await userEvent.click(screen.getByText('Enterprise trial'));
expect(openPricingModal).toHaveBeenCalledTimes(1);
});
it('should open pricing modal when plan mentioned is Enterprise', () => {
it('should open pricing modal when plan mentioned is Enterprise', async () => {
const openPricingModal = jest.fn();
jest.spyOn(useOpenPricingModal, 'default').mockImplementation(() => ({
openPricingModal,
isAirGapped: false,
}));
const wrapper = shallow(<AtPlanMention plan='Enterprise plan'/>);
wrapper.find('a').simulate('click', {
preventDefault: () => {
},
});
renderWithContext(<AtPlanMention plan='Enterprise plan'/>);
await userEvent.click(screen.getByText('Enterprise plan'));
expect(openPricingModal).toHaveBeenCalledTimes(1);
});
it('should open purchase modal when plan mentioned is professional', () => {
it('should open purchase modal when plan mentioned is professional', async () => {
const openPricingModal = jest.fn();
jest.spyOn(useOpenPricingModal, 'default').mockImplementation(() => ({
openPricingModal,
isAirGapped: false,
}));
const wrapper = shallow(<AtPlanMention plan='Professional plan'/>);
wrapper.find('a').simulate('click', {
preventDefault: () => {
},
});
renderWithContext(<AtPlanMention plan='Professional plan'/>);
await userEvent.click(screen.getByText('Professional plan'));
expect(openPricingModal).toHaveBeenCalledTimes(1);
});
@@ -64,9 +56,9 @@ describe('components/AtPlanMention', () => {
isAirGapped: true,
}));
const wrapper = shallow(<AtPlanMention plan='Enterprise plan'/>);
expect(wrapper.find('span').exists()).toBe(true);
expect(wrapper.find('a').exists()).toBe(false);
expect(wrapper.find('span').text()).toBe('Enterprise plan');
renderWithContext(<AtPlanMention plan='Enterprise plan'/>);
// When air-gapped, renders as span instead of anchor
expect(screen.getByText('Enterprise plan').tagName).toBe('SPAN');
});
});
@@ -1,31 +1,33 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`/components/common/CircularChart should match snapshot 1`] = `
<div
className="CircularChart"
>
<svg
className="circular-chart success"
height="100"
viewBox="0 0 36 36"
width="100"
<div>
<div
class="CircularChart"
>
<path
className="circle-bg"
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"
/>
<path
className="circle"
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"
strokeDasharray="75, 100"
/>
<text
className="percentageOrNumber"
x="18"
y="20.70"
<svg
class="circular-chart success"
height="100"
viewBox="0 0 36 36"
width="100"
>
75
</text>
</svg>
<path
class="circle-bg"
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"
/>
<path
class="circle"
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"
stroke-dasharray="75, 100"
/>
<text
class="percentageOrNumber"
x="18"
y="20.70"
>
75
</text>
</svg>
</div>
</div>
`;
@@ -1,12 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import CircularChart from 'components/common/circular_chart/circular_chart';
import {render, screen} from 'tests/react_testing_utils';
// import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import CircularChart from './circular_chart';
describe('/components/common/CircularChart', () => {
const baseProps = {
@@ -17,33 +16,33 @@ describe('/components/common/CircularChart', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(
const {container} = render(
<CircularChart
{...baseProps}
type={'success'}
/>);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('test circularChart contains the text value as specified in the base props', () => {
const wrapper = shallow(
render(
<CircularChart
{...baseProps}
type={'success'}
/>);
const circularChartText = wrapper.find('.percentageOrNumber').text();
const circularChartText = screen.getByText('75');
expect(parseInt(circularChartText, 10)).toBe(baseProps.value);
expect(circularChartText).toBeInTheDocument();
});
test('test circularChart contains the text value with the percentage symbol when isPercentage is set to true', () => {
const wrapper = shallow(
render(
<CircularChart
{...{...baseProps, isPercentage: true}}
type={'success'}
/>);
const circularChartText = wrapper.find('.percentageOrNumber').text();
const circularChartText = screen.getByText('75 %');
expect(circularChartText).toBe(`${baseProps.value.toString()} %`);
expect(circularChartText).toBeInTheDocument();
});
});
@@ -1,10 +1,25 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/custom_status/custom_status_emoji should match snapshot 1`] = `<CustomStatusEmoji />`;
exports[`components/custom_status/custom_status_emoji should match snapshot 1`] = `
<div>
<span
aria-label=":smile:"
class="emoticon"
data-emoticon="smile"
style="background-image: url(\\"/static/emoji/1f604.png\\"); background-size: contain; height: 16px; width: 16px; max-height: 16px; max-width: 16px; min-height: 16px; min-width: 16px; overflow: hidden; margin-left: 4px; margin-top: -1px;"
/>
</div>
`;
exports[`components/custom_status/custom_status_emoji should match snapshot with props 1`] = `
<CustomStatusEmoji
emojiSize={34}
showTooltip={true}
/>
<div>
<span>
<span
aria-label=":smile:"
class="emoticon"
data-emoticon="smile"
style="background-image: url(\\"/static/emoji/1f604.png\\"); background-size: contain; height: 34px; width: 34px; max-height: 34px; max-width: 34px; min-height: 34px; min-width: 34px; overflow: hidden; margin-left: 4px; margin-top: -1px;"
/>
</span>
</div>
`;
@@ -1,55 +1,32 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/custom_status/custom_status_emoji should match snapshot 1`] = `
<button
className="statusSuggestion__row cursor--pointer"
onClick={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
tabIndex={0}
>
<div
className="statusSuggestion__icon"
<div>
<button
class="statusSuggestion__row cursor--pointer"
tabindex="0"
>
<Memo(RenderEmoji)
emojiName=""
size={20}
<div
class="statusSuggestion__icon"
/>
</div>
<CustomStatusText
className="statusSuggestion__text"
text=""
/>
</button>
</button>
</div>
`;
exports[`components/custom_status/custom_status_emoji should match snapshot with duration 1`] = `
<button
className="statusSuggestion__row cursor--pointer"
onClick={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
tabIndex={0}
>
<div
className="statusSuggestion__icon"
<div>
<button
class="statusSuggestion__row cursor--pointer"
tabindex="0"
>
<Memo(RenderEmoji)
emojiName=""
size={20}
<div
class="statusSuggestion__icon"
/>
</div>
<CustomStatusText
className="statusSuggestion__text with_duration"
text=""
/>
<span
className="statusSuggestion__duration"
>
<MemoizedFormattedMessage
defaultMessage="Today"
id="custom_status.expiry_dropdown.today"
/>
</span>
</button>
<span
class="statusSuggestion__duration"
>
Today
</span>
</button>
</div>
`;
@@ -1,9 +1,21 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/custom_status/custom_status_text should match snapshot 1`] = `<CustomStatusText />`;
exports[`components/custom_status/custom_status_text should match snapshot 1`] = `
<div>
<span
class="overflow--ellipsis text-nowrap "
>
Available
</span>
</div>
`;
exports[`components/custom_status/custom_status_text should match snapshot with props 1`] = `
<CustomStatusText
text="In a meeting"
/>
<div>
<span
class="overflow--ellipsis text-nowrap custom-class"
>
In a meeting
</span>
</div>
`;
@@ -1,13 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {mount} from 'enzyme';
import React from 'react';
import {Provider} from 'react-redux';
import * as CustomStatusSelectors from 'selectors/views/custom_status';
import mockStore from 'tests/test_store';
import {renderWithContext} from 'tests/react_testing_utils';
import CustomStatusEmoji from './custom_status_emoji';
@@ -15,42 +13,45 @@ jest.mock('mattermost-redux/selectors/entities/timezone');
jest.mock('selectors/views/custom_status');
describe('components/custom_status/custom_status_emoji', () => {
const store = mockStore({});
const getCustomStatus = () => {
return null;
const customStatus = {
emoji: 'smile',
text: 'Happy',
};
(CustomStatusSelectors.makeGetCustomStatus as jest.Mock).mockReturnValue(getCustomStatus);
(CustomStatusSelectors.isCustomStatusEnabled as any as jest.Mock).mockReturnValue(true);
beforeEach(() => {
(CustomStatusSelectors.makeGetCustomStatus as jest.Mock).mockReturnValue(() => customStatus);
(CustomStatusSelectors.isCustomStatusEnabled as any as jest.Mock).mockReturnValue(true);
(CustomStatusSelectors.isCustomStatusExpired as jest.Mock).mockReturnValue(false);
});
it('should match snapshot', () => {
const wrapper = mount(<CustomStatusEmoji/>, {wrappingComponent: Provider, wrappingComponentProps: {store}});
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<CustomStatusEmoji/>);
expect(container).toMatchSnapshot();
});
it('should match snapshot with props', () => {
const wrapper = mount(
const {container} = renderWithContext(
<CustomStatusEmoji
emojiSize={34}
showTooltip={true}
/>,
{wrappingComponent: Provider, wrappingComponentProps: {store}},
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
it('should not render when EnableCustomStatus in config is false', () => {
(CustomStatusSelectors.isCustomStatusEnabled as any as jest.Mock).mockReturnValue(false);
const wrapper = mount(<CustomStatusEmoji/>, {wrappingComponent: Provider, wrappingComponentProps: {store}});
const {container} = renderWithContext(<CustomStatusEmoji/>);
expect(wrapper.isEmptyRender()).toBeTruthy();
expect(container).toBeEmptyDOMElement();
});
it('should not render when custom status is expired', () => {
(CustomStatusSelectors.isCustomStatusEnabled as any as jest.Mock).mockReturnValue(true);
(CustomStatusSelectors.isCustomStatusExpired as jest.Mock).mockReturnValue(true);
const wrapper = mount(<CustomStatusEmoji/>, {wrappingComponent: Provider, wrappingComponentProps: {store}});
const {container} = renderWithContext(<CustomStatusEmoji/>);
expect(wrapper.isEmptyRender()).toBeTruthy();
expect(container).toBeEmptyDOMElement();
});
});
@@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {CustomStatusDuration} from '@mattermost/types/users';
import {renderWithContext, userEvent} from 'tests/react_testing_utils';
import CustomStatusSuggestion from './custom_status_suggestion';
describe('components/custom_status/custom_status_emoji', () => {
@@ -20,11 +21,11 @@ describe('components/custom_status/custom_status_emoji', () => {
};
it('should match snapshot', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<CustomStatusSuggestion {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
it('should match snapshot with duration', () => {
@@ -35,29 +36,41 @@ describe('components/custom_status/custom_status_emoji', () => {
duration: CustomStatusDuration.TODAY,
},
};
const wrapper = shallow(
const {container} = renderWithContext(
<CustomStatusSuggestion {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
it('should call handleSuggestionClick when click occurs on div', () => {
const wrapper = shallow(
it('should call handleSuggestionClick when click occurs on div', async () => {
// Suppress the DOM nesting warning (button inside button) which is a component issue
const originalError = console.error;
console.error = jest.fn();
const {container} = renderWithContext(
<CustomStatusSuggestion {...baseProps}/>,
);
wrapper.find('.statusSuggestion__row').simulate('click');
await userEvent.click(container.querySelector('.statusSuggestion__row')!);
expect(baseProps.handleSuggestionClick).toHaveBeenCalledTimes(1);
console.error = originalError;
});
it('should render clearButton when hover occurs on div', () => {
const wrapper = shallow(
it('should render clearButton when hover occurs on div', async () => {
// Suppress the DOM nesting warning (button inside button) which is a component issue
const originalError = console.error;
console.error = jest.fn();
const {container} = renderWithContext(
<CustomStatusSuggestion {...baseProps}/>,
);
expect(wrapper.find('.suggestion-clear').exists()).toBeFalsy();
wrapper.find('.statusSuggestion__row').simulate('mouseEnter');
expect(wrapper.find('.suggestion-clear').exists()).toBeTruthy();
expect(container.querySelector('.suggestion-clear')).not.toBeInTheDocument();
await userEvent.hover(container.querySelector('.statusSuggestion__row')!);
expect(container.querySelector('.suggestion-clear')).toBeInTheDocument();
console.error = originalError;
});
});
@@ -1,42 +1,42 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {mount} from 'enzyme';
import React from 'react';
import {Provider} from 'react-redux';
import * as CustomStatusSelectors from 'selectors/views/custom_status';
import mockStore from 'tests/test_store';
import {renderWithContext} from 'tests/react_testing_utils';
import CustomStatusText from './custom_status_text';
jest.mock('selectors/views/custom_status');
describe('components/custom_status/custom_status_text', () => {
const store = mockStore({});
beforeEach(() => {
(CustomStatusSelectors.isCustomStatusEnabled as any as jest.Mock).mockReturnValue(true);
});
it('should match snapshot', () => {
const wrapper = mount(<CustomStatusText/>, {wrappingComponent: Provider, wrappingComponentProps: {store}});
const {container} = renderWithContext(<CustomStatusText text='Available'/>);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
it('should match snapshot with props', () => {
const wrapper = mount(
const {container} = renderWithContext(
<CustomStatusText
text='In a meeting'
className='custom-class'
/>,
{wrappingComponent: Provider, wrappingComponentProps: {store}},
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
it('should not render when EnableCustomStatus in config is false', () => {
(CustomStatusSelectors.isCustomStatusEnabled as any as jest.Mock).mockReturnValue(false);
const wrapper = mount(<CustomStatusText/>, {wrappingComponent: Provider, wrappingComponentProps: {store}});
const {container} = renderWithContext(<CustomStatusText/>);
expect(wrapper.isEmptyRender()).toBeTruthy();
expect(container).toBeEmptyDOMElement();
});
});
@@ -1,13 +1,14 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/error_page/ErrorLink should match snapshot 1`] = `
<ForwardRef
href="https://docs.mattermost.com/deployment/sso-gitlab.html"
location="error_link"
>
<MemoizedFormattedMessage
defaultMessage="GitLab"
id="error.oauth_missing_code.gitlab.link"
/>
</ForwardRef>
<div>
<a
href="https://docs.mattermost.com/deployment/sso-gitlab.html?utm_source=mattermost&utm_medium=in-product&utm_content=error_link&uid=uid-123&sid=sid-456&edition=team&server_version=11.4.0"
location="error_link"
rel="noopener noreferrer"
target="_blank"
>
GitLab
</a>
</div>
`;
@@ -1,208 +1,158 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/error_page/ErrorMessage should match snapshot, channel_not_found type 1`] = `
<p>
<MemoizedFormattedMessage
defaultMessage="The channel you're requesting is private or does not exist. Please contact an Administrator to be added to the channel."
id="error.channel_not_found.message"
/>
</p>
<div>
<p>
The channel you're requesting is private or does not exist. Please contact an Administrator to be added to the channel.
</p>
</div>
`;
exports[`components/error_page/ErrorMessage should match snapshot, channel_not_found type 2`] = `
<p>
<MemoizedFormattedMessage
defaultMessage="Your guest account has no channels assigned. Please contact an administrator."
id="error.channel_not_found.message_guest"
/>
</p>
exports[`components/error_page/ErrorMessage should match snapshot, channel_not_found type for guest 1`] = `
<div>
<p>
Your guest account has no channels assigned. Please contact an administrator.
</p>
</div>
`;
exports[`components/error_page/ErrorMessage should match snapshot, local_storage type 1`] = `
<div>
<MemoizedFormattedMessage
defaultMessage="Mattermost was unable to load because a setting in your browser prevents the use of its local storage features. To allow Mattermost to load, try the following actions:"
id="error.local_storage.message"
/>
<ul>
<li>
<MemoizedFormattedMessage
defaultMessage="Enable cookies"
id="error.local_storage.help1"
/>
</li>
<li>
<MemoizedFormattedMessage
defaultMessage="Turn off private browsing"
id="error.local_storage.help2"
/>
</li>
<li>
<MemoizedFormattedMessage
defaultMessage="Use a supported browser (IE 11, Chrome 61+, Firefox 60+, Safari 12+, Edge 42+)"
id="error.local_storage.help3"
/>
</li>
</ul>
<div>
Mattermost was unable to load because a setting in your browser prevents the use of its local storage features. To allow Mattermost to load, try the following actions:
<ul>
<li>
Enable cookies
</li>
<li>
Turn off private browsing
</li>
<li>
Use a supported browser (IE 11, Chrome 61+, Firefox 60+, Safari 12+, Edge 42+)
</li>
</ul>
</div>
</div>
`;
exports[`components/error_page/ErrorMessage should match snapshot, no type but with message 1`] = `
<p>
error message
</p>
`;
exports[`components/error_page/ErrorMessage should match snapshot, no type nor message 1`] = `
<p>
<MemoizedFormattedMessage
defaultMessage="An error has occurred."
id="error.generic.message"
/>
</p>
`;
exports[`components/error_page/ErrorMessage should match snapshot, oauth_access_denied type 1`] = `
<p>
<MemoizedFormattedMessage
defaultMessage="You must authorize Mattermost to log in with {service}."
id="error.oauth_access_denied"
values={
Object {
"service": "Gitlab",
}
}
/>
</p>
`;
exports[`components/error_page/ErrorMessage should match snapshot, oauth_invalid_param type 1`] = `
<p>
error message
</p>
`;
exports[`components/error_page/ErrorMessage should match snapshot, oauth_invalid_redirect_url type 1`] = `
<p>
error message
</p>
`;
exports[`components/error_page/ErrorMessage should match snapshot, oauth_missing_code type 1`] = `
<div>
<p>
<MemoizedFormattedMessage
defaultMessage="The service provider {service} did not provide an authorization code in the redirect URL."
id="error.oauth_missing_code"
values={
Object {
"service": "Gitlab",
}
}
/>
</p>
<p>
<MemoizedFormattedMessage
defaultMessage="For {link} make sure your administrator enabled the Google+ API."
id="error.oauth_missing_code.google"
values={
Object {
"link": <ErrorLink
message={
Object {
"defaultMessage": "Google Apps",
"id": "error.oauth_missing_code.google.link",
}
}
url="https://docs.mattermost.com/deployment/sso-google.html"
/>,
}
}
/>
</p>
<p>
<MemoizedFormattedMessage
defaultMessage="For {link} make sure the administrator of your Microsoft organization has enabled the Mattermost app."
id="error.oauth_missing_code.office365"
values={
Object {
"link": <ErrorLink
message={
Object {
"defaultMessage": "Entra ID",
"id": "error.oauth_missing_code.office365.link",
}
}
url="https://docs.mattermost.com/deployment/sso-office.html"
/>,
}
}
/>
</p>
<p>
<MemoizedFormattedMessage
defaultMessage="For {link} please make sure you followed the setup instructions."
id="error.oauth_missing_code.gitlab"
values={
Object {
"link": <ErrorLink
message={
Object {
"defaultMessage": "GitLab",
"id": "error.oauth_missing_code.gitlab.link",
}
}
url="https://docs.mattermost.com/deployment/sso-gitlab.html"
/>,
}
}
/>
</p>
<p>
<MemoizedFormattedMessage
defaultMessage="If you reviewed the above and are still having trouble with configuration, you may post in our {link} where we'll be happy to help with issues during setup."
id="error.oauth_missing_code.forum"
values={
Object {
"link": <ErrorLink
message={
Object {
"defaultMessage": "Troubleshooting forum",
"id": "error.oauth_missing_code.forum.link",
}
}
url="https://forum.mattermost.com/c/trouble-shoot"
/>,
}
}
/>
error message
</p>
</div>
`;
exports[`components/error_page/ErrorMessage should match snapshot, no type nor message 1`] = `
<div>
<p>
An error has occurred.
</p>
</div>
`;
exports[`components/error_page/ErrorMessage should match snapshot, oauth_access_denied type 1`] = `
<div>
<p>
You must authorize Mattermost to log in with Gitlab.
</p>
</div>
`;
exports[`components/error_page/ErrorMessage should match snapshot, oauth_invalid_param type 1`] = `
<div>
<p>
error message
</p>
</div>
`;
exports[`components/error_page/ErrorMessage should match snapshot, oauth_invalid_redirect_url type 1`] = `
<div>
<p>
error message
</p>
</div>
`;
exports[`components/error_page/ErrorMessage should match snapshot, oauth_missing_code type 1`] = `
<div>
<div>
<p>
The service provider Gitlab did not provide an authorization code in the redirect URL.
</p>
<p>
For
<a
href="https://docs.mattermost.com/deployment/sso-google.html?utm_source=mattermost&utm_medium=in-product&utm_content=error_link&uid=user-id-123&sid=telemetry-id-456&edition=team&server_version=9.0.0"
location="error_link"
rel="noopener noreferrer"
target="_blank"
>
Google Apps
</a>
make sure your administrator enabled the Google+ API.
</p>
<p>
For
<a
href="https://docs.mattermost.com/deployment/sso-office.html?utm_source=mattermost&utm_medium=in-product&utm_content=error_link&uid=user-id-123&sid=telemetry-id-456&edition=team&server_version=9.0.0"
location="error_link"
rel="noopener noreferrer"
target="_blank"
>
Entra ID
</a>
make sure the administrator of your Microsoft organization has enabled the Mattermost app.
</p>
<p>
For
<a
href="https://docs.mattermost.com/deployment/sso-gitlab.html?utm_source=mattermost&utm_medium=in-product&utm_content=error_link&uid=user-id-123&sid=telemetry-id-456&edition=team&server_version=9.0.0"
location="error_link"
rel="noopener noreferrer"
target="_blank"
>
GitLab
</a>
please make sure you followed the setup instructions.
</p>
<p>
If you reviewed the above and are still having trouble with configuration, you may post in our
<a
href="https://forum.mattermost.com/c/trouble-shoot?utm_source=mattermost&utm_medium=in-product&utm_content=error_link&uid=user-id-123&sid=telemetry-id-456&edition=team&server_version=9.0.0"
location="error_link"
rel="noopener noreferrer"
target="_blank"
>
Troubleshooting forum
</a>
where we'll be happy to help with issues during setup.
</p>
</div>
</div>
`;
exports[`components/error_page/ErrorMessage should match snapshot, page_not_found type 1`] = `
<p>
<MemoizedFormattedMessage
defaultMessage="The page you were trying to reach does not exist"
id="error.not_found.message"
/>
</p>
<div>
<p>
The page you were trying to reach does not exist
</p>
</div>
`;
exports[`components/error_page/ErrorMessage should match snapshot, permalink_not_found type 1`] = `
<p>
<MemoizedFormattedMessage
defaultMessage="Permalink belongs to a deleted message or to a channel to which you do not have access."
id="permalink.error.access"
/>
</p>
<div>
<p>
Permalink belongs to a deleted message or to a channel to which you do not have access.
</p>
</div>
`;
exports[`components/error_page/ErrorMessage should match snapshot, team_not_found type 1`] = `
<p>
<MemoizedFormattedMessage
defaultMessage="The team you're requesting is private or does not exist. Please contact your Administrator for an invitation."
id="error.team_not_found.message"
/>
</p>
<div>
<p>
The team you're requesting is private or does not exist. Please contact your Administrator for an invitation.
</p>
</div>
`;
@@ -1,77 +1,67 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/error_page/ErrorTitle should match snapshot, channel_not_found type 1`] = `
<MemoizedFormattedMessage
defaultMessage="Channel Not Found"
id="error.channel_not_found.title"
/>
<div>
Channel Not Found
</div>
`;
exports[`components/error_page/ErrorTitle should match snapshot, local_storage type 1`] = `
<MemoizedFormattedMessage
defaultMessage="Cannot Load Mattermost"
id="error.local_storage.title"
/>
<div>
Cannot Load Mattermost
</div>
`;
exports[`components/error_page/ErrorTitle should match snapshot, no type but with title 1`] = `
<Fragment>
<div>
error title
</Fragment>
</div>
`;
exports[`components/error_page/ErrorTitle should match snapshot, no type nor title 1`] = `
<MemoizedFormattedMessage
defaultMessage="Error"
id="error.generic.title"
/>
<div>
Error
</div>
`;
exports[`components/error_page/ErrorTitle should match snapshot, oauth_access_denied type 1`] = `
<MemoizedFormattedMessage
defaultMessage="Authorization Error"
id="error.oauth_access_denied.title"
/>
<div>
Authorization Error
</div>
`;
exports[`components/error_page/ErrorTitle should match snapshot, oauth_invalid_param type 1`] = `
<MemoizedFormattedMessage
defaultMessage="OAuth Parameter Error"
id="error.oauth_invalid_param.title"
/>
<div>
OAuth Parameter Error
</div>
`;
exports[`components/error_page/ErrorTitle should match snapshot, oauth_invalid_redirect_url type 1`] = `
<MemoizedFormattedMessage
defaultMessage="OAuth Parameter Error"
id="error.oauth_invalid_param.title"
/>
<div>
OAuth Parameter Error
</div>
`;
exports[`components/error_page/ErrorTitle should match snapshot, oauth_missing_code type 1`] = `
<MemoizedFormattedMessage
defaultMessage="Mattermost Needs Your Help"
id="error.oauth_missing_code.title"
/>
<div>
Mattermost Needs Your Help
</div>
`;
exports[`components/error_page/ErrorTitle should match snapshot, page_not_found type 1`] = `
<MemoizedFormattedMessage
defaultMessage="Page Not Found"
id="error.not_found.title"
/>
<div>
Page Not Found
</div>
`;
exports[`components/error_page/ErrorTitle should match snapshot, permalink_not_found type 1`] = `
<MemoizedFormattedMessage
defaultMessage="Message Not Found"
id="permalink.error.title"
/>
<div>
Message Not Found
</div>
`;
exports[`components/error_page/ErrorTitle should match snapshot, team_not_found type 1`] = `
<MemoizedFormattedMessage
defaultMessage="Team Not Found"
id="error.team_not_found.title"
/>
<div>
Team Not Found
</div>
`;
@@ -1,10 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import ErrorLink from 'components/error_page/error_link';
import {renderWithContext} from 'tests/react_testing_utils';
import ErrorLink from './error_link';
describe('components/error_page/ErrorLink', () => {
const baseProps = {
@@ -16,10 +17,23 @@ describe('components/error_page/ErrorLink', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorLink {...baseProps}/>,
{
entities: {
users: {
currentUserId: 'uid-123',
},
general: {
config: {
TelemetryId: 'sid-456',
Version: '11.4.0',
},
},
},
},
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,13 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import ErrorMessage from 'components/error_page/error_message';
import {renderWithContext} from 'tests/react_testing_utils';
import {ErrorPageTypes} from 'utils/constants';
import ErrorMessage from './error_message';
describe('components/error_page/ErrorMessage', () => {
const baseProps = {
type: ErrorPageTypes.LOCAL_STORAGE,
@@ -15,110 +15,136 @@ describe('components/error_page/ErrorMessage', () => {
service: '',
};
const state = {
entities: {
users: {
currentUserId: 'user-id-123',
},
general: {
config: {
TelemetryId: 'telemetry-id-456',
Version: '9.0.0',
},
},
},
};
test('should match snapshot, local_storage type', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorMessage {...baseProps}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, permalink_not_found type', () => {
const props = {...baseProps, type: ErrorPageTypes.PERMALINK_NOT_FOUND};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorMessage {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, oauth_missing_code type', () => {
const props = {...baseProps, type: ErrorPageTypes.OAUTH_MISSING_CODE, service: 'Gitlab'};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorMessage {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, oauth_access_denied type', () => {
const props = {...baseProps, type: ErrorPageTypes.OAUTH_ACCESS_DENIED, service: 'Gitlab'};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorMessage {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, oauth_invalid_param type', () => {
const props = {...baseProps, type: ErrorPageTypes.OAUTH_INVALID_PARAM, message: 'error message'};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorMessage {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, oauth_invalid_redirect_url type', () => {
const props = {...baseProps, type: ErrorPageTypes.OAUTH_INVALID_REDIRECT_URL, message: 'error message'};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorMessage {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, page_not_found type', () => {
const props = {...baseProps, type: ErrorPageTypes.PAGE_NOT_FOUND};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorMessage {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, team_not_found type', () => {
const props = {...baseProps, type: ErrorPageTypes.TEAM_NOT_FOUND};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorMessage {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, channel_not_found type', () => {
const props = {...baseProps, type: ErrorPageTypes.CHANNEL_NOT_FOUND};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorMessage {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
const wrapper2 = shallow(
<ErrorMessage
{...props}
isGuest={true}
/>,
test('should match snapshot, channel_not_found type for guest', () => {
const props = {...baseProps, type: ErrorPageTypes.CHANNEL_NOT_FOUND, isGuest: true};
const {container} = renderWithContext(
<ErrorMessage {...props}/>,
state,
);
expect(wrapper2).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, no type but with message', () => {
const props = {...baseProps, type: '', message: 'error message'};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorMessage {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, no type nor message', () => {
const props = {...baseProps, type: '', message: ''};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorMessage {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,13 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import ErrorTitle from 'components/error_page/error_title';
import {renderWithContext} from 'tests/react_testing_utils';
import {ErrorPageTypes} from 'utils/constants';
import ErrorTitle from './error_title';
describe('components/error_page/ErrorTitle', () => {
const baseProps = {
type: ErrorPageTypes.LOCAL_STORAGE,
@@ -15,100 +15,100 @@ describe('components/error_page/ErrorTitle', () => {
};
test('should match snapshot, local_storage type', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorTitle {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, permalink_not_found type', () => {
const props = {...baseProps, type: ErrorPageTypes.PERMALINK_NOT_FOUND};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorTitle {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, oauth_missing_code type', () => {
const props = {...baseProps, type: ErrorPageTypes.OAUTH_MISSING_CODE};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorTitle {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, oauth_access_denied type', () => {
const props = {...baseProps, type: ErrorPageTypes.OAUTH_ACCESS_DENIED};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorTitle {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, oauth_invalid_param type', () => {
const props = {...baseProps, type: ErrorPageTypes.OAUTH_INVALID_PARAM};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorTitle {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, oauth_invalid_redirect_url type', () => {
const props = {...baseProps, type: ErrorPageTypes.OAUTH_INVALID_REDIRECT_URL};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorTitle {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, page_not_found type', () => {
const props = {...baseProps, type: ErrorPageTypes.PAGE_NOT_FOUND};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorTitle {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, team_not_found type', () => {
const props = {...baseProps, type: ErrorPageTypes.TEAM_NOT_FOUND};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorTitle {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, channel_not_found type', () => {
const props = {...baseProps, type: ErrorPageTypes.CHANNEL_NOT_FOUND};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorTitle {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, no type but with title', () => {
const props = {...baseProps, type: '', title: 'error title'};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorTitle {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, no type nor title', () => {
const props = {...baseProps, type: '', title: ''};
const wrapper = shallow(
const {container} = renderWithContext(
<ErrorTitle {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {Client4} from 'mattermost-redux/client';
import {render} from 'tests/react_testing_utils';
import ExternalImage from './external_image';
describe('ExternalImage', () => {
@@ -23,10 +24,10 @@ describe('ExternalImage', () => {
};
test('should render an image', () => {
const wrapper = shallow(<ExternalImage {...baseProps}/>);
const {container} = render(<ExternalImage {...baseProps}/>);
expect(baseProps.children).toHaveBeenCalledWith(baseProps.src);
expect(wrapper.find('img').exists()).toBe(true);
expect(container.querySelector('img')).toBeInTheDocument();
});
test('should render an image without image metadata', () => {
@@ -35,10 +36,10 @@ describe('ExternalImage', () => {
imageMetadata: undefined,
};
const wrapper = shallow(<ExternalImage {...props}/>);
const {container} = render(<ExternalImage {...props}/>);
expect(baseProps.children).toHaveBeenCalledWith(baseProps.src);
expect(wrapper.find('img').exists()).toBe(true);
expect(container.querySelector('img')).toBeInTheDocument();
});
test('should render an SVG when enabled', () => {
@@ -53,10 +54,10 @@ describe('ExternalImage', () => {
src: 'https://example.com/logo.svg',
};
const wrapper = shallow(<ExternalImage {...props}/>);
const {container} = render(<ExternalImage {...props}/>);
expect(props.children).toHaveBeenCalledWith(props.src);
expect(wrapper.find('img').exists()).toBe(true);
expect(container.querySelector('img')).toBeInTheDocument();
});
test('should not render an SVG when disabled', () => {
@@ -72,10 +73,10 @@ describe('ExternalImage', () => {
src: 'https://example.com/logo.svg',
};
const wrapper = shallow(<ExternalImage {...props}/>);
const {container} = render(<ExternalImage {...props}/>);
expect(props.children).toHaveBeenCalledWith('');
expect(wrapper.find('img').exists()).toBe(true);
expect(container.querySelector('img')).toBeInTheDocument();
});
test('should pass src through the image proxy when enabled', () => {
@@ -84,9 +85,9 @@ describe('ExternalImage', () => {
hasImageProxy: true,
};
const wrapper = shallow(<ExternalImage {...props}/>);
const {container} = render(<ExternalImage {...props}/>);
expect(props.children).toHaveBeenCalledWith(Client4.getBaseRoute() + '/image?url=' + encodeURIComponent(props.src));
expect(wrapper.find('img').exists()).toBe(true);
expect(container.querySelector('img')).toBeInTheDocument();
});
});
@@ -1,77 +1,128 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/file_attachment/FilenameOverlay should match snapshot, compact display 1`] = `
<WithTooltip
title="test_filename"
>
<div>
<a
className="post-image__name btn btn-icon btn-sm"
class="post-image__name btn btn-icon btn-sm"
href="#"
onClick={[MockFunction]}
rel="noopener noreferrer"
>
<AttachmentIcon
className="icon"
/>
<span
class="icon"
>
<svg
aria-label="Attachment Icon"
height="16px"
role="img"
viewBox="0 0 16 16"
width="16px"
>
<g
fill="inherit"
fill-rule="evenodd"
>
<g
fill="inherit"
fill-rule="nonzero"
transform="translate(-1029.000000, -954.000000)"
>
<g
transform="translate(25.000000, 937.000000)"
>
<g
transform="translate(1004.000000, 17.000000)"
>
<path
d="M5.35,15.56 C3.98,15.56 2.61,15.039 1.567,13.997 C0.557,12.984 0,11.642 0,10.212 C0,8.783 0.557,7.44 1.566,6.429 L6.869,1.126 C8.371,-0.376 10.812,-0.375 12.314,1.125 C13.815,2.627 13.815,5.069 12.314,6.57 L7.011,11.873 C6.094,12.792 4.603,12.79 3.687,11.873 C2.771,10.958 2.771,9.467 3.687,8.551 L8.99,3.248 C9.323,2.916 9.861,2.916 10.193,3.248 C10.525,3.579 10.525,4.118 10.193,4.449 L4.89,9.752 C4.637,10.006 4.637,10.418 4.89,10.672 C5.143,10.923 5.555,10.925 5.809,10.672 L11.113,5.369 C11.952,4.53 11.952,3.166 11.113,2.327 C10.276,1.49 8.911,1.488 8.073,2.327 L2.769,7.631 C2.079,8.32 1.699,9.237 1.699,10.212 C1.699,11.188 2.079,12.104 2.768,12.794 C4.19,14.216 6.502,14.216 7.925,12.798 L7.929,12.794 C7.929,12.793 7.929,12.793 7.929,12.793 L15.355,5.369 C15.687,5.037 16.224,5.037 16.556,5.369 C16.888,5.7 16.888,6.239 16.556,6.57 L8.779,14.348 L8.761,14.332 C7.776,15.15 6.562,15.56 5.35,15.56 Z"
/>
</g>
</g>
</g>
</g>
</svg>
</span>
test_filename
</a>
</WithTooltip>
</div>
`;
exports[`components/file_attachment/FilenameOverlay should match snapshot, standard but not downloadable 1`] = `
<span
className="post-image__name"
>
test_filename
</span>
<div>
<span
class="post-image__name"
>
test_filename
</span>
</div>
`;
exports[`components/file_attachment/FilenameOverlay should match snapshot, standard display 1`] = `
<div
className="post-image__name"
>
<WithTooltip
title={
Object {
"defaultMessage": "Download",
"id": "view_image_popover.download",
}
}
<div>
<div
class="post-image__name"
>
<ForwardRef
<a
aria-label="download"
className="btn btn-icon btn-sm"
class="btn btn-icon btn-sm"
download="test_filename"
href="/api/v4/files/thumbnail_id?download=1"
location="filename_overlay"
rel="noopener noreferrer"
target="_blank"
>
test_filename
</ForwardRef>
</WithTooltip>
</a>
</div>
</div>
`;
exports[`components/file_attachment/FilenameOverlay should match snapshot, with Download icon as children 1`] = `
<div
className="post-image__name"
>
<WithTooltip
title={
Object {
"defaultMessage": "Download",
"id": "view_image_popover.download",
}
}
<div>
<div
class="post-image__name"
>
<ForwardRef
<a
aria-label="download"
className="btn btn-icon btn-sm"
class="btn btn-icon btn-sm"
download="test_filename"
href="/api/v4/files/thumbnail_id?download=1"
location="filename_overlay"
rel="noopener noreferrer"
target="_blank"
>
<AttachmentIcon />
</ForwardRef>
</WithTooltip>
<span>
<svg
aria-label="Attachment Icon"
height="16px"
role="img"
viewBox="0 0 16 16"
width="16px"
>
<g
fill="inherit"
fill-rule="evenodd"
>
<g
fill="inherit"
fill-rule="nonzero"
transform="translate(-1029.000000, -954.000000)"
>
<g
transform="translate(25.000000, 937.000000)"
>
<g
transform="translate(1004.000000, 17.000000)"
>
<path
d="M5.35,15.56 C3.98,15.56 2.61,15.039 1.567,13.997 C0.557,12.984 0,11.642 0,10.212 C0,8.783 0.557,7.44 1.566,6.429 L6.869,1.126 C8.371,-0.376 10.812,-0.375 12.314,1.125 C13.815,2.627 13.815,5.069 12.314,6.57 L7.011,11.873 C6.094,12.792 4.603,12.79 3.687,11.873 C2.771,10.958 2.771,9.467 3.687,8.551 L8.99,3.248 C9.323,2.916 9.861,2.916 10.193,3.248 C10.525,3.579 10.525,4.118 10.193,4.449 L4.89,9.752 C4.637,10.006 4.637,10.418 4.89,10.672 C5.143,10.923 5.555,10.925 5.809,10.672 L11.113,5.369 C11.952,4.53 11.952,3.166 11.113,2.327 C10.276,1.49 8.911,1.488 8.073,2.327 L2.769,7.631 C2.079,8.32 1.699,9.237 1.699,10.212 C1.699,11.188 2.079,12.104 2.768,12.794 C4.19,14.216 6.502,14.216 7.925,12.798 L7.929,12.794 C7.929,12.793 7.929,12.793 7.929,12.793 L15.355,5.369 C15.687,5.037 16.224,5.037 16.556,5.369 C16.888,5.7 16.888,6.239 16.556,6.57 L8.779,14.348 L8.761,14.332 C7.776,15.15 6.562,15.56 5.35,15.56 Z"
/>
</g>
</g>
</g>
</g>
</svg>
</span>
</a>
</div>
</div>
`;
@@ -1,51 +1,53 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`FileThumbnail should render a normal-sized image 1`] = `
<div
className="post-image normal"
style={
Object {
"backgroundImage": "url(/api/v4/files/thumbnail_id/thumbnail)",
"backgroundSize": "cover",
}
}
/>
<div>
<div
class="post-image normal"
style="background-image: url(\\"/api/v4/files/thumbnail_id/thumbnail\\"); background-size: cover;"
/>
</div>
`;
exports[`FileThumbnail should render a small image 1`] = `
<div
className="post-image small"
style={
Object {
"backgroundImage": "url(/api/v4/files/thumbnail_id/thumbnail)",
"backgroundSize": "cover",
}
}
/>
<div>
<div
class="post-image small"
style="background-image: url(\\"/api/v4/files/thumbnail_id/thumbnail\\"); background-size: cover;"
/>
</div>
`;
exports[`FileThumbnail should render an icon for a PDF 1`] = `
<div
className="file-icon pdf"
/>
<div>
<div
class="file-icon pdf"
/>
</div>
`;
exports[`FileThumbnail should render an icon for a PSD (MM-67077) 1`] = `
<div
className="file-icon generic"
/>
<div>
<div
class="file-icon generic"
/>
</div>
`;
exports[`FileThumbnail should render an icon for an SVG when SVG previews are disabled 1`] = `
<div
className="file-icon generic"
/>
<div>
<div
class="file-icon generic"
/>
</div>
`;
exports[`FileThumbnail should render an svg when svg previews are enabled 1`] = `
<img
alt="file thumbnail image"
className="post-image normal"
src="/api/v4/files/thumbnail_id"
/>
<div>
<img
alt="file thumbnail image"
class="post-image normal"
src="/api/v4/files/thumbnail_id"
/>
</div>
`;
@@ -1,9 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {render} from 'tests/react_testing_utils';
import FileThumbnail from './file_thumbnail';
describe('FileThumbnail', () => {
@@ -29,11 +30,11 @@ describe('FileThumbnail', () => {
};
test('should render a small image', () => {
const wrapper = shallow(
const {container} = render(
<FileThumbnail {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should render a normal-sized image', () => {
@@ -46,11 +47,11 @@ describe('FileThumbnail', () => {
},
};
const wrapper = shallow(
const {container} = render(
<FileThumbnail {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should render an svg when svg previews are enabled', () => {
@@ -63,12 +64,12 @@ describe('FileThumbnail', () => {
enableSVGs: true,
};
const wrapper = shallow(
const {container} = render(
<FileThumbnail {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('img').exists()).toBe(true);
expect(container).toMatchSnapshot();
expect(container.querySelector('img')).toBeInTheDocument();
});
test('should render an icon for an SVG when SVG previews are disabled', () => {
@@ -81,12 +82,12 @@ describe('FileThumbnail', () => {
enableSVGs: false,
};
const wrapper = shallow(
const {container} = render(
<FileThumbnail {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('div.file-icon').exists()).toBe(true);
expect(container).toMatchSnapshot();
expect(container.querySelector('div.file-icon')).toBeInTheDocument();
});
test('should render an icon for a PDF', () => {
@@ -98,12 +99,12 @@ describe('FileThumbnail', () => {
},
};
const wrapper = shallow(
const {container} = render(
<FileThumbnail {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('div.file-icon').exists()).toBe(true);
expect(container).toMatchSnapshot();
expect(container.querySelector('div.file-icon')).toBeInTheDocument();
});
test('should render an icon for a PSD (MM-67077)', () => {
@@ -115,11 +116,11 @@ describe('FileThumbnail', () => {
},
};
const wrapper = shallow(
const {container} = render(
<FileThumbnail {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('div.file-icon').exists()).toBe(true);
expect(container).toMatchSnapshot();
expect(container.querySelector('div.file-icon')).toBeInTheDocument();
});
});
@@ -1,12 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import FilenameOverlay from 'components/file_attachment/filename_overlay';
import AttachmentIcon from 'components/widgets/icons/attachment_icon';
import {render, renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
describe('components/file_attachment/FilenameOverlay', () => {
function emptyFunction() {} //eslint-disable-line no-empty-function
const fileInfo = {
@@ -35,47 +36,47 @@ describe('components/file_attachment/FilenameOverlay', () => {
};
test('should match snapshot, standard display', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<FilenameOverlay {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, compact display', () => {
test('should match snapshot, compact display', async () => {
const handleImageClick = jest.fn();
const props = {...baseProps, compactDisplay: true, handleImageClick};
const wrapper = shallow(
const {container} = renderWithContext(
<FilenameOverlay {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find(AttachmentIcon).exists()).toBe(true);
expect(container).toMatchSnapshot();
expect(container.querySelector('.icon')).toBeInTheDocument();
wrapper.find('a').first().simulate('click');
await userEvent.click(screen.getByRole('link'));
expect(handleImageClick).toHaveBeenCalledTimes(1);
});
test('should match snapshot, with Download icon as children', () => {
const props = {...baseProps, canDownload: true};
const wrapper = shallow(
const {container} = renderWithContext(
<FilenameOverlay {...props}>
<AttachmentIcon/>
</FilenameOverlay>,
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find(AttachmentIcon).exists()).toBe(true);
expect(container).toMatchSnapshot();
expect(screen.getByRole('img', {name: 'Attachment Icon'})).toBeInTheDocument();
});
test('should match snapshot, standard but not downloadable', () => {
const props = {...baseProps, canDownload: false};
const wrapper = shallow(
const {container} = render(
<FilenameOverlay {...props}>
<AttachmentIcon/>
</FilenameOverlay>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,65 +1,69 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/FileInfoPreview should match snapshot, can download files 1`] = `
<div
className="file-details__container"
>
<a
className="file-details__preview"
href="https://pre-release.mattermost.com/api/v4/files/rqir81f7a7ft8m6j6ej7g1txuo"
>
<span
className="file-details__preview-helper"
/>
<img
alt="file preview"
src=""
/>
</a>
<div>
<div
className="file-details"
class="file-details__container"
>
<div
className="file-details__name"
<a
class="file-details__preview"
href="https://pre-release.mattermost.com/api/v4/files/rqir81f7a7ft8m6j6ej7g1txuo"
>
Test Image
</div>
<span
class="file-details__preview-helper"
/>
<img
alt="file preview"
src=""
/>
</a>
<div
className="file-details__info"
class="file-details"
>
File type JPG, Size 100B
<div
class="file-details__name"
>
Test Image
</div>
<div
class="file-details__info"
>
File type JPG, Size 100B
</div>
</div>
</div>
</div>
`;
exports[`components/FileInfoPreview should match snapshot, cannot download files 1`] = `
<div
className="file-details__container"
>
<span
className="file-details__preview"
<div>
<div
class="file-details__container"
>
<span
className="file-details__preview-helper"
/>
<img
alt="file preview"
src=""
/>
</span>
<div
className="file-details"
>
<div
className="file-details__name"
class="file-details__preview"
>
Test Image 2
</div>
<span
class="file-details__preview-helper"
/>
<img
alt="file preview"
src=""
/>
</span>
<div
className="file-details__info"
class="file-details"
>
File type PNG, Size 200B
<div
class="file-details__name"
>
Test Image 2
</div>
<div
class="file-details__info"
>
File type PNG, Size 200B
</div>
</div>
</div>
</div>
@@ -1,16 +1,17 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import type {FileInfo} from '@mattermost/types/files';
import FileInfoPreview from 'components/file_info_preview/file_info_preview';
import {renderWithContext} from 'tests/react_testing_utils';
import FileInfoPreview from './file_info_preview';
describe('components/FileInfoPreview', () => {
test('should match snapshot, can download files', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<FileInfoPreview
fileUrl='https://pre-release.mattermost.com/api/v4/files/rqir81f7a7ft8m6j6ej7g1txuo'
fileInfo={{name: 'Test Image', size: 100, extension: 'jpg'} as FileInfo}
@@ -18,11 +19,11 @@ describe('components/FileInfoPreview', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, cannot download files', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<FileInfoPreview
fileUrl='https://pre-release.mattermost.com/api/v4/files/aasf9afshaskj1asf91jasf0a0'
fileInfo={{name: 'Test Image 2', size: 200, extension: 'png'} as FileInfo}
@@ -30,6 +31,6 @@ describe('components/FileInfoPreview', () => {
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,415 +1,228 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/file_search_result/FileSearchResultItem should match snapshot 1`] = `
<div
className="search-item__container"
data-testid="search-item-container"
>
<button
className="FileSearchResultItem"
onClick={[Function]}
<div>
<div
class="search-item__container"
data-testid="search-item-container"
>
<Connect(Component)
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "jpg",
"has_preview_image": true,
"height": 200,
"id": "file_info_id",
"mime_type": "mime_type",
"name": "name",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
/>
<div
className="fileData"
<button
class="FileSearchResultItem"
>
<div
className="fileDataName"
>
name
</div>
class="post-image normal"
style="background-image: url(\\"/api/v4/files/file_info_id/thumbnail\\"); background-size: cover;"
/>
<div
className="fileMetadata"
class="fileData"
>
<span>
1B
</span>
<span>
•
</span>
<Connect(injectIntl(Timestamp))
ranges={
Array [
Object {
"display": <Memo(MemoizedFormattedMessage)
defaultMessage="Today"
id="date_separator.today"
/>,
"equals": Array [
"day",
0,
],
},
Object {
"display": <Memo(MemoizedFormattedMessage)
defaultMessage="Yesterday"
id="date_separator.yesterday"
/>,
"equals": Array [
"day",
-1,
],
},
]
}
value={1}
/>
<div
class="fileDataName"
>
name
</div>
<div
class="fileMetadata"
>
<span>
1B
</span>
<span>
•
</span>
<time
datetime="1970-01-01T00:00:00.001"
>
January 01, 1970 at 12:00 AM
</time>
</div>
</div>
</div>
<WithTooltip
title={
Object {
"defaultMessage": "Download",
"id": "file_search_result_item.download",
}
}
>
<a
className="action-icon download-icon btn btn-icon btn-sm"
class="action-icon download-icon btn btn-icon btn-sm"
href="/api/v4/files/file_info_id?download=1"
onClick={[Function]}
>
<i
className="icon icon-download-outline"
class="icon icon-download-outline"
/>
</a>
</WithTooltip>
</button>
</button>
</div>
</div>
`;
exports[`components/file_search_result/FileSearchResultItem should match snapshot with DM 1`] = `
<div
className="search-item__container"
data-testid="search-item-container"
>
<button
className="FileSearchResultItem"
onClick={[Function]}
<div>
<div
class="search-item__container"
data-testid="search-item-container"
>
<Connect(Component)
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "jpg",
"has_preview_image": true,
"height": 200,
"id": "file_info_id",
"mime_type": "mime_type",
"name": "name",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
/>
<div
className="fileData"
<button
class="FileSearchResultItem"
>
<div
className="fileDataName"
>
name
</div>
class="post-image normal"
style="background-image: url(\\"/api/v4/files/file_info_id/thumbnail\\"); background-size: cover;"
/>
<div
className="fileMetadata"
class="fileData"
>
<Memo(Tag)
className="file-search-channel-name"
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Direct Message"
id="search_item.file_tag.direct_message"
/>
}
/>
<span>
1B
</span>
<span>
•
</span>
<Connect(injectIntl(Timestamp))
ranges={
Array [
Object {
"display": <Memo(MemoizedFormattedMessage)
defaultMessage="Today"
id="date_separator.today"
/>,
"equals": Array [
"day",
0,
],
},
Object {
"display": <Memo(MemoizedFormattedMessage)
defaultMessage="Yesterday"
id="date_separator.yesterday"
/>,
"equals": Array [
"day",
-1,
],
},
]
}
value={1}
/>
<div
class="fileDataName"
>
name
</div>
<div
class="fileMetadata"
>
<div
class="TagWrapper-kezsCu gCuUKH Tag Tag--xs file-search-channel-name"
>
<span
class="TagText-bWoQAI beIloY"
>
Direct Message
</span>
</div>
<span>
1B
</span>
<span>
•
</span>
<time
datetime="1970-01-01T00:00:00.001"
>
January 01, 1970 at 12:00 AM
</time>
</div>
</div>
</div>
<WithTooltip
title={
Object {
"defaultMessage": "Download",
"id": "file_search_result_item.download",
}
}
>
<a
className="action-icon download-icon btn btn-icon btn-sm"
class="action-icon download-icon btn btn-icon btn-sm"
href="/api/v4/files/file_info_id?download=1"
onClick={[Function]}
>
<i
className="icon icon-download-outline"
class="icon icon-download-outline"
/>
</a>
</WithTooltip>
</button>
</button>
</div>
</div>
`;
exports[`components/file_search_result/FileSearchResultItem should match snapshot with GM 1`] = `
<div
className="search-item__container"
data-testid="search-item-container"
>
<button
className="FileSearchResultItem"
onClick={[Function]}
<div>
<div
class="search-item__container"
data-testid="search-item-container"
>
<Connect(Component)
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "jpg",
"has_preview_image": true,
"height": 200,
"id": "file_info_id",
"mime_type": "mime_type",
"name": "name",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
/>
<div
className="fileData"
<button
class="FileSearchResultItem"
>
<div
className="fileDataName"
>
name
</div>
class="post-image normal"
style="background-image: url(\\"/api/v4/files/file_info_id/thumbnail\\"); background-size: cover;"
/>
<div
className="fileMetadata"
class="fileData"
>
<Memo(Tag)
className="file-search-channel-name"
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Group Message"
id="search_item.file_tag.group_message"
/>
}
/>
<span>
1B
</span>
<span>
•
</span>
<Connect(injectIntl(Timestamp))
ranges={
Array [
Object {
"display": <Memo(MemoizedFormattedMessage)
defaultMessage="Today"
id="date_separator.today"
/>,
"equals": Array [
"day",
0,
],
},
Object {
"display": <Memo(MemoizedFormattedMessage)
defaultMessage="Yesterday"
id="date_separator.yesterday"
/>,
"equals": Array [
"day",
-1,
],
},
]
}
value={1}
/>
<div
class="fileDataName"
>
name
</div>
<div
class="fileMetadata"
>
<div
class="TagWrapper-kezsCu gCuUKH Tag Tag--xs file-search-channel-name"
>
<span
class="TagText-bWoQAI beIloY"
>
Group Message
</span>
</div>
<span>
1B
</span>
<span>
•
</span>
<time
datetime="1970-01-01T00:00:00.001"
>
January 01, 1970 at 12:00 AM
</time>
</div>
</div>
</div>
<WithTooltip
title={
Object {
"defaultMessage": "Download",
"id": "file_search_result_item.download",
}
}
>
<a
className="action-icon download-icon btn btn-icon btn-sm"
class="action-icon download-icon btn btn-icon btn-sm"
href="/api/v4/files/file_info_id?download=1"
onClick={[Function]}
>
<i
className="icon icon-download-outline"
class="icon icon-download-outline"
/>
</a>
</WithTooltip>
</button>
</button>
</div>
</div>
`;
exports[`components/file_search_result/FileSearchResultItem should match snapshot with channel name 1`] = `
<div
className="search-item__container"
data-testid="search-item-container"
>
<button
className="FileSearchResultItem"
onClick={[Function]}
<div>
<div
class="search-item__container"
data-testid="search-item-container"
>
<Connect(Component)
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "jpg",
"has_preview_image": true,
"height": 200,
"id": "file_info_id",
"mime_type": "mime_type",
"name": "name",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
/>
<div
className="fileData"
<button
class="FileSearchResultItem"
>
<div
className="fileDataName"
>
name
</div>
class="post-image normal"
style="background-image: url(\\"/api/v4/files/file_info_id/thumbnail\\"); background-size: cover;"
/>
<div
className="fileMetadata"
class="fileData"
>
<Memo(Tag)
className="file-search-channel-name"
text="test"
/>
<span>
1B
</span>
<span>
•
</span>
<Connect(injectIntl(Timestamp))
ranges={
Array [
Object {
"display": <Memo(MemoizedFormattedMessage)
defaultMessage="Today"
id="date_separator.today"
/>,
"equals": Array [
"day",
0,
],
},
Object {
"display": <Memo(MemoizedFormattedMessage)
defaultMessage="Yesterday"
id="date_separator.yesterday"
/>,
"equals": Array [
"day",
-1,
],
},
]
}
value={1}
/>
<div
class="fileDataName"
>
name
</div>
<div
class="fileMetadata"
>
<div
class="TagWrapper-kezsCu gCuUKH Tag Tag--xs file-search-channel-name"
>
<span
class="TagText-bWoQAI beIloY"
>
test
</span>
</div>
<span>
1B
</span>
<span>
•
</span>
<time
datetime="1970-01-01T00:00:00.001"
>
January 01, 1970 at 12:00 AM
</time>
</div>
</div>
</div>
<WithTooltip
title={
Object {
"defaultMessage": "Download",
"id": "file_search_result_item.download",
}
}
>
<a
className="action-icon download-icon btn btn-icon btn-sm"
class="action-icon download-icon btn btn-icon btn-sm"
href="/api/v4/files/file_info_id?download=1"
onClick={[Function]}
>
<i
className="icon icon-download-outline"
class="icon icon-download-outline"
/>
</a>
</WithTooltip>
</button>
</button>
</div>
</div>
`;
@@ -1,12 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import type {ShallowWrapper} from 'enzyme';
import React from 'react';
import type {ChannelType} from '@mattermost/types/channels';
import {renderWithContext} from 'tests/react_testing_utils';
import Constants from 'utils/constants';
import {TestHelper} from 'utils/test_helper';
@@ -28,11 +27,11 @@ describe('components/file_search_result/FileSearchResultItem', () => {
};
test('should match snapshot', () => {
const wrapper: ShallowWrapper<any, any, FileSearchResultItem> = shallow(
const {container} = renderWithContext(
<FileSearchResultItem {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with channel name', () => {
@@ -41,11 +40,11 @@ describe('components/file_search_result/FileSearchResultItem', () => {
channelDisplayName: 'test',
};
const wrapper: ShallowWrapper<any, any, FileSearchResultItem> = shallow(
const {container} = renderWithContext(
<FileSearchResultItem {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with DM', () => {
@@ -55,11 +54,11 @@ describe('components/file_search_result/FileSearchResultItem', () => {
channelType: Constants.DM_CHANNEL as ChannelType,
};
const wrapper: ShallowWrapper<any, any, FileSearchResultItem> = shallow(
const {container} = renderWithContext(
<FileSearchResultItem {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with GM', () => {
@@ -69,10 +68,10 @@ describe('components/file_search_result/FileSearchResultItem', () => {
channelType: Constants.GM_CHANNEL as ChannelType,
};
const wrapper: ShallowWrapper<any, any, FileSearchResultItem> = shallow(
const {container} = renderWithContext(
<FileSearchResultItem {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,78 +1,75 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/FileUploadOverlay should match snapshot when file upload is showing with no overlay type 1`] = `
<div
className="file-overlay hidden"
id="fileUploadOverlay"
>
<div>
<div
className="overlay__indent"
class="file-overlay hidden"
id="fileUploadOverlay"
>
<div
className="overlay__circle vertical"
class="overlay__indent"
>
<img
alt=""
className="overlay__files"
loading="lazy"
src=""
/>
<MemoizedFormattedMessage
defaultMessage="Drop a file to upload it."
id="upload_overlay.info"
/>
<div
class="overlay__circle vertical"
>
<img
alt=""
class="overlay__files"
loading="lazy"
src=""
/>
Drop a file to upload it.
</div>
</div>
</div>
</div>
`;
exports[`components/FileUploadOverlay should match snapshot when file upload is showing with overlay type of center 1`] = `
<div
className="file-overlay hidden center-file-overlay"
id="fileUploadOverlay"
>
<div>
<div
className="overlay__indent"
class="file-overlay hidden center-file-overlay"
id="fileUploadOverlay"
>
<div
className="overlay__circle vertical"
class="overlay__indent"
>
<img
alt=""
className="overlay__files"
loading="lazy"
src=""
/>
<MemoizedFormattedMessage
defaultMessage="Drop a file to upload it."
id="upload_overlay.info"
/>
<div
class="overlay__circle vertical"
>
<img
alt=""
class="overlay__files"
loading="lazy"
src=""
/>
Drop a file to upload it.
</div>
</div>
</div>
</div>
`;
exports[`components/FileUploadOverlay should match snapshot when file upload is showing with overlay type of right 1`] = `
<div
className="file-overlay hidden right-file-overlay"
id="fileUploadOverlay"
>
<div>
<div
className="overlay__indent"
class="file-overlay hidden right-file-overlay"
id="fileUploadOverlay"
>
<div
className="overlay__circle vertical"
class="overlay__indent"
>
<img
alt=""
className="overlay__files"
loading="lazy"
src=""
/>
<MemoizedFormattedMessage
defaultMessage="Drop a file to upload it."
id="upload_overlay.info"
/>
<div
class="overlay__circle vertical"
>
<img
alt=""
class="overlay__files"
loading="lazy"
src=""
/>
Drop a file to upload it.
</div>
</div>
</div>
</div>
@@ -1,42 +1,43 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import FileUploadOverlay from 'components/file_upload_overlay/index';
import {renderWithContext} from 'tests/react_testing_utils';
import FileUploadOverlay from './index';
describe('components/FileUploadOverlay', () => {
test('should match snapshot when file upload is showing with no overlay type', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<FileUploadOverlay
overlayType=''
id={'fileUploadOverlay'}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when file upload is showing with overlay type of right', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<FileUploadOverlay
overlayType='right'
id={'fileUploadOverlay'}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when file upload is showing with overlay type of center', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<FileUploadOverlay
overlayType='center'
id={'fileUploadOverlay'}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,10 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {mount} from 'enzyme';
import React from 'react';
import {IntlProvider} from 'react-intl';
import {render} from 'tests/react_testing_utils';
import FormattedMarkdownMessage from './formatted_markdown_message';
jest.unmock('react-intl');
@@ -15,8 +16,8 @@ describe('components/FormattedMarkdownMessage', () => {
id: 'test.foo',
defaultMessage: '**bold** *italic* [link](https://mattermost.com/) <br/> [link target blank](!https://mattermost.com/)',
};
const wrapper = mount(wrapProvider(<FormattedMarkdownMessage {...props}/>));
expect(wrapper).toMatchSnapshot();
const {container} = render(wrapProvider(<FormattedMarkdownMessage {...props}/>));
expect(container).toMatchSnapshot();
});
test('should backup to default', () => {
@@ -24,8 +25,8 @@ describe('components/FormattedMarkdownMessage', () => {
id: 'xxx',
defaultMessage: 'testing default message',
};
const wrapper = mount(wrapProvider(<FormattedMarkdownMessage {...props}/>));
expect(wrapper).toMatchSnapshot();
const {container} = render(wrapProvider(<FormattedMarkdownMessage {...props}/>));
expect(container).toMatchSnapshot();
});
test('should escape non-BR', () => {
@@ -37,8 +38,8 @@ describe('components/FormattedMarkdownMessage', () => {
script: (...content: string[]) => `<script>${content}</script>`,
},
};
const wrapper = mount(wrapProvider(<FormattedMarkdownMessage {...props}/>));
expect(wrapper).toMatchSnapshot();
const {container} = render(wrapProvider(<FormattedMarkdownMessage {...props}/>));
expect(container).toMatchSnapshot();
});
test('values should work', () => {
@@ -49,8 +50,8 @@ describe('components/FormattedMarkdownMessage', () => {
petName: 'sweetie',
},
};
const wrapper = mount(wrapProvider(<FormattedMarkdownMessage {...props}/>));
expect(wrapper).toMatchSnapshot();
const {container} = render(wrapProvider(<FormattedMarkdownMessage {...props}/>));
expect(container).toMatchSnapshot();
});
test('should allow to disable links', () => {
@@ -62,12 +63,12 @@ describe('components/FormattedMarkdownMessage', () => {
},
disableLinks: true,
};
const wrapper = mount(wrapProvider(<FormattedMarkdownMessage {...props}/>));
expect(wrapper).toMatchSnapshot();
const {container} = render(wrapProvider(<FormattedMarkdownMessage {...props}/>));
expect(container).toMatchSnapshot();
});
});
export function wrapProvider(el: JSX.Element) {
function wrapProvider(el: JSX.Element) {
const enTranslationData = {
'test.foo': '**bold** *italic* [link](https://mattermost.com/) <br/> [link target blank](!https://mattermost.com/)',
'test.bar': '<b>hello</b> <script>var malicious = true;</script> world!',
@@ -79,6 +80,6 @@ export function wrapProvider(el: JSX.Element) {
messages={enTranslationData}
>
{el}
</IntlProvider>)
;
</IntlProvider>
);
}
@@ -1,14 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {Modal} from 'react-bootstrap';
import GetLinkModal from 'components/get_link_modal';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import {act} from 'tests/react_testing_utils';
import {renderWithContext, act, screen, userEvent} from 'tests/react_testing_utils';
describe('components/GetLinkModal', () => {
const onHide = jest.fn();
@@ -32,87 +29,116 @@ describe('components/GetLinkModal', () => {
const helpText = 'help text';
const props = {...requiredProps, helpText};
const wrapper = shallow(
const {baseElement} = renderWithContext(
<GetLinkModal {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(baseElement).toMatchSnapshot();
});
test('should match snapshot when helpText is not set', () => {
const wrapper = shallow(
const {baseElement} = renderWithContext(
<GetLinkModal {...requiredProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(baseElement).toMatchSnapshot();
});
test('should have called onHide', () => {
test('should have called onHide when close button is clicked', async () => {
const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime});
const newOnHide = jest.fn();
const props = {...requiredProps, onHide: newOnHide};
const wrapper = shallow(
const {baseElement} = renderWithContext(
<GetLinkModal {...props}/>,
);
wrapper.find(Modal).first().props().onHide();
// Click the modal header X button (triggers Modal's onHide)
const modalCloseButton = baseElement.querySelector('.close') as HTMLElement;
await user.click(modalCloseButton);
expect(newOnHide).toHaveBeenCalledTimes(1);
expect(wrapper.state('copiedLink')).toBe(false);
wrapper.setProps({show: true});
wrapper.setState({copiedLink: true});
expect(wrapper).toMatchSnapshot();
expect(wrapper.state('copiedLink')).toBe(true);
// Copy link to set copiedLink state to true
const copyButton = screen.getByRole('button', {name: /Copy Link/});
await user.click(copyButton);
expect(copyButton).toHaveTextContent('Copied');
expect(copyButton).toHaveClass('btn-success');
wrapper.find('#linkModalCloseButton').simulate('click');
// Take snapshot with copied state
expect(baseElement).toMatchSnapshot();
// Click the footer close button
const footerCloseButton = baseElement.querySelector('#linkModalCloseButton') as HTMLElement;
await user.click(footerCloseButton);
expect(newOnHide).toHaveBeenCalledTimes(2);
expect(wrapper).toMatchSnapshot();
expect(wrapper.state('copiedLink')).toBe(false);
// Take snapshot after closing (copiedLink should be reset)
expect(baseElement).toMatchSnapshot();
// Verify copiedLink was reset (button should show Copy Link, not Copied)
expect(copyButton).toHaveTextContent('Copy Link');
expect(copyButton).not.toHaveClass('btn-success');
});
test('should have handle copyLink', () => {
const wrapper = mountWithIntl(
test('should have handle copyLink', async () => {
const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime});
const {baseElement} = renderWithContext(
<GetLinkModal {...requiredProps}/>,
);
wrapper.find('#linkModalTextArea').simulate('click');
expect(wrapper.state('copiedLink')).toBe(true);
});
test('should change button state when copying', () => {
const wrapper = mountWithIntl(
<GetLinkModal {...requiredProps}/>,
);
const textarea = baseElement.querySelector('#linkModalTextArea') as HTMLElement;
const copyButton = screen.getByRole('button', {name: /Copy Link/});
// Initial state
expect(wrapper.find('#linkModalCopyLink').text()).toContain('Copy Link');
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-primary')).toBe(true);
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-success')).toBe(false);
expect(copyButton).toHaveTextContent('Copy Link');
// Click on the textarea triggers copyLink
await user.click(textarea);
// Button should now show "Copied"
expect(copyButton).toHaveTextContent('Copied');
expect(copyButton).toHaveClass('btn-success');
});
test('should change button state when copying', async () => {
const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime});
renderWithContext(
<GetLinkModal {...requiredProps}/>,
);
const copyButton = screen.getByRole('button', {name: /Copy Link/});
// Initial state
expect(copyButton).toHaveTextContent('Copy Link');
expect(copyButton).toHaveClass('btn-primary');
expect(copyButton).not.toHaveClass('btn-success');
// After copying
wrapper.find('#linkModalCopyLink').simulate('click');
expect(wrapper.find('#linkModalCopyLink').text()).toContain('Copied');
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-primary')).toBe(true);
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-success')).toBe(true);
await user.click(copyButton);
expect(copyButton).toHaveTextContent('Copied');
expect(copyButton).toHaveClass('btn-primary');
expect(copyButton).toHaveClass('btn-success');
// After timeout
act(() => {
jest.advanceTimersByTime(1000);
});
wrapper.update();
expect(wrapper.find('#linkModalCopyLink').text()).toContain('Copy Link');
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-primary')).toBe(true);
expect(wrapper.find('#linkModalCopyLink').hasClass('btn-success')).toBe(false);
expect(copyButton).toHaveTextContent('Copy Link');
expect(copyButton).toHaveClass('btn-primary');
expect(copyButton).not.toHaveClass('btn-success');
});
test('should cleanup timeout on unmount', () => {
const wrapper = mountWithIntl(
test('should cleanup timeout on unmount', async () => {
const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime});
const {unmount} = renderWithContext(
<GetLinkModal {...requiredProps}/>,
);
wrapper.find('#linkModalCopyLink').simulate('click');
expect(wrapper.state('copiedLink')).toBe(true);
const copyButton = screen.getByRole('button', {name: /Copy Link/});
await user.click(copyButton);
expect(copyButton).toHaveTextContent('Copied');
wrapper.unmount();
unmount();
jest.advanceTimersByTime(1000);
// If we get here without errors, the timeout was properly cleaned up
@@ -1,24 +1,38 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/HintToast should match snapshot 1`] = `
<div
className="hint-toast"
data-testid="hint-toast"
>
<div>
<div
className="hint-toast__message"
class="hint-toast"
data-testid="hint-toast"
>
A hint
</div>
<div
className="hint-toast__dismiss"
data-testid="dismissHintToast"
onClick={[Function]}
>
<CloseIcon
className="close-btn"
id="dismissHintToast"
/>
<div
class="hint-toast__message"
>
A hint
</div>
<div
class="hint-toast__dismiss"
data-testid="dismissHintToast"
>
<span
class="close-btn"
id="dismissHintToast"
>
<svg
aria-label="Close Icon"
height="24px"
role="img"
viewBox="0 0 24 24"
width="24px"
>
<path
d="M18 7.209L16.791 6 12 10.791 7.209 6 6 7.209 10.791 12 6 16.791 7.209 18 12 13.209 16.791 18 18 16.791 13.209 12z"
fill-rule="nonzero"
/>
</svg>
</span>
</div>
</div>
</div>
`;
@@ -1,31 +1,32 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {render, screen, userEvent} from 'tests/react_testing_utils';
import {HintToast} from './hint_toast';
describe('components/HintToast', () => {
test('should match snapshot', () => {
const wrapper = shallow(
const {container} = render(
<HintToast
onDismiss={jest.fn()}
>{'A hint'}</HintToast>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should fire onDismiss callback', () => {
test('should fire onDismiss callback', async () => {
const dismissHandler = jest.fn();
const wrapper = shallow(
render(
<HintToast
onDismiss={dismissHandler}
>{'A hint'}</HintToast>,
);
wrapper.find('.hint-toast__dismiss').simulate('click');
await userEvent.click(screen.getByTestId('dismissHintToast'));
expect(dismissHandler).toHaveBeenCalled();
});
@@ -1,11 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import type {Group} from '@mattermost/types/groups';
import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
import ListModal, {DEFAULT_NUM_PER_PAGE} from './list_modal';
@@ -21,7 +21,7 @@ describe('components/ListModal', () => {
const totalCount = mockItems.length + mockItemsPage2.length;
const baseProps = {
loadItems: async (pageNumber: number, searchTerm: string) => {
loadItems: jest.fn(async (pageNumber: number, searchTerm: string) => {
if (searchTerm === mockSearchTerm) {
return {items: mockItemsSearch, totalCount};
}
@@ -29,12 +29,13 @@ describe('components/ListModal', () => {
return {items: mockItems, totalCount};
}
return {items: mockItemsPage2, totalCount};
},
}),
renderRow: (item: Group) => {
return (
<div
className='item'
key={item.id}
data-testid={`item-${item.id}`}
>
{item.id}
</div>
@@ -47,74 +48,151 @@ describe('components/ListModal', () => {
titleBarButtonTextOnClick: () => {},
};
it('should match snapshot', () => {
const wrapper = shallow(
it('should match snapshot', async () => {
const {baseElement} = renderWithContext(
<ListModal {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
setTimeout(() => {
expect(wrapper.state('items')).toEqual(mockItems);
expect(wrapper.state('totalCount')).toEqual(totalCount);
expect(wrapper.state('numPerPage')).toEqual(DEFAULT_NUM_PER_PAGE);
}, 0);
// Wait for items to load
await waitFor(() => {
expect(screen.getByTestId('item-123')).toBeInTheDocument();
});
expect(baseElement).toMatchSnapshot();
// Verify items are rendered
expect(screen.getByTestId('item-123')).toBeInTheDocument();
expect(screen.getByTestId('item-234')).toBeInTheDocument();
// Verify pagination text
expect(screen.getByText(/1 - 2 of 3 total/)).toBeInTheDocument();
});
it('should update numPerPage', () => {
const numPerPage = totalCount - 1;
const props = {...baseProps};
props.numPerPage = numPerPage;
const wrapper = shallow(
it('should update numPerPage', async () => {
const numPerPage = totalCount - 1; // numPerPage = 2
const props = {...baseProps, numPerPage};
renderWithContext(
<ListModal {...props}/>,
);
setTimeout(() => {
expect(wrapper.state('numPerPage')).toEqual(numPerPage);
}, 0);
// Wait for items to load
await waitFor(() => {
expect(screen.getByTestId('item-123')).toBeInTheDocument();
});
// Verify numPerPage is applied by checking Next button appears
// With numPerPage=2 and 2 items loaded, items.length >= numPerPage is true
// so Next button should be visible (unlike default numPerPage=50 where it wouldn't)
expect(screen.getByRole('button', {name: 'Next'})).toBeInTheDocument();
});
it('should match snapshot with title bar button', () => {
const props = {...baseProps};
props.titleBarButtonText = 'Add Foo';
props.titleBarButtonTextOnClick = () => { };
const wrapper = shallow(
<ListModal {...baseProps}/>,
it('should match snapshot with title bar button', async () => {
const props = {
...baseProps,
titleBarButtonText: 'Add Foo',
titleBarButtonOnClick: jest.fn(),
};
const {baseElement} = renderWithContext(
<ListModal {...props}/>,
);
expect(wrapper).toMatchSnapshot();
// Wait for items to load
await waitFor(() => {
expect(screen.getByTestId('item-123')).toBeInTheDocument();
});
expect(baseElement).toMatchSnapshot();
// Verify title bar button is present
expect(screen.getByRole('link', {name: 'Add Foo'})).toBeInTheDocument();
});
test('should have called onHide when handleExit is called', () => {
test('should have called onHide when handleExit is called', async () => {
const onHide = jest.fn();
const props = {...baseProps, onHide};
const wrapper = shallow(
const {baseElement} = renderWithContext(
<ListModal {...props}/>,
);
(wrapper.instance() as ListModal).handleExit();
expect(onHide).toHaveBeenCalledTimes(1);
// Wait for items to load
await waitFor(() => {
expect(screen.getByTestId('item-123')).toBeInTheDocument();
});
// Click the modal close button
const closeButton = baseElement.querySelector('.close') as HTMLElement;
await userEvent.click(closeButton);
// Wait for onHide to be called (called after exit animation)
await waitFor(() => {
expect(onHide).toHaveBeenCalledTimes(1);
});
});
test('paging loads new items', () => {
const wrapper = shallow(
<ListModal {...baseProps}/>,
test('paging loads new items', async () => {
const props = {
...baseProps,
numPerPage: 2, // Set to 2 so Next button appears when we have 2 items
};
renderWithContext(
<ListModal {...props}/>,
);
(wrapper.instance() as ListModal).onNext();
setTimeout(() => {
expect(wrapper.state('page')).toEqual(1);
expect(wrapper.state('items')).toEqual(mockItemsPage2);
}, 0);
(wrapper.instance() as ListModal).onPrev();
setTimeout(() => {
expect(wrapper.state('page')).toEqual(0);
expect(wrapper.state('items')).toEqual(mockItems);
}, 0);
// Wait for initial items to load
await waitFor(() => {
expect(screen.getByTestId('item-123')).toBeInTheDocument();
});
// Verify page 1 items
expect(screen.getByTestId('item-123')).toBeInTheDocument();
expect(screen.getByTestId('item-234')).toBeInTheDocument();
// Click Next
await userEvent.click(screen.getByRole('button', {name: 'Next'}));
// Wait for page 2 items
await waitFor(() => {
expect(screen.getByTestId('item-345')).toBeInTheDocument();
});
// Verify page 1 items are gone and page 2 items are present
expect(screen.queryByTestId('item-123')).not.toBeInTheDocument();
expect(screen.getByTestId('item-345')).toBeInTheDocument();
// Click Previous
await userEvent.click(screen.getByRole('button', {name: 'Previous'}));
// Wait for page 1 items again
await waitFor(() => {
expect(screen.getByTestId('item-123')).toBeInTheDocument();
});
// Verify we're back on page 1
expect(screen.getByTestId('item-123')).toBeInTheDocument();
expect(screen.getByTestId('item-234')).toBeInTheDocument();
});
test('search input', () => {
const wrapper = shallow(
test('search input', async () => {
renderWithContext(
<ListModal {...baseProps}/>,
);
(wrapper.instance() as ListModal).onSearchInput({target: {value: mockSearchTerm}} as React.ChangeEvent<HTMLInputElement>);
setTimeout(() => {
expect(wrapper.state('searchTerm')).toEqual(mockSearchTerm);
expect(wrapper.state('items')).toEqual(mockItemsSearch);
}, 0);
// Wait for initial items to load
await waitFor(() => {
expect(screen.getByTestId('item-123')).toBeInTheDocument();
});
// Type in search input
const searchInput = screen.getByPlaceholderText('search for name');
await userEvent.type(searchInput, mockSearchTerm);
// Wait for filtered items
await waitFor(() => {
// mockItemsSearch contains items with 'ar3' in name: bar31 and bar3
expect(screen.getByTestId('item-123')).toBeInTheDocument(); // bar31 matches
});
// Verify item-234 (bar2) is not present since it doesn't match 'ar3'
expect(screen.queryByTestId('item-234')).not.toBeInTheDocument();
});
});
@@ -1,30 +1,34 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import LoadingImagePreview from 'components/loading_image_preview';
describe('components/LoadingImagePreview', () => {
test('should match snapshot', () => {
const loading = 'Loading';
let progress = 50;
import {render, screen} from 'tests/react_testing_utils';
const wrapper = shallow(
describe('components/LoadingImagePreview', () => {
test('should match snapshot with progress 50%', () => {
const {container} = render(
<LoadingImagePreview
loading={loading}
progress={progress}
loading='Loading'
progress={50}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('span').text()).toBe('Loading 50%');
expect(container).toMatchSnapshot();
expect(screen.getByText('Loading 50%')).toBeInTheDocument();
});
progress = 90;
wrapper.setProps({loading, progress});
test('should match snapshot with progress 90%', () => {
const {container} = render(
<LoadingImagePreview
loading='Loading'
progress={90}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('span').text()).toBe('Loading 90%');
expect(container).toMatchSnapshot();
expect(screen.getByText('Loading 90%')).toBeInTheDocument();
});
});
@@ -1,170 +1,491 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/MarkdownImage should match snapshot 1`] = `
<Connect(Component)
imageMetadata={
Object {
"format": "png",
"frameCount": 0,
"height": 90,
"width": 1041,
}
}
src="/images/logo.png"
>
<Component />
</Connect(Component)>
<div>
<div
class="image-loading__container markdown-inline-img markdown-inline-img--loading"
style="max-width: 1041px;"
>
<svg
style="max-height: 90px; max-width: 1041px; vertical-align: middle;"
viewBox="0 0 1041 90"
xmlns="http://www.w3.org/2000/svg"
/>
</div>
<div
class="file-preview__button"
style="display: none;"
>
<figure
class="image-loaded-container"
>
<img
alt="test image"
aria-label="file thumbnail"
class="markdown-inline-img markdown-inline-img--loading"
height=""
src="/images/logo.png"
tabindex="0"
title="test title"
width=""
/>
<span
class="image-preview-utility-buttons-container image-preview-utility-buttons-container--small-image image-preview-utility-buttons-container--small-image-no-copy-button"
>
<button
aria-label="Copy link"
class="style--none size-aware-image__copy_link"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z"
/>
</svg>
</button>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="/images/logo.png"
rel="noopener noreferrer"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
`;
exports[`components/MarkdownImage should match snapshot for SizeAwareImage dimensions 1`] = `
<injectIntl(SizeAwareImage)
alt="test image"
className="markdown-inline-img markdown-inline-img--hover markdown-inline-img--no-border"
dimensions={
Object {
"format": "jpg",
"frameCount": 0,
"height": 90,
"width": 100,
}
}
height=""
onClick={[Function]}
onImageLoadFail={[Function]}
onImageLoaded={[Function]}
showLoader={false}
src="safeSrc"
title="test title"
width=""
/>
<div>
<div
class="file-preview__button"
style="display: inline-block;"
>
<figure
class="image-loaded-container"
>
<img
alt="test image"
aria-label="file thumbnail"
class="markdown-inline-img markdown-inline-img--hover markdown-inline-img--no-border"
height=""
src="path/image"
tabindex="0"
title="test title"
width=""
/>
<span
class="image-preview-utility-buttons-container"
>
<button
aria-label="Copy link"
class="style--none size-aware-image__copy_link"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z"
/>
</svg>
</button>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="path/image"
rel="noopener noreferrer"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
`;
exports[`components/MarkdownImage should match snapshot for broken link 1`] = `
<Connect(Component)
imageMetadata={
Object {
"format": "png",
"frameCount": 0,
"height": 10,
"width": 10,
}
}
src="brokenLink"
>
<Component />
</Connect(Component)>
<div>
<div
class="image-loading__container markdown-inline-img markdown-inline-img--loading"
style="max-width: 10px;"
>
<svg
style="max-height: 10px; max-width: 10px; vertical-align: middle;"
viewBox="0 0 10 10"
xmlns="http://www.w3.org/2000/svg"
/>
</div>
<div
class="file-preview__button"
style="display: none;"
>
<figure
class="image-loaded-container"
>
<img
alt="test image"
aria-label="file thumbnail"
class="markdown-inline-img markdown-inline-img--loading"
height=""
src="brokenLink"
tabindex="0"
title="test title"
width=""
/>
</figure>
</div>
</div>
`;
exports[`components/MarkdownImage should provide image src as an alt text for MarkdownImageExpand if image has no own alt text 1`] = `
<div>
<Connect(MarkdownImageExpand)
alt="https://example.com/image.png"
imageKey="https://example.com/image.png"
postId="post_id"
<div
class="markdown-image-expand markdown-image-expand--expanded"
>
<injectIntl(SizeAwareImage)
alt=""
className="markdown-inline-img markdown-inline-img--hover cursor--pointer a11y--active"
dimensions={Object {}}
height="250"
onClick={[Function]}
onImageLoadFail={[Function]}
onImageLoaded={[Function]}
showLoader={false}
src="https://example.com/image.png"
title="test title"
width="50"
/>
</Connect(MarkdownImageExpand)>
<button
class="markdown-image-expand__collapse-button"
type="button"
>
<span
class="icon icon-menu-down"
/>
</button>
<div
class="file-preview__button"
style="display: inline-block;"
>
<figure
class="image-loaded-container"
>
<img
alt=""
aria-label="file thumbnail"
class="markdown-inline-img markdown-inline-img--hover cursor--pointer a11y--active"
height="250"
src="https://example.com/image.png"
tabindex="0"
title="test title"
width="50"
/>
<span
class="image-preview-utility-buttons-container image-preview-utility-buttons-container--small-image image-preview-utility-buttons-container--small-image-no-copy-button"
>
<button
aria-label="Copy link"
class="style--none size-aware-image__copy_link"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z"
/>
</svg>
</button>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="https://example.com/image.png"
rel="noopener noreferrer"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
</div>
`;
exports[`components/MarkdownImage should render a link if the source is unsafe 1`] = `
<div
style={
Object {
"display": "inline-block",
}
}
>
<img
alt="test image"
className="markdown-inline-img broken-image"
src=""
/>
<div>
<div
style="display: inline-block;"
>
<img
alt="test image"
class="markdown-inline-img broken-image"
src=""
/>
</div>
</div>
`;
exports[`components/MarkdownImage should render an image with no preview if the source is safe and the image is a link 1`] = `
<div>
<injectIntl(SizeAwareImage)
alt="test image"
className="markdown-inline-img markdown-inline-img--hover markdown-inline-img--no-border"
dimensions={
Object {
"format": "png",
"frameCount": 0,
"height": 90,
"width": 1041,
}
}
height=""
onClick={[Function]}
onImageLoadFail={[Function]}
onImageLoaded={[Function]}
showLoader={false}
src="https://example.com/image.png"
title="test title"
width=""
/>
<div
class="file-preview__button"
style="display: inline-block;"
>
<figure
class="image-loaded-container"
>
<img
alt="test image"
aria-label="file thumbnail"
class="markdown-inline-img markdown-inline-img--hover markdown-inline-img--no-border"
height=""
src="https://example.com/image.png"
tabindex="0"
title="test title"
width=""
/>
<span
class="image-preview-utility-buttons-container"
>
<button
aria-label="Copy link"
class="style--none size-aware-image__copy_link"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z"
/>
</svg>
</button>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="https://example.com/image.png"
rel="noopener noreferrer"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
`;
exports[`components/MarkdownImage should render an image with preview modal if the source is safe 1`] = `
<div>
<injectIntl(SizeAwareImage)
alt="test image"
className="markdown-inline-img markdown-inline-img--hover cursor--pointer a11y--active"
dimensions={
Object {
"format": "png",
"frameCount": 0,
"height": 90,
"width": 1041,
}
}
height=""
onClick={[Function]}
onImageLoadFail={[Function]}
onImageLoaded={[Function]}
showLoader={false}
src="https://example.com/image.png"
title="test title"
width=""
/>
<div
class="file-preview__button"
style="display: inline-block;"
>
<figure
class="image-loaded-container"
>
<img
alt="test image"
aria-label="file thumbnail"
class="markdown-inline-img markdown-inline-img--hover cursor--pointer a11y--active"
height=""
src="https://example.com/image.png"
tabindex="0"
title="test title"
width=""
/>
<span
class="image-preview-utility-buttons-container"
>
<button
aria-label="Copy link"
class="style--none size-aware-image__copy_link"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z"
/>
</svg>
</button>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="https://example.com/image.png"
rel="noopener noreferrer"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
`;
exports[`components/MarkdownImage should render image with MarkdownImageExpand if it is taller than 100px 1`] = `
<div>
<Connect(MarkdownImageExpand)
alt="test image"
imageKey="https://example.com/image.png"
postId="post_id"
<div
class="markdown-image-expand markdown-image-expand--expanded"
>
<injectIntl(SizeAwareImage)
alt="test image"
className="markdown-inline-img markdown-inline-img--hover cursor--pointer a11y--active"
dimensions={Object {}}
height="250"
onClick={[Function]}
onImageLoadFail={[Function]}
onImageLoaded={[Function]}
showLoader={false}
src="https://example.com/image.png"
title="test title"
width="50"
/>
</Connect(MarkdownImageExpand)>
<button
class="markdown-image-expand__collapse-button"
type="button"
>
<span
class="icon icon-menu-down"
/>
</button>
<div
class="file-preview__button"
style="display: inline-block;"
>
<figure
class="image-loaded-container"
>
<img
alt="test image"
aria-label="file thumbnail"
class="markdown-inline-img markdown-inline-img--hover cursor--pointer a11y--active"
height="250"
src="https://example.com/image.png"
tabindex="0"
title="test title"
width="50"
/>
<span
class="image-preview-utility-buttons-container image-preview-utility-buttons-container--small-image image-preview-utility-buttons-container--small-image-no-copy-button"
>
<button
aria-label="Copy link"
class="style--none size-aware-image__copy_link"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z"
/>
</svg>
</button>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="https://example.com/image.png"
rel="noopener noreferrer"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
</div>
`;
@@ -1,12 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import FilePreviewModal from 'components/file_preview_modal';
import SizeAwareImage from 'components/size_aware_image';
import {fireEvent, renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import Constants from 'utils/constants';
import MarkdownImage from './markdown_image';
@@ -36,221 +33,253 @@ describe('components/MarkdownImage', () => {
test('should match snapshot', () => {
const props = {...baseProps, src: '/images/logo.png'};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot for broken link', () => {
const props = {...baseProps,
const props = {
...baseProps,
imageMetadata: {
format: 'png',
height: 10,
width: 10,
frameCount: 0,
},
src: 'brokenLink'};
const wrapper = shallow(
src: 'brokenLink',
};
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should handle load failure properly', () => {
const props = {...baseProps,
const props = {
...baseProps,
imageMetadata: {
format: 'png',
height: 10,
width: 10,
frameCount: 0,
},
src: 'brokenLink'};
const wrapper = shallow(
src: 'brokenLink',
};
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
expect(wrapper.state('loadFailed')).toBe(false);
const img = container.querySelector('img');
expect(img).toBeInTheDocument();
expect(img).not.toHaveClass('broken-image');
(wrapper.instance() as MarkdownImage).handleLoadFail();
// Simulate load failure
fireEvent.error(img!);
expect(wrapper.state('loadFailed')).toBe(true);
// After load failure, image should have broken-image class
expect(container.querySelector('img')).toHaveClass('broken-image');
});
test('should reset loadFailed state after image source is updated', () => {
const props = {...baseProps,
const props = {
...baseProps,
imageMetadata: {
format: 'png',
height: 10,
width: 10,
frameCount: 0,
},
src: 'brokenLink'};
const nextProps = {...baseProps, src: 'https://example.com/image.png'};
const wrapper = shallow(
src: 'brokenLink',
};
const {container, rerender} = renderWithContext(
<MarkdownImage {...props}/>,
);
wrapper.instance().setState({loadFailed: true});
const img = container.querySelector('img');
wrapper.setProps(nextProps);
// Simulate load failure
fireEvent.error(img!);
expect(container.querySelector('img')).toHaveClass('broken-image');
expect(wrapper.state('loadFailed')).toBe(false);
// Update with new source
const nextProps = {...baseProps, src: 'https://example.com/image.png'};
rerender(<MarkdownImage {...nextProps}/>);
// After source update, broken-image class should be removed
expect(container.querySelector('img')).not.toHaveClass('broken-image');
});
test('should render a link if the source is unsafe', () => {
const props = {...baseProps, src: ''};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
expect(wrapper.find('img').props().alt).toBe(props.alt);
expect(wrapper.find('img').props().className).toBe(props.className + ' broken-image');
expect(wrapper).toMatchSnapshot();
const img = container.querySelector('img');
expect(img).toHaveAttribute('alt', props.alt);
expect(img).toHaveClass('broken-image');
expect(container).toMatchSnapshot();
});
test('should handle not loaded state properly', () => {
const props = {...baseProps, src: 'https://example.com/image.png'};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
expect(wrapper.state('loaded')).toBe(false);
const childrenNode = wrapper.props().children(props.src);
// using a div as a workaround because shallow doesn't support react fragments
const childrenWrapper = shallow(<div>{childrenNode}</div>);
expect(childrenWrapper.find(SizeAwareImage)).toHaveLength(1);
expect(childrenWrapper.find(SizeAwareImage).prop('className')).
toEqual(`${props.className} markdown-inline-img--loading`);
// Initially image should have loading class
const img = container.querySelector('img');
expect(img).toHaveClass('markdown-inline-img--loading');
});
test('should handle not loaded state properly in case of a header change system message', () => {
const props = {...baseProps, src: 'https://example.com/image.png', postType: 'system_header_change'};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
expect(wrapper.state('loaded')).toBe(false);
const childrenNode = wrapper.props().children(props.src);
// using a div as a workaround because shallow doesn't support react fragments
const childrenWrapper = shallow(<div>{childrenNode}</div>);
expect(childrenWrapper.find(SizeAwareImage)).toHaveLength(1);
expect(childrenWrapper.find(SizeAwareImage).prop('className')).
toEqual(`${props.className} markdown-inline-img--scaled-down-loading`);
// For header change system message, should have scaled-down-loading class
const img = container.querySelector('img');
expect(img).toHaveClass('markdown-inline-img--scaled-down-loading');
});
test('should set loaded state when img loads and call onImageLoaded prop', () => {
const props = {...baseProps, src: 'https://example.com/image.png'};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
const dimensions = {
height: props.imageMetadata.height,
width: props.imageMetadata.width,
};
expect(wrapper.state('loaded')).toBe(false);
const img = container.querySelector('img');
(wrapper.instance() as MarkdownImage).handleImageLoaded(dimensions);
// Initially should have loading class
expect(img).toHaveClass('markdown-inline-img--loading');
expect(wrapper.state('loaded')).toBe(true);
// Mock naturalHeight/naturalWidth for SizeAwareImage's onImageLoaded callback
Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 1041, configurable: true});
// Simulate successful load
fireEvent.load(img!);
// After load, should not have loading class and onImageLoaded should be called
expect(img).not.toHaveClass('markdown-inline-img--loading');
expect(props.onImageLoaded).toHaveBeenCalledTimes(1);
expect(props.onImageLoaded).toHaveBeenCalledWith(dimensions);
expect(props.onImageLoaded).toHaveBeenCalledWith({height: 90, width: 1041});
});
it('should match snapshot for SizeAwareImage dimensions', () => {
const props = {...baseProps,
const props = {
...baseProps,
imageMetadata: {format: 'jpg', frameCount: 0, width: 100, height: 90},
src: 'path/image',
};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
wrapper.instance().setState({loaded: true});
const img = container.querySelector('img');
const childrenWrapper = wrapper.props().children('safeSrc');
expect(childrenWrapper).toMatchSnapshot();
// Mock naturalHeight/naturalWidth for SizeAwareImage's onImageLoaded callback
Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 100, configurable: true});
// Simulate load
fireEvent.load(img!);
expect(container).toMatchSnapshot();
});
test('should render an image with preview modal if the source is safe', () => {
const props = {...baseProps, src: 'https://example.com/image.png'};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
wrapper.instance().setState({loaded: true});
const childrenNode = wrapper.props().children(props.src);
const img = container.querySelector('img');
// using a div as a workaround because shallow doesn't support react fragments
const childrenWrapper = shallow(<div>{childrenNode}</div>);
// Mock naturalHeight/naturalWidth for SizeAwareImage's onImageLoaded callback
Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 1041, configurable: true});
expect(childrenWrapper.find(SizeAwareImage)).toHaveLength(1);
expect(childrenWrapper.find(SizeAwareImage).prop('className')).
toEqual(`${props.className} markdown-inline-img--hover cursor--pointer a11y--active`);
expect(childrenWrapper).toMatchSnapshot();
// Simulate load
fireEvent.load(img!);
// After load, should have hover and cursor classes for clickable preview
expect(img).toHaveClass('markdown-inline-img--hover');
expect(img).toHaveClass('cursor--pointer');
expect(img).toHaveClass('a11y--active');
expect(container).toMatchSnapshot();
});
test('should render an image with no preview if the source is safe and the image is a link', () => {
const props = {...baseProps, src: 'https://example.com/image.png', imageIsLink: true};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
wrapper.instance().setState({loaded: true});
const childrenNode = wrapper.props().children(props.src);
const img = container.querySelector('img');
// using a div as a workaround because shallow doesn't support react fragments
const childrenWrapper = shallow(<div>{childrenNode}</div>);
// Mock naturalHeight/naturalWidth for SizeAwareImage's onImageLoaded callback
Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 1041, configurable: true});
expect(childrenWrapper.find(SizeAwareImage)).toHaveLength(1);
expect(childrenWrapper.find(SizeAwareImage).prop('className')).
toEqual(`${props.className} markdown-inline-img--hover markdown-inline-img--no-border`);
expect(childrenWrapper.find(FilePreviewModal)).toHaveLength(0);
expect(childrenWrapper).toMatchSnapshot();
// Simulate load
fireEvent.load(img!);
// When imageIsLink, should have no-border class and not cursor--pointer
expect(img).toHaveClass('markdown-inline-img--hover');
expect(img).toHaveClass('markdown-inline-img--no-border');
expect(img).not.toHaveClass('cursor--pointer');
expect(container).toMatchSnapshot();
});
test('should call openModal when showModal is called', () => {
test('should call openModal when showModal is called', async () => {
const props = {...baseProps, src: 'https://example.com/image.png'};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
const mockEvent = {
preventDefault: () => {},
};
(wrapper.instance() as MarkdownImage).showModal(mockEvent as unknown as React.MouseEvent<HTMLImageElement>, 'https://example.com/image.png');
const img = container.querySelector('img');
// Mock naturalHeight/naturalWidth for SizeAwareImage's onImageLoaded callback
Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 1041, configurable: true});
// Simulate load
fireEvent.load(img!);
// Click the image to trigger showModal
await userEvent.click(img!);
expect(props.actions.openModal).toHaveBeenCalledTimes(1);
});
test('should properly scale down the image in case of a header change system message', () => {
const props = {...baseProps, src: 'https://example.com/image.png', postType: 'system_header_change'};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
wrapper.instance().setState({loaded: true});
const childrenNode = wrapper.props().children(props.src);
const img = container.querySelector('img');
// using a div as a workaround because shallow doesn't support react fragments
const childrenWrapper = shallow(<div>{childrenNode}</div>);
// Mock naturalHeight/naturalWidth for SizeAwareImage's onImageLoaded callback
Object.defineProperty(img, 'naturalHeight', {value: 90, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 1041, configurable: true});
expect(childrenWrapper.find(SizeAwareImage)).toHaveLength(1);
expect(childrenWrapper.find(SizeAwareImage).prop('className')).
toEqual(`${props.className} markdown-inline-img--hover cursor--pointer a11y--active markdown-inline-img--scaled-down`);
// Simulate load
fireEvent.load(img!);
// After load, should have scaled-down class
expect(img).toHaveClass('markdown-inline-img--scaled-down');
expect(img).not.toHaveClass('markdown-inline-img--scaled-down-loading');
});
test('should render image with title, height, width', () => {
const props = {
...baseProps,
alt: 'test image',
title: 'test title',
className: 'markdown-inline-img',
@@ -259,32 +288,29 @@ describe('components/MarkdownImage', () => {
imageIsLink: false,
height: '76',
width: '50',
actions: baseProps.actions,
};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
wrapper.instance().setState({loaded: true});
const childrenNode = wrapper.props().children(props.src);
const img = container.querySelector('img');
// using a div as a workaround because shallow doesn't support react fragments
const childrenWrapper = shallow(<div>{childrenNode}</div>);
// Mock naturalHeight/naturalWidth for SizeAwareImage's onImageLoaded callback
Object.defineProperty(img, 'naturalHeight', {value: 76, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 50, configurable: true});
expect(childrenWrapper.find(SizeAwareImage)).toHaveLength(1);
expect(childrenWrapper.find(SizeAwareImage).prop('className')).
toEqual(`${props.className} markdown-inline-img--hover cursor--pointer a11y--active`);
// Simulate load to get final state
fireEvent.load(img!);
// These props can be strings since they're passed directly through to HTML tags
expect(childrenWrapper.find(SizeAwareImage).prop('width')).toEqual('50');
expect(childrenWrapper.find(SizeAwareImage).prop('height')).toEqual('76');
expect(childrenWrapper.find(SizeAwareImage).prop('title')).toEqual('test title');
expect(img).toHaveAttribute('width', '50');
expect(img).toHaveAttribute('height', '76');
expect(img).toHaveAttribute('title', 'test title');
});
test(`should render image with MarkdownImageExpand if it is taller than ${Constants.EXPANDABLE_INLINE_IMAGE_MIN_HEIGHT}px`, () => {
const props = {
...baseProps,
alt: 'test image',
title: 'test title',
className: 'markdown-inline-img',
@@ -293,23 +319,27 @@ describe('components/MarkdownImage', () => {
imageIsLink: false,
height: '250',
width: '50',
actions: baseProps.actions,
};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
wrapper.instance().setState({loaded: true});
const childrenNode = wrapper.props().children(props.src);
// using a div as a workaround because shallow doesn't support react fragments
const childrenWrapper = shallow(<div>{childrenNode}</div>);
const img = container.querySelector('img');
expect(childrenWrapper).toMatchSnapshot();
// Mock naturalHeight/naturalWidth for SizeAwareImage's onImageLoaded callback
Object.defineProperty(img, 'naturalHeight', {value: 250, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 50, configurable: true});
// Simulate load
fireEvent.load(img!);
expect(container).toMatchSnapshot();
});
test('should provide image src as an alt text for MarkdownImageExpand if image has no own alt text', () => {
const props = {
...baseProps,
alt: '',
title: 'test title',
className: 'markdown-inline-img',
@@ -318,26 +348,29 @@ describe('components/MarkdownImage', () => {
imageIsLink: false,
height: '250',
width: '50',
actions: baseProps.actions,
};
const wrapper = shallow(
const {container} = renderWithContext(
<MarkdownImage {...props}/>,
);
wrapper.instance().setState({loaded: true});
const childrenNode = wrapper.props().children(props.src);
// using a div as a workaround because shallow doesn't support react fragments
const childrenWrapper = shallow(<div>{childrenNode}</div>);
const img = container.querySelector('img');
expect(childrenWrapper).toMatchSnapshot();
// Mock naturalHeight/naturalWidth for SizeAwareImage's onImageLoaded callback
Object.defineProperty(img, 'naturalHeight', {value: 250, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 50, configurable: true});
// Simulate load
fireEvent.load(img!);
expect(container).toMatchSnapshot();
});
test('should render a alt text if the link is unsafe', () => {
const props = {...baseProps, isUnsafeLinksPost: true};
const wrapper = shallow(
renderWithContext(
<MarkdownImage {...props}/>,
);
expect(wrapper.text()).toBe(props.alt);
expect(screen.getByText(props.alt)).toBeInTheDocument();
});
});
@@ -1,10 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import MessageSubmitError from 'components/message_submit_error';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import MessageSubmitError from './message_submit_error';
describe('components/MessageSubmitError', () => {
const baseProps = {
@@ -24,12 +25,13 @@ describe('components/MessageSubmitError', () => {
submittedMessage,
};
const wrapper = shallow(
renderWithContext(
<MessageSubmitError {...props}/>,
);
expect(wrapper.find('[id="message_submit_error.invalidCommand"]').exists()).toBe(true);
expect(wrapper.text()).not.toEqual('No command found');
expect(screen.getByText(/Command with a trigger of/)).toBeInTheDocument();
expect(screen.getByText('Click here to send as a message.')).toBeInTheDocument();
expect(screen.queryByText('No command found')).not.toBeInTheDocument();
});
it('should not display the submit link if the error is not for an invalid slash command', () => {
@@ -45,11 +47,11 @@ describe('components/MessageSubmitError', () => {
submittedMessage,
};
const wrapper = shallow(
renderWithContext(
<MessageSubmitError {...props}/>,
);
expect(wrapper.find('[id="message_submit_error.invalidCommand"]').exists()).toBe(false);
expect(wrapper.text()).toEqual('Some server error');
expect(screen.queryByText('Click here to send as a message.')).not.toBeInTheDocument();
expect(screen.getByText('Some server error')).toBeInTheDocument();
});
});
@@ -1,40 +1,29 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/MsgTyping should match snapshot, on multiple users typing 1`] = `
<span
className="msg-typing"
>
<MemoizedFormattedMessage
defaultMessage="{users} and {last} are typing..."
id="msg_typing.areTyping"
values={
Object {
"last": "another.user",
"users": "test.user, other.test.user",
}
}
/>
</span>
<div>
<span
class="msg-typing"
>
test.user, other.test.user and another.user are typing...
</span>
</div>
`;
exports[`components/MsgTyping should match snapshot, on nobody typing 1`] = `
<span
className="msg-typing"
/>
<div>
<span
class="msg-typing"
/>
</div>
`;
exports[`components/MsgTyping should match snapshot, on one user typing 1`] = `
<span
className="msg-typing"
>
<MemoizedFormattedMessage
defaultMessage="{user} is typing..."
id="msg_typing.isTyping"
values={
Object {
"user": "test.user",
}
}
/>
</span>
<div>
<span
class="msg-typing"
>
test.user is typing...
</span>
</div>
`;
@@ -1,10 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import MsgTyping from 'components/msg_typing/msg_typing';
import {renderWithContext} from 'tests/react_testing_utils';
import MsgTyping from './msg_typing';
describe('components/MsgTyping', () => {
const baseProps = {
@@ -16,23 +17,23 @@ describe('components/MsgTyping', () => {
};
test('should match snapshot, on nobody typing', () => {
const wrapper = shallow(<MsgTyping {...baseProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<MsgTyping {...baseProps}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot, on one user typing', () => {
const typingUsers = ['test.user'];
const props = {...baseProps, typingUsers};
const wrapper = shallow(<MsgTyping {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<MsgTyping {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot, on multiple users typing', () => {
const typingUsers = ['test.user', 'other.test.user', 'another.user'];
const props = {...baseProps, typingUsers};
const wrapper = shallow(<MsgTyping {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<MsgTyping {...props}/>);
expect(container).toMatchSnapshot();
});
});
@@ -1,25 +1,22 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import type {PDFDocumentProxy, PDFPageProxy} from 'pdfjs-dist';
import React from 'react';
import PDFPreview from 'components/pdf_preview';
import type {Props} from 'components/pdf_preview';
import {render, renderWithContext, waitFor} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
jest.mock('pdfjs-dist', () => ({
getDocument: () => Promise.resolve({
numPages: 3,
getPage: (i: number) => Promise.resolve({
pageIndex: i,
getContext: (s: string) => Promise.resolve({s}),
}),
}),
const mockGetDocument = jest.fn();
jest.mock('pdfjs-dist/legacy/build/pdf.mjs', () => ({
getDocument: (params: unknown) => mockGetDocument(params),
}));
jest.mock('pdfjs-dist/build/pdf.worker.min.mjs', () => ({}));
describe('component/PDFPreview', () => {
const requiredProps: Props = {
fileInfo: TestHelper.getFileInfoMock({extension: 'pdf'}),
@@ -28,51 +25,127 @@ describe('component/PDFPreview', () => {
handleBgClose: jest.fn(),
};
beforeEach(() => {
mockGetDocument.mockReset();
mockGetDocument.mockReturnValue({
promise: Promise.resolve({
numPages: 3,
getPage: (i: number) => Promise.resolve({
pageIndex: i,
getViewport: () => ({height: 100, width: 100}),
render: () => ({promise: Promise.resolve()}),
}),
}),
});
});
test('should match snapshot, loading', () => {
const wrapper = shallow(
const {container} = render(
<PDFPreview {...requiredProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, not successful', () => {
const wrapper = shallow(
test('should match snapshot, not successful', async () => {
// Mock PDF loading to fail
mockGetDocument.mockReturnValue({
promise: Promise.reject(new Error('Failed to load PDF')),
});
// Use renderWithContext because FileInfoPreview requires Redux store
const {container} = renderWithContext(
<PDFPreview {...requiredProps}/>,
);
wrapper.setState({loading: false});
expect(wrapper).toMatchSnapshot();
// Wait for loading to complete (and fail)
await waitFor(() => {
expect(container.querySelector('.view-image__loading')).not.toBeInTheDocument();
});
expect(container).toMatchSnapshot();
});
test('should update state with new value from props when prop changes', () => {
const wrapper = shallow<PDFPreview>(
test('should update state with new value from props when prop changes', async () => {
const {container, rerender} = render(
<PDFPreview {...requiredProps}/>,
);
// Wait for initial PDF to load
await waitFor(() => {
expect(container.querySelector('.post-code')).toBeInTheDocument();
});
const newFileUrl = 'https://some-new-url';
wrapper.setProps({fileUrl: newFileUrl});
const {prevFileUrl} = wrapper.instance().state;
expect(prevFileUrl).toEqual(newFileUrl);
// Rerender with new fileUrl - component should show loading state again
rerender(
<PDFPreview
{...requiredProps}
fileUrl={newFileUrl}
/>,
);
// Verify loading state is shown (state was reset due to new fileUrl)
expect(container.querySelector('.view-image__loading')).toBeInTheDocument();
// Wait for new PDF to load
await waitFor(() => {
expect(container.querySelector('.post-code')).toBeInTheDocument();
});
// Verify getDocument was called with new URL
expect(mockGetDocument).toHaveBeenLastCalledWith(
expect.objectContaining({url: newFileUrl}),
);
});
test('should return correct state when onDocumentLoad is called', () => {
const wrapper = shallow<PDFPreview>(
test('should return correct state when onDocumentLoad is called', async () => {
// Test with 0 pages
mockGetDocument.mockReturnValueOnce({
promise: Promise.resolve({
numPages: 0,
getPage: jest.fn(),
}),
});
const {container, rerender} = render(
<PDFPreview {...requiredProps}/>,
);
let pdf = {numPages: 0} as PDFDocumentProxy;
wrapper.instance().onDocumentLoad(pdf);
expect(wrapper.state('pdf')).toEqual(pdf);
expect(wrapper.state('numPages')).toEqual(pdf.numPages);
// Wait for PDF to load with 0 pages
await waitFor(() => {
expect(container.querySelector('.view-image__loading')).not.toBeInTheDocument();
});
pdf = {
numPages: 100,
getPage: async (i) => {
const page = {pageNumber: i} as PDFPageProxy;
return Promise.resolve(page);
},
} as PDFDocumentProxy;
wrapper.instance().onDocumentLoad(pdf);
expect(wrapper.state('pdf')).toEqual(pdf);
expect(wrapper.state('numPages')).toEqual(pdf.numPages);
// Should have 0 canvas elements
expect(container.querySelectorAll('canvas')).toHaveLength(0);
// Test with 100 pages
mockGetDocument.mockReturnValueOnce({
promise: Promise.resolve({
numPages: 100,
getPage: (i: number) => Promise.resolve({
pageIndex: i,
getViewport: () => ({height: 100, width: 100}),
render: () => ({promise: Promise.resolve()}),
}),
}),
});
const newFileUrl = 'https://another-url';
rerender(
<PDFPreview
{...requiredProps}
fileUrl={newFileUrl}
/>,
);
// Wait for new PDF to load
await waitFor(() => {
expect(container.querySelector('.post-code')).toBeInTheDocument();
});
// Should have 100 canvas elements
expect(container.querySelectorAll('canvas')).toHaveLength(100);
});
});
@@ -1,21 +1,22 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import PostDeletedModal from 'components/post_deleted_modal';
import {renderWithContext} from 'tests/react_testing_utils';
describe('components/ChannelInfoModal', () => {
import PostDeletedModal from './post_deleted_modal';
describe('components/PostDeletedModal', () => {
const baseProps = {
onExited: jest.fn(),
};
test('should match snapshot', () => {
const wrapper = shallow(
const {baseElement} = renderWithContext(
<PostDeletedModal {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(baseElement).toMatchSnapshot();
});
});
@@ -1,100 +1,184 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/ProfilePicture should match snapshot, no user specified, default props 1`] = `
<span
className="status-wrapper style--none"
>
<div>
<span
className="profile-icon "
class="status-wrapper style--none"
>
<Memo(Avatar)
size="md"
url="http://example.com/image.png"
/>
<span
class="profile-icon "
>
<img
alt="user profile image"
class="Avatar Avatar-md"
loading="lazy"
src="http://example.com/image.png"
/>
</span>
<span
class="status "
>
<svg
aria-label="Away"
height="100%"
role="img"
style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
viewBox="0 0 20 20"
width="100%"
>
<path
class="away--icon"
d="M10,0C15.519,0 20,4.481 20,10C20,15.519 15.519,20 10,20C4.481,20 0,15.519 0,10C0,4.481 4.481,0 10,0ZM10.27,3C10.949,3 11.5,3.586 11.5,4.307L11.5,9.379L15.002,12.881C15.492,13.37 15.499,14.158 15.019,14.638L14.638,15.019C14.158,15.499 13.37,15.492 12.881,15.002L8.887,11.008C8.739,10.861 8.636,10.686 8.576,10.501C8.528,10.402 8.5,10.299 8.5,10.193L8.5,4.307C8.5,3.586 9.051,3 9.73,3L10.27,3Z"
/>
</svg>
</span>
</span>
<Memo(StatusIcon)
status="away"
/>
</span>
</div>
`;
exports[`components/ProfilePicture should match snapshot, no user specified, overridden props 1`] = `
<span
className="status-wrapper style--none"
>
<div>
<span
className="profile-icon "
class="status-wrapper style--none"
>
<Memo(Avatar)
size="xl"
url="http://example.com/image.png"
/>
<span
class="profile-icon "
>
<img
alt="user profile image"
class="Avatar Avatar-xl"
loading="lazy"
src="http://example.com/image.png"
/>
</span>
<span
class="status "
>
<svg
aria-label="Away"
height="100%"
role="img"
style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
viewBox="0 0 20 20"
width="100%"
>
<path
class="away--icon"
d="M10,0C15.519,0 20,4.481 20,10C20,15.519 15.519,20 10,20C4.481,20 0,15.519 0,10C0,4.481 4.481,0 10,0ZM10.27,3C10.949,3 11.5,3.586 11.5,4.307L11.5,9.379L15.002,12.881C15.492,13.37 15.499,14.158 15.019,14.638L14.638,15.019C14.158,15.499 13.37,15.492 12.881,15.002L8.887,11.008C8.739,10.861 8.636,10.686 8.576,10.501C8.528,10.402 8.5,10.299 8.5,10.193L8.5,4.307C8.5,3.586 9.051,3 9.73,3L10.27,3Z"
/>
</svg>
</span>
</span>
<Memo(StatusIcon)
status="away"
/>
</span>
</div>
`;
exports[`components/ProfilePicture should match snapshot, profile and src, default props 1`] = `
<ProfilePopoverController
src="http://example.com/image.png"
triggerComponentAs="button"
triggerComponentClass="status-wrapper style--none"
triggerComponentStyle={
Object {
"borderRadius": "50%",
"height": "32px",
"width": "32px",
}
}
userId="uid"
>
<span
className="profile-icon "
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="status-wrapper style--none"
style="border-radius: 50%; width: 32px; height: 32px;"
>
<Memo(Avatar)
url="http://example.com/emoji.png"
/>
</span>
<Memo(StatusIcon)
status="away"
/>
</ProfilePopoverController>
<span
class="profile-icon "
>
<img
alt="user profile image"
class="Avatar Avatar-md"
loading="lazy"
src="http://example.com/emoji.png"
/>
</span>
<span
class="status "
>
<svg
aria-label="Away"
height="100%"
role="img"
style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
viewBox="0 0 20 20"
width="100%"
>
<path
class="away--icon"
d="M10,0C15.519,0 20,4.481 20,10C20,15.519 15.519,20 10,20C4.481,20 0,15.519 0,10C0,4.481 4.481,0 10,0ZM10.27,3C10.949,3 11.5,3.586 11.5,4.307L11.5,9.379L15.002,12.881C15.492,13.37 15.499,14.158 15.019,14.638L14.638,15.019C14.158,15.499 13.37,15.492 12.881,15.002L8.887,11.008C8.739,10.861 8.636,10.686 8.576,10.501C8.528,10.402 8.5,10.299 8.5,10.193L8.5,4.307C8.5,3.586 9.051,3 9.73,3L10.27,3Z"
/>
</svg>
</span>
</button>
</div>
`;
exports[`components/ProfilePicture should match snapshot, user specified 1`] = `
<span
className="status-wrapper style--none"
>
<div>
<span
className="profile-icon "
class="status-wrapper style--none"
>
<Memo(Avatar)
size="md"
url="http://example.com/image.png"
/>
<span
class="profile-icon "
>
<img
alt="user profile image"
class="Avatar Avatar-md"
loading="lazy"
src="http://example.com/image.png"
/>
</span>
<span
class="status "
>
<svg
aria-label="Away"
height="100%"
role="img"
style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
viewBox="0 0 20 20"
width="100%"
>
<path
class="away--icon"
d="M10,0C15.519,0 20,4.481 20,10C20,15.519 15.519,20 10,20C4.481,20 0,15.519 0,10C0,4.481 4.481,0 10,0ZM10.27,3C10.949,3 11.5,3.586 11.5,4.307L11.5,9.379L15.002,12.881C15.492,13.37 15.499,14.158 15.019,14.638L14.638,15.019C14.158,15.499 13.37,15.492 12.881,15.002L8.887,11.008C8.739,10.861 8.636,10.686 8.576,10.501C8.528,10.402 8.5,10.299 8.5,10.193L8.5,4.307C8.5,3.586 9.051,3 9.73,3L10.27,3Z"
/>
</svg>
</span>
</span>
<Memo(StatusIcon)
status="away"
/>
</span>
</div>
`;
exports[`components/ProfilePicture should match snapshot, user specified, overridden props 1`] = `
<span
className="status-wrapper style--none"
>
<div>
<span
className="profile-icon "
class="status-wrapper style--none"
>
<Memo(Avatar)
size="xs"
url="http://example.com/image.png"
/>
<span
class="profile-icon "
>
<img
alt="user profile image"
class="Avatar Avatar-xs"
loading="lazy"
src="http://example.com/image.png"
/>
</span>
<span
class="status "
>
<svg
aria-label="Away"
height="100%"
role="img"
style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
viewBox="0 0 20 20"
width="100%"
>
<path
class="away--icon"
d="M10,0C15.519,0 20,4.481 20,10C20,15.519 15.519,20 10,20C4.481,20 0,15.519 0,10C0,4.481 4.481,0 10,0ZM10.27,3C10.949,3 11.5,3.586 11.5,4.307L11.5,9.379L15.002,12.881C15.492,13.37 15.499,14.158 15.019,14.638L14.638,15.019C14.158,15.499 13.37,15.492 12.881,15.002L8.887,11.008C8.739,10.861 8.636,10.686 8.576,10.501C8.528,10.402 8.5,10.299 8.5,10.193L8.5,4.307C8.5,3.586 9.051,3 9.73,3L10.27,3Z"
/>
</svg>
</span>
</span>
<Memo(StatusIcon)
status="away"
/>
</span>
</div>
`;
@@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import type {ComponentProps} from 'react';
import ProfilePicture from 'components/profile_picture';
import {render} from 'tests/react_testing_utils';
import ProfilePicture from './index';
type Props = ComponentProps<typeof ProfilePicture>;
@@ -17,11 +18,11 @@ describe('components/ProfilePicture', () => {
test('should match snapshot, no user specified, default props', () => {
const props: Props = baseProps;
const wrapper = shallow(
const {container} = render(
<ProfilePicture {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, profile and src, default props', () => {
@@ -31,11 +32,11 @@ describe('components/ProfilePicture', () => {
userId: 'uid',
src: 'http://example.com/emoji.png',
};
const wrapper = shallow(
const {container} = render(
<ProfilePicture {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, no user specified, overridden props', () => {
@@ -43,11 +44,11 @@ describe('components/ProfilePicture', () => {
...baseProps,
size: 'xl',
};
const wrapper = shallow(
const {container} = render(
<ProfilePicture {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, user specified', () => {
@@ -55,11 +56,11 @@ describe('components/ProfilePicture', () => {
...baseProps,
username: 'username',
};
const wrapper = shallow(
const {container} = render(
<ProfilePicture {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, user specified, overridden props', () => {
@@ -68,10 +69,10 @@ describe('components/ProfilePicture', () => {
username: 'username',
size: 'xs',
};
const wrapper = shallow(
const {container} = render(
<ProfilePicture {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,66 +1,53 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import ScrollToBottomToast from './scroll_to_bottom_toast';
describe('ScrollToBottomToast Component', () => {
const mockOnDismiss = jest.fn();
const mockOnClick = jest.fn();
const mockOnClickEvent = {
preventDefault: jest.fn(),
stopPropagation: jest.fn(),
};
it('should render ScrollToBottomToast component', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<ScrollToBottomToast
onDismiss={mockOnDismiss}
onClick={mockOnClick}
/>,
);
// Assertions
expect(wrapper.find('.scroll-to-bottom-toast')).toHaveLength(1);
expect(wrapper.find('UnreadBelowIcon')).toHaveLength(1);
expect(wrapper.text()).toContain('Jump to recents');
expect(wrapper.find('.scroll-to-bottom-toast__dismiss')).toHaveLength(1);
expect(wrapper.find('.close-btn')).toHaveLength(1);
expect(container.querySelector('.scroll-to-bottom-toast')).toBeInTheDocument();
expect(screen.getByText('Jump to recents')).toBeInTheDocument();
expect(container.querySelector('.scroll-to-bottom-toast__dismiss')).toBeInTheDocument();
});
it('should call onClick when clicked', () => {
const wrapper = shallow(
it('should call onClick when clicked', async () => {
const {container} = renderWithContext(
<ScrollToBottomToast
onDismiss={mockOnDismiss}
onClick={mockOnClick}
/>,
);
// Simulate click
wrapper.simulate('click', mockOnClickEvent);
await userEvent.click(container.querySelector('.scroll-to-bottom-toast')!);
// Expect the onClick function to be called
expect(mockOnClick).toHaveBeenCalled();
});
it('should call onDismiss when close button is clicked', () => {
const wrapper = shallow(
it('should call onDismiss when close button is clicked', async () => {
const {container} = renderWithContext(
<ScrollToBottomToast
onDismiss={mockOnDismiss}
onClick={mockOnClick}
/>,
);
// Simulate click on the close button
wrapper.find('.scroll-to-bottom-toast__dismiss').simulate('click', mockOnClickEvent);
await userEvent.click(container.querySelector('.scroll-to-bottom-toast__dismiss')!);
// Expect the onDismiss function to be called
expect(mockOnDismiss).toHaveBeenCalled();
// Expect to stop propagation to avoid scrolling down on dismissing
expect(mockOnClickEvent.stopPropagation).toHaveBeenCalled();
});
});
@@ -1,74 +1,109 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/SingleImageView permalink preview should render with permalink styling if in permalink 1`] = `
<div
className="file-view--single image-permalink"
>
<div>
<div
className="file__image"
class="file-view--single image-permalink"
>
<div
className="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
className="style--none single-image-view__toggle"
data-expanded={true}
key="toggle"
onClick={[Function]}
>
<span
className="icon icon-menu-down"
/>
</button>
</div>
<div
className="image-container image-permalink"
style={Object {}}
class="file__image"
>
<div
className="image-loaded"
style={Object {}}
class="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
class="style--none single-image-view__toggle"
data-expanded="true"
>
<span
class="icon icon-menu-down"
/>
</button>
</div>
<div
class="image-container image-permalink"
>
<div
className="image-permalink"
class="image-loaded"
>
<injectIntl(SizeAwareImage)
className="image-permalink"
dimensions={
Object {
"height": 200,
"width": 350,
}
}
enablePublicLink={false}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "jpg",
"has_preview_image": true,
"height": 200,
"id": "file_info_id",
"mime_type": "mime_type",
"name": "name",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
fileURL="/api/v4/files/file_info_id"
getFilePublicLink={[Function]}
handleSmallImageContainer={true}
onClick={[Function]}
onImageLoaded={[Function]}
showLoader={true}
src="/api/v4/files/file_info_id/preview"
/>
<div
class="image-permalink"
>
<div
class="image-loading__container image-permalink"
style="max-width: 350px;"
>
<div
style="position: absolute; top: 50%; transform: translate(-50%, -50%); left: 50%;"
>
<div
class="file__image-loading"
>
<span
class="LoadingSpinner"
data-testid="loadingSpinner"
id="loadingSpinner"
>
<span
class="fa fa-spinner fa-fw fa-pulse spinner"
title="Loading Icon"
/>
</span>
<span
class="loader-percent"
/>
</div>
</div>
<svg
style="max-height: 200px; max-width: 350px; vertical-align: middle;"
viewBox="0 0 350 200"
xmlns="http://www.w3.org/2000/svg"
/>
</div>
<div
class="file-preview__button"
style="display: none;"
>
<figure
class="image-loaded-container"
>
<img
aria-label="file thumbnail name"
class="image-permalink"
src="/api/v4/files/file_info_id/preview"
tabindex="0"
/>
<span
class="image-preview-utility-buttons-container image-preview-utility-buttons-container--small-image image-preview-utility-buttons-container--small-image-no-copy-button"
>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="/api/v4/files/file_info_id"
rel="noopener noreferrer"
role="button"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
</div>
</div>
</div>
@@ -77,74 +112,109 @@ exports[`components/SingleImageView permalink preview should render with permali
`;
exports[`components/SingleImageView should match snapshot 1`] = `
<div
className="file-view--single"
>
<div>
<div
className="file__image"
class="file-view--single"
>
<div
className="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
className="style--none single-image-view__toggle"
data-expanded={true}
key="toggle"
onClick={[Function]}
>
<span
className="icon icon-menu-down"
/>
</button>
</div>
<div
className="image-container"
style={Object {}}
class="file__image"
>
<div
className="image-loaded"
style={Object {}}
class="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
class="style--none single-image-view__toggle"
data-expanded="true"
>
<span
class="icon icon-menu-down"
/>
</button>
</div>
<div
class="image-container"
>
<div
className=""
class="image-loaded"
>
<injectIntl(SizeAwareImage)
className=""
dimensions={
Object {
"height": 200,
"width": 350,
}
}
enablePublicLink={false}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "jpg",
"has_preview_image": true,
"height": 200,
"id": "file_info_id",
"mime_type": "mime_type",
"name": "name",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
fileURL="/api/v4/files/file_info_id"
getFilePublicLink={[Function]}
handleSmallImageContainer={true}
onClick={[Function]}
onImageLoaded={[Function]}
showLoader={true}
src="/api/v4/files/file_info_id/preview"
/>
<div
class=""
>
<div
class="image-loading__container "
style="max-width: 350px;"
>
<div
style="position: absolute; top: 50%; transform: translate(-50%, -50%); left: 50%;"
>
<div
class="file__image-loading"
>
<span
class="LoadingSpinner"
data-testid="loadingSpinner"
id="loadingSpinner"
>
<span
class="fa fa-spinner fa-fw fa-pulse spinner"
title="Loading Icon"
/>
</span>
<span
class="loader-percent"
/>
</div>
</div>
<svg
style="max-height: 200px; max-width: 350px; vertical-align: middle;"
viewBox="0 0 350 200"
xmlns="http://www.w3.org/2000/svg"
/>
</div>
<div
class="file-preview__button"
style="display: none;"
>
<figure
class="image-loaded-container"
>
<img
aria-label="file thumbnail name"
class=""
src="/api/v4/files/file_info_id/preview"
tabindex="0"
/>
<span
class="image-preview-utility-buttons-container image-preview-utility-buttons-container--small-image image-preview-utility-buttons-container--small-image-no-copy-button"
>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="/api/v4/files/file_info_id"
rel="noopener noreferrer"
role="button"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
</div>
</div>
</div>
@@ -153,74 +223,78 @@ exports[`components/SingleImageView should match snapshot 1`] = `
`;
exports[`components/SingleImageView should match snapshot 2`] = `
<div
className="file-view--single"
>
<div>
<div
className="file__image"
class="file-view--single"
>
<div
className="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
className="style--none single-image-view__toggle"
data-expanded={true}
key="toggle"
onClick={[Function]}
>
<span
className="icon icon-menu-down"
/>
</button>
</div>
<div
className="image-container"
style={Object {}}
class="file__image"
>
<div
className="image-loaded image-fade-in"
style={Object {}}
class="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
class="style--none single-image-view__toggle"
data-expanded="true"
>
<span
class="icon icon-menu-down"
/>
</button>
</div>
<div
class="image-container"
>
<div
className=""
class="image-loaded image-fade-in"
>
<injectIntl(SizeAwareImage)
className=""
dimensions={
Object {
"height": 200,
"width": 350,
}
}
enablePublicLink={false}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "jpg",
"has_preview_image": true,
"height": 200,
"id": "file_info_id",
"mime_type": "mime_type",
"name": "name",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
fileURL="/api/v4/files/file_info_id"
getFilePublicLink={[Function]}
handleSmallImageContainer={true}
onClick={[Function]}
onImageLoaded={[Function]}
showLoader={true}
src="/api/v4/files/file_info_id/preview"
/>
<div
class=""
>
<div
class="file-preview__button"
style="display: inline-block;"
>
<figure
class="image-loaded-container"
>
<img
aria-label="file thumbnail name"
class=""
src="/api/v4/files/file_info_id/preview"
tabindex="0"
/>
<span
class="image-preview-utility-buttons-container"
>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="/api/v4/files/file_info_id"
rel="noopener noreferrer"
role="button"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
</div>
</div>
</div>
@@ -229,78 +303,111 @@ exports[`components/SingleImageView should match snapshot 2`] = `
`;
exports[`components/SingleImageView should match snapshot, SVG image 1`] = `
<div
className="file-view--single"
>
<div>
<div
className="file__image"
class="file-view--single"
>
<div
className="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
className="style--none single-image-view__toggle"
data-expanded={true}
key="toggle"
onClick={[Function]}
>
<span
className="icon icon-menu-down"
/>
</button>
</div>
<div
className="image-container"
style={Object {}}
class="file__image"
>
<div
className="image-loaded svg"
style={
Object {
"width": "100%",
}
}
class="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
class="style--none single-image-view__toggle"
data-expanded="true"
>
<span
class="icon icon-menu-down"
/>
</button>
</div>
<div
class="image-container"
>
<div
className=""
class="image-loaded svg"
style="width: 100%;"
>
<injectIntl(SizeAwareImage)
className=""
dimensions={
Object {
"height": 200,
"width": 350,
}
}
enablePublicLink={false}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "svg",
"has_preview_image": true,
"height": 200,
"id": "svg_file_info_id",
"mime_type": "mime_type",
"name": "name_svg",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
fileURL="/api/v4/files/svg_file_info_id"
getFilePublicLink={[Function]}
handleSmallImageContainer={true}
onClick={[Function]}
onImageLoaded={[Function]}
showLoader={true}
src="/api/v4/files/svg_file_info_id/preview"
/>
<div
class=""
>
<div
class="image-loading__container "
style="max-width: 350px;"
>
<div
style="position: absolute; top: 50%; transform: translate(-50%, -50%); left: 50%;"
>
<div
class="file__image-loading"
>
<span
class="LoadingSpinner"
data-testid="loadingSpinner"
id="loadingSpinner"
>
<span
class="fa fa-spinner fa-fw fa-pulse spinner"
title="Loading Icon"
/>
</span>
<span
class="loader-percent"
/>
</div>
</div>
<svg
style="max-height: 200px; max-width: 350px; vertical-align: middle;"
viewBox="0 0 350 200"
xmlns="http://www.w3.org/2000/svg"
/>
</div>
<div
class="file-preview__button"
style="display: none;"
>
<figure
class="image-loaded-container"
>
<img
aria-label="file thumbnail name_svg"
class=""
src="/api/v4/files/svg_file_info_id/preview"
style="width: 350px; height: auto;"
tabindex="0"
/>
<span
class="image-preview-utility-buttons-container image-preview-utility-buttons-container--small-image image-preview-utility-buttons-container--small-image-no-copy-button"
>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="/api/v4/files/svg_file_info_id"
rel="noopener noreferrer"
role="button"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
</div>
</div>
</div>
@@ -309,78 +416,80 @@ exports[`components/SingleImageView should match snapshot, SVG image 1`] = `
`;
exports[`components/SingleImageView should match snapshot, SVG image 2`] = `
<div
className="file-view--single"
>
<div>
<div
className="file__image"
class="file-view--single"
>
<div
className="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
className="style--none single-image-view__toggle"
data-expanded={true}
key="toggle"
onClick={[Function]}
>
<span
className="icon icon-menu-down"
/>
</button>
</div>
<div
className="image-container"
style={Object {}}
class="file__image"
>
<div
className="image-loaded image-fade-in svg"
style={
Object {
"width": "100%",
}
}
class="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
class="style--none single-image-view__toggle"
data-expanded="true"
>
<span
class="icon icon-menu-down"
/>
</button>
</div>
<div
class="image-container"
>
<div
className=""
class="image-loaded image-fade-in svg"
style="width: 100%;"
>
<injectIntl(SizeAwareImage)
className=""
dimensions={
Object {
"height": 200,
"width": 350,
}
}
enablePublicLink={false}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "svg",
"has_preview_image": true,
"height": 200,
"id": "svg_file_info_id",
"mime_type": "mime_type",
"name": "name_svg",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
fileURL="/api/v4/files/svg_file_info_id"
getFilePublicLink={[Function]}
handleSmallImageContainer={true}
onClick={[Function]}
onImageLoaded={[Function]}
showLoader={true}
src="/api/v4/files/svg_file_info_id/preview"
/>
<div
class=""
>
<div
class="file-preview__button"
style="display: inline-block;"
>
<figure
class="image-loaded-container"
>
<img
aria-label="file thumbnail name_svg"
class=""
src="/api/v4/files/svg_file_info_id/preview"
style="width: 350px; height: auto;"
tabindex="0"
/>
<span
class="image-preview-utility-buttons-container"
>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="/api/v4/files/svg_file_info_id"
rel="noopener noreferrer"
role="button"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
</div>
</div>
</div>
@@ -389,74 +498,78 @@ exports[`components/SingleImageView should match snapshot, SVG image 2`] = `
`;
exports[`components/SingleImageView should set loaded state on callback of onImageLoaded on SizeAwareImage component 1`] = `
<div
className="file-view--single"
>
<div>
<div
className="file__image"
class="file-view--single"
>
<div
className="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
className="style--none single-image-view__toggle"
data-expanded={true}
key="toggle"
onClick={[Function]}
>
<span
className="icon icon-menu-down"
/>
</button>
</div>
<div
className="image-container"
style={Object {}}
class="file__image"
>
<div
className="image-loaded image-fade-in"
style={Object {}}
class="image-header image-header--expanded"
>
<button
aria-label="Toggle Embed Visibility"
class="style--none single-image-view__toggle"
data-expanded="true"
>
<span
class="icon icon-menu-down"
/>
</button>
</div>
<div
class="image-container"
>
<div
className=""
class="image-loaded image-fade-in"
>
<injectIntl(SizeAwareImage)
className=""
dimensions={
Object {
"height": 200,
"width": 350,
}
}
enablePublicLink={false}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "jpg",
"has_preview_image": true,
"height": 200,
"id": "file_info_id",
"mime_type": "mime_type",
"name": "name",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
fileURL="/api/v4/files/file_info_id"
getFilePublicLink={[Function]}
handleSmallImageContainer={true}
onClick={[Function]}
onImageLoaded={[Function]}
showLoader={true}
src="/api/v4/files/file_info_id/preview"
/>
<div
class=""
>
<div
class="file-preview__button"
style="display: inline-block;"
>
<figure
class="image-loaded-container"
>
<img
aria-label="file thumbnail name"
class=""
src="/api/v4/files/file_info_id/preview"
tabindex="0"
/>
<span
class="image-preview-utility-buttons-container"
>
<a
aria-label="Download"
class="style--none size-aware-image__download"
download=""
href="/api/v4/files/file_info_id"
rel="noopener noreferrer"
role="button"
target="_blank"
>
<svg
class="style--none"
fill="currentColor"
height="20"
version="1.1"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,18H5v2h14V18z M11,2h2v10.2l4.5-4.5l1.4,1.4L12,16L5.1,9.1l1.4-1.4l4.5,4.5V2z"
/>
</svg>
</a>
</span>
</figure>
</div>
</div>
</div>
</div>
</div>
@@ -1,12 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import SingleImageView from 'components/single_image_view/single_image_view';
import SizeAwareImage from 'components/size_aware_image';
import {fireEvent, renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
describe('components/SingleImageView', () => {
@@ -24,14 +23,19 @@ describe('components/SingleImageView', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<SingleImageView {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
wrapper.setState({loaded: true});
expect(wrapper).toMatchSnapshot();
// Simulate loaded state by triggering image load
const img = container.querySelector('img');
expect(img).toBeInTheDocument();
Object.defineProperty(img, 'naturalHeight', {value: 100, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 100, configurable: true});
fireEvent.load(img!);
expect(container).toMatchSnapshot();
});
test('should match snapshot, SVG image', () => {
@@ -41,27 +45,40 @@ describe('components/SingleImageView', () => {
extension: 'svg',
});
const props = {...baseProps, fileInfo};
const wrapper = shallow(
const {container} = renderWithContext(
<SingleImageView {...props}/>,
);
wrapper.setState({viewPortWidth: 300});
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
wrapper.setState({loaded: true});
expect(wrapper).toMatchSnapshot();
// Simulate loaded state by triggering image load
const img = container.querySelector('img');
expect(img).toBeInTheDocument();
Object.defineProperty(img, 'naturalHeight', {value: 100, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 100, configurable: true});
fireEvent.load(img!);
expect(container).toMatchSnapshot();
});
test('should call openModal on handleImageClick', () => {
const wrapper = shallow(
test('should call openModal on handleImageClick', async () => {
const {container} = renderWithContext(
<SingleImageView {...baseProps}/>,
);
wrapper.find(SizeAwareImage).at(0).simulate('click', {preventDefault: () => { }});
const img = container.querySelector('img');
expect(img).toBeInTheDocument();
// Simulate loaded state
Object.defineProperty(img, 'naturalHeight', {value: 100, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 100, configurable: true});
fireEvent.load(img!);
// Click the image
await userEvent.click(img!);
expect(baseProps.actions.openModal).toHaveBeenCalledTimes(1);
});
test('should call toggleEmbedVisibility with post id', () => {
test('should call toggleEmbedVisibility with post id', async () => {
const props = {
...baseProps,
actions: {
@@ -70,59 +87,67 @@ describe('components/SingleImageView', () => {
},
};
const wrapper = shallow(
renderWithContext(
<SingleImageView {...props}/>,
);
const instance = wrapper.instance() as SingleImageView;
const event = {
stopPropagation: jest.fn(),
} as unknown as React.MouseEvent<HTMLButtonElement>;
instance.toggleEmbedVisibility(event);
await userEvent.click(screen.getByRole('button', {name: 'Toggle Embed Visibility'}));
expect(props.actions.toggleEmbedVisibility).toHaveBeenCalledTimes(1);
expect(props.actions.toggleEmbedVisibility).toHaveBeenCalledWith('original_post_id');
});
test('should set loaded state on callback of onImageLoaded on SizeAwareImage component', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<SingleImageView {...baseProps}/>,
);
expect(wrapper.state('loaded')).toEqual(false);
wrapper.find(SizeAwareImage).prop('onImageLoaded')?.({height: 0, width: 0});
expect(wrapper.state('loaded')).toEqual(true);
expect(wrapper).toMatchSnapshot();
// Initially should not have image-fade-in class (loaded = false)
const imageLoadedDiv = container.querySelector('.image-loaded');
expect(imageLoadedDiv).not.toHaveClass('image-fade-in');
// Simulate image loaded
const img = container.querySelector('img');
expect(img).toBeInTheDocument();
Object.defineProperty(img, 'naturalHeight', {value: 100, configurable: true});
Object.defineProperty(img, 'naturalWidth', {value: 100, configurable: true});
fireEvent.load(img!);
// After load, should have image-fade-in class (loaded = true)
expect(container.querySelector('.image-loaded')).toHaveClass('image-fade-in');
expect(container).toMatchSnapshot();
});
test('should correctly pass prop down to surround small images with a container', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<SingleImageView {...baseProps}/>,
);
expect(wrapper.find(SizeAwareImage).prop('handleSmallImageContainer')).
toEqual(true);
// The SizeAwareImage component should receive handleSmallImageContainer=true
// This is verified by checking that the component renders correctly
// The actual prop passing is internal, but we can verify the component structure
expect(container.querySelector('.file-preview__button')).toBeInTheDocument();
});
test('should not show filename when image is displayed', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<SingleImageView
{...baseProps}
isEmbedVisible={true}
/>,
);
expect(wrapper.find('.image-header').text()).toHaveLength(0);
expect(container.querySelector('.image-header')?.textContent).toHaveLength(0);
});
test('should show filename when image is collapsed', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<SingleImageView
{...baseProps}
isEmbedVisible={false}
/>,
);
expect(wrapper.find('.image-header').text()).
toEqual(baseProps.fileInfo.name);
expect(container.querySelector('.image-header')?.textContent).toEqual(baseProps.fileInfo.name);
});
describe('permalink preview', () => {
@@ -132,10 +157,10 @@ describe('components/SingleImageView', () => {
isInPermalink: true,
};
const wrapper = shallow(<SingleImageView {...props}/>);
const {container} = renderWithContext(<SingleImageView {...props}/>);
expect(wrapper.find('.image-permalink').exists()).toBe(true);
expect(wrapper).toMatchSnapshot();
expect(container.querySelector('.image-permalink')).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
});
});
@@ -1,13 +1,10 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/timestamp/SemanticTime should support custom label 1`] = `
<Memo(SemanticTime)
aria-label="A custom label"
value={2020-06-05T10:20:30.000Z}
>
<div>
<time
aria-label="A custom label"
dateTime="2020-06-05T10:20:30.000"
datetime="2020-06-05T10:20:30.000"
/>
</Memo(SemanticTime)>
</div>
`;
@@ -1,44 +1,42 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {mount} from 'enzyme';
import React from 'react';
import {render, screen} from 'tests/react_testing_utils';
import SemanticTime from './semantic_time';
describe('components/timestamp/SemanticTime', () => {
test('should render time semantically', () => {
const date = new Date('2020-06-05T10:20:30Z');
const wrapper = mount(
render(
<SemanticTime
value={date}
value={new Date('2020-06-05T10:20:30Z')}
/>,
);
expect(wrapper.find('time').prop('dateTime')).toBe('2020-06-05T10:20:30.000');
expect(screen.getByRole('time')).toHaveAttribute('datetime', '2020-06-05T10:20:30.000');
});
test('should support passthrough children', () => {
const date = new Date('2020-06-05T10:20:30Z');
const wrapper = mount(
render(
<SemanticTime
value={date}
value={new Date('2020-06-05T10:20:30Z')}
>
{'10:20'}
</SemanticTime>,
);
expect(wrapper.find('time').text()).toBe('10:20');
expect(screen.getByRole('time')).toHaveTextContent('10:20');
});
test('should support custom label', () => {
const date = new Date('2020-06-05T10:20:30Z');
const wrapper = mount(
const {container} = render(
<SemanticTime
value={date}
value={new Date('2020-06-05T10:20:30Z')}
aria-label='A custom label'
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,74 +1,60 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/UserProfile should match snapshot 1`] = `
<Fragment>
<ProfilePopoverController
hideStatus={false}
overwriteName=""
src="/api/v4/users/undefined/image?_=0"
triggerComponentAs="button"
triggerComponentClass="user-popover style--none"
userId="user_id"
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="user-popover style--none"
>
nickname
</ProfilePopoverController>
</Fragment>
</button>
</div>
`;
exports[`components/UserProfile should match snapshot, when displayUsername is enabled 1`] = `
<Fragment>
<ProfilePopoverController
hideStatus={false}
overwriteName=""
src="/api/v4/users/undefined/image?_=0"
triggerComponentAs="button"
triggerComponentClass="user-popover style--none"
userId="user_id"
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="user-popover style--none"
>
@username
</ProfilePopoverController>
</Fragment>
</button>
</div>
`;
exports[`components/UserProfile should match snapshot, when popover is disabled 1`] = `
<div
className="user-popover"
>
nickname
<div>
<div
class="user-popover"
>
nickname
</div>
</div>
`;
exports[`components/UserProfile should match snapshot, when user is shared 1`] = `
<Fragment>
<ProfilePopoverController
hideStatus={false}
overwriteName=""
src="/api/v4/users/undefined/image?_=0"
triggerComponentAs="button"
triggerComponentClass="user-popover style--none"
userId="user_id"
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="user-popover style--none"
>
nickname
</ProfilePopoverController>
</Fragment>
</button>
</div>
`;
exports[`components/UserProfile should match snapshot, with colorization 1`] = `
<Fragment>
<ProfilePopoverController
hideStatus={false}
overwriteName=""
src="/api/v4/users/undefined/image?_=0"
triggerComponentAs="button"
triggerComponentClass="user-popover style--none"
triggerComponentStyle={
Object {
"color": "#bbd279",
}
}
userId="user_id"
<div>
<button
aria-expanded="false"
aria-haspopup="dialog"
class="user-popover style--none"
style="color: rgb(187, 210, 121);"
>
nickname
</ProfilePopoverController>
</Fragment>
</button>
</div>
`;
@@ -1,13 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import type {UserProfile as UserProfileType} from '@mattermost/types/users';
import {Preferences} from 'mattermost-redux/constants';
import {render} from 'tests/react_testing_utils';
import UserProfile from './user_profile';
describe('components/UserProfile', () => {
@@ -27,8 +28,8 @@ describe('components/UserProfile', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(<UserProfile {...baseProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = render(<UserProfile {...baseProps}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot, with colorization', () => {
@@ -37,8 +38,8 @@ describe('components/UserProfile', () => {
colorize: true,
};
const wrapper = shallow(<UserProfile {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = render(<UserProfile {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot, when user is shared', () => {
@@ -47,27 +48,27 @@ describe('components/UserProfile', () => {
isShared: true,
};
const wrapper = shallow(<UserProfile {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = render(<UserProfile {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot, when popover is disabled', () => {
const wrapper = shallow(
const {container} = render(
<UserProfile
{...baseProps}
disablePopover={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, when displayUsername is enabled', () => {
const wrapper = shallow(
const {container} = render(
<UserProfile
{...baseProps}
displayUsername={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,157 +1,103 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`YoutubeVideo should match init snapshot 1`] = `
<Provider
store={
Object {
"clearActions": [Function],
"dispatch": [Function],
"getActions": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
}
}
>
<YoutubeVideo
googleDeveloperKey="googledevkey"
hasImageProxy={false}
link="https://www.youtube.com/watch?v=xqCoNej8Zxo"
metadata={
Object {
"images": Array [
Object {
"secure_url": "linkForThumbnail",
"url": "linkForThumbnail",
},
],
"title": "Youtube title",
}
}
postId="post_id_1"
show={true}
youtubeReferrerPolicy={false}
<div>
<div
class="post__embed-container"
>
<div
className="post__embed-container"
>
<div>
<h4>
<span
className="video-type"
>
YouTube -
</span>
<span
className="video-title"
>
<ForwardRef
href="https://www.youtube.com/watch?v=xqCoNej8Zxo"
location="youtube_video"
>
<a
href="https://www.youtube.com/watch?v=xqCoNej8Zxo"
location="youtube_video"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
Youtube title
</a>
</ForwardRef>
</span>
</h4>
<div
className="video-div embed-responsive-item"
<div>
<h4>
<span
class="video-type"
>
<YouTubeThumbnail
onError={[Function]}
play={[Function]}
thumbnailUrl="https://img.youtube.com/vi/xqCoNej8Zxo/maxresdefault.jpg"
videoTitle="Youtube title"
YouTube -
</span>
<span
class="video-title"
>
<a
href="https://www.youtube.com/watch?v=xqCoNej8Zxo"
location="youtube_video"
rel="noopener noreferrer"
target="_blank"
>
<div
aria-label="Play Youtube title on YouTube"
className="video-thumbnail__container"
onClick={[Function]}
onKeyDown={[Function]}
role="button"
tabIndex={0}
>
<Connect(Component)
src="https://img.youtube.com/vi/xqCoNej8Zxo/maxresdefault.jpg"
>
<Memo(ExternalImage)
dispatch={[Function]}
enableSVGs={false}
hasImageProxy={false}
src="https://img.youtube.com/vi/xqCoNej8Zxo/maxresdefault.jpg"
>
<img
alt="Thumbnail for Youtube title on YouTube"
className="video-thumbnail"
onError={[Function]}
src="https://img.youtube.com/vi/xqCoNej8Zxo/maxresdefault.jpg"
/>
</Memo(ExternalImage)>
</Connect(Component)>
<div
aria-label="Play video"
className="play-button"
role="presentation"
>
<i
aria-hidden="true"
className="icon-play"
/>
</div>
</div>
</YouTubeThumbnail>
Youtube title
</a>
</span>
</h4>
<div
class="video-div embed-responsive-item"
>
<div
aria-label="Play Youtube title on YouTube"
class="video-thumbnail__container"
role="button"
tabindex="0"
>
<img
alt="Thumbnail for Youtube title on YouTube"
class="video-thumbnail"
src="https://img.youtube.com/vi/xqCoNej8Zxo/maxresdefault.jpg"
/>
<div
aria-label="Play video"
class="play-button"
role="presentation"
>
<i
aria-hidden="true"
class="icon-play"
/>
</div>
</div>
</div>
</div>
</YoutubeVideo>
</Provider>
</div>
</div>
`;
exports[`YoutubeVideo should match snapshot for playing state 1`] = `
<div
className="post__embed-container"
>
<div>
<h4>
<span
className="video-type"
>
YouTube -
</span>
<span
className="video-title"
>
<ForwardRef
href="https://www.youtube.com/watch?v=xqCoNej8Zxo"
location="youtube_video"
<div>
<div
class="post__embed-container"
>
<div>
<h4>
<span
class="video-type"
>
Youtube title
</ForwardRef>
</span>
</h4>
<div
className="video-div embed-responsive-item"
>
YouTube -
</span>
<span
class="video-title"
>
<a
href="https://www.youtube.com/watch?v=xqCoNej8Zxo"
location="youtube_video"
rel="noopener noreferrer"
target="_blank"
>
Youtube title
</a>
</span>
</h4>
<div
className="video-playing"
class="video-div embed-responsive-item"
>
<iframe
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen={true}
frameBorder="0"
height="100%"
sandbox="allow-scripts allow-same-origin allow-popups allow-presentation"
src="https://www.youtube.com/embed/xqCoNej8Zxo?autoplay=1&rel=0&fs=1&enablejsapi=1"
title="Youtube title"
width="100%"
/>
<div
class="video-playing"
>
<iframe
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen=""
frameborder="0"
height="100%"
sandbox="allow-scripts allow-same-origin allow-popups allow-presentation"
src="https://www.youtube.com/embed/xqCoNej8Zxo?autoplay=1&rel=0&fs=1&enablejsapi=1"
title="Youtube title"
width="100%"
/>
</div>
</div>
</div>
</div>
@@ -159,44 +105,48 @@ exports[`YoutubeVideo should match snapshot for playing state 1`] = `
`;
exports[`YoutubeVideo should match snapshot for playing state and \`youtubeReferrerPolicy = true\` 1`] = `
<div
className="post__embed-container"
>
<div>
<h4>
<span
className="video-type"
>
YouTube -
</span>
<span
className="video-title"
>
<ForwardRef
href="https://www.youtube.com/watch?v=xqCoNej8Zxo"
location="youtube_video"
<div>
<div
class="post__embed-container"
>
<div>
<h4>
<span
class="video-type"
>
Youtube title
</ForwardRef>
</span>
</h4>
<div
className="video-div embed-responsive-item"
>
YouTube -
</span>
<span
class="video-title"
>
<a
href="https://www.youtube.com/watch?v=xqCoNej8Zxo"
location="youtube_video"
rel="noopener noreferrer"
target="_blank"
>
Youtube title
</a>
</span>
</h4>
<div
className="video-playing"
class="video-div embed-responsive-item"
>
<iframe
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen={true}
frameBorder="0"
height="100%"
referrerPolicy="origin"
sandbox="allow-scripts allow-same-origin allow-popups allow-presentation"
src="https://www.youtube.com/embed/xqCoNej8Zxo?autoplay=1&rel=0&fs=1&enablejsapi=1"
title="Youtube title"
width="100%"
/>
<div
class="video-playing"
>
<iframe
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen=""
frameborder="0"
height="100%"
referrerpolicy="origin"
sandbox="allow-scripts allow-same-origin allow-popups allow-presentation"
src="https://www.youtube.com/embed/xqCoNej8Zxo?autoplay=1&rel=0&fs=1&enablejsapi=1"
title="Youtube title"
width="100%"
/>
</div>
</div>
</div>
</div>
@@ -1,13 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {mount, shallow} from 'enzyme';
import React from 'react';
import {Provider} from 'react-redux';
import type {DeepPartial} from '@mattermost/types/utilities';
import mockStore from 'tests/test_store';
import {fireEvent, renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import type {GlobalState} from 'types/store';
@@ -47,40 +45,51 @@ describe('YoutubeVideo', () => {
};
test('should match init snapshot', () => {
const store = mockStore(initialState);
const wrapper = mount(
<Provider store={store}>
<YoutubeVideo {...baseProps}/>
</Provider>,
const {container} = renderWithContext(
<YoutubeVideo {...baseProps}/>,
initialState,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
// Verify that useMaxResThumbnail is true by default
expect((wrapper.find('YoutubeVideo').instance() as YoutubeVideo).state.useMaxResThumbnail).toBe(true);
expect(wrapper.find('h4').text()).toEqual('YouTube - Youtube title');
// Verify that maxresdefault thumbnail is used by default (useMaxResThumbnail = true)
expect(container.querySelector('.video-thumbnail')).toHaveAttribute(
'src',
'https://img.youtube.com/vi/xqCoNej8Zxo/maxresdefault.jpg',
);
expect(screen.getByRole('heading', {level: 4})).toHaveTextContent('YouTube - Youtube title');
});
test('should match snapshot for playing state', () => {
const wrapper = shallow(<YoutubeVideo {...baseProps}/>);
wrapper.setState({playing: true});
expect(wrapper).toMatchSnapshot();
test('should match snapshot for playing state', async () => {
const {container} = renderWithContext(
<YoutubeVideo {...baseProps}/>,
initialState,
);
// Click the play button to set playing state
await userEvent.click(screen.getByRole('button', {name: /Play Youtube title on YouTube/}));
expect(container).toMatchSnapshot();
});
test('should match snapshot for playing state and `youtubeReferrerPolicy = true`', () => {
const wrapper = shallow(
test('should match snapshot for playing state and `youtubeReferrerPolicy = true`', async () => {
const {container} = renderWithContext(
<YoutubeVideo
{...baseProps}
youtubeReferrerPolicy={true}
/>,
initialState,
);
wrapper.setState({playing: true});
expect(wrapper).toMatchSnapshot();
// Click the play button to set playing state
await userEvent.click(screen.getByRole('button', {name: /Play Youtube title on YouTube/}));
expect(container).toMatchSnapshot();
// Verify that the iframe has a referrerPolicy attribute (set to 'origin') when youtubeReferrerPolicy is true.
expect(wrapper.find('.video-playing iframe').prop('referrerPolicy')).toEqual('origin');
expect(container.querySelector('.video-playing iframe')).toHaveAttribute('referrerPolicy', 'origin');
// Verify that the iframe src includes the new parameters
expect(wrapper.find('.video-playing iframe').prop('src')).toEqual('https://www.youtube.com/embed/xqCoNej8Zxo?autoplay=1&rel=0&fs=1&enablejsapi=1');
expect(container.querySelector('.video-playing iframe')).toHaveAttribute('src', 'https://www.youtube.com/embed/xqCoNej8Zxo?autoplay=1&rel=0&fs=1&enablejsapi=1');
});
test('should use url if secure_url is not present', () => {
@@ -93,28 +102,56 @@ describe('YoutubeVideo', () => {
}],
},
};
const wrapper = shallow(<YoutubeVideo {...props}/>);
const {container} = renderWithContext(
<YoutubeVideo {...props}/>,
initialState,
);
// Verify that useMaxResThumbnail is true by default
expect(wrapper.state('useMaxResThumbnail')).toBe(true);
// Verify that maxresdefault thumbnail is used by default (useMaxResThumbnail = true)
expect(container.querySelector('.video-thumbnail')).toHaveAttribute(
'src',
'https://img.youtube.com/vi/xqCoNej8Zxo/maxresdefault.jpg',
);
});
describe('thumbnail fallback', () => {
it('should fallback to hqdefault.jpg on image error', () => {
const wrapper = shallow(<YoutubeVideo {...baseProps}/>);
const {container} = renderWithContext(
<YoutubeVideo {...baseProps}/>,
initialState,
);
// Simulate an image error by calling handleImageError.
(wrapper.instance() as YoutubeVideo).handleImageError();
const thumbnail = container.querySelector('.video-thumbnail');
expect(thumbnail).toBeInTheDocument();
// Verify that useMaxResThumbnail is now false (will use hqdefault.jpg).
expect(wrapper.state('useMaxResThumbnail')).toBe(false);
// Verify that maxresdefault is used initially
expect(thumbnail).toHaveAttribute(
'src',
'https://img.youtube.com/vi/xqCoNej8Zxo/maxresdefault.jpg',
);
// Simulate an image error
fireEvent.error(thumbnail!);
// Verify that hqdefault is used after error (useMaxResThumbnail is now false)
expect(container.querySelector('.video-thumbnail')).toHaveAttribute(
'src',
'https://img.youtube.com/vi/xqCoNej8Zxo/hqdefault.jpg',
);
});
});
it('should initialize with useMaxResThumbnail set to true', () => {
const wrapper = shallow(<YoutubeVideo {...baseProps}/>);
const {container} = renderWithContext(
<YoutubeVideo {...baseProps}/>,
initialState,
);
// Verify that the component initializes with useMaxResThumbnail = true
expect(wrapper.state('useMaxResThumbnail')).toBe(true);
// Verify that the component initializes with useMaxResThumbnail = true by checking
// that maxresdefault.jpg is used for the thumbnail
expect(container.querySelector('.video-thumbnail')).toHaveAttribute(
'src',
'https://img.youtube.com/vi/xqCoNej8Zxo/maxresdefault.jpg',
);
});
});
@@ -25,6 +25,11 @@ import type {GlobalState} from 'types/store';
export * from '@testing-library/react';
export {userEvent};
export type IntlOptions = {
messages?: Record<string, string>;
locale?: string;
}
export type FullContextOptions = {
intlMessages?: Record<string, string>;
locale?: string;