MM-67281: (test) Migrate Enzyme to RTL (#35029)

* test: migrate enzyme to rtl

* replace renderWithIntl with renderWithContext

* remove jest.clearAllMocks

* use userEvent

* fix unrelated lint error
This commit is contained in:
sabril 2026-01-27 12:22:13 +08:00 committed by GitHub
parent c537b88f93
commit 3db7477e45
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
58 changed files with 4350 additions and 5826 deletions

View file

@ -844,15 +844,19 @@ const defaultServerConfig: AdminConfig = {
AutoTranslationSettings: {
Enable: false,
Provider: '',
LibreTranslate: {
URL: '',
APIKey: '',
},
TargetLanguages: ['en'],
TimeoutsMs: {
Short: 1200,
Medium: 2500,
Long: 6000,
Notification: 300,
},
LibreTranslate: {
URL: '',
APIKey: '',
},
Agents: {
LLMServiceID: '',
},
},
};

View file

@ -2,43 +2,27 @@
exports[`components/AutosizeTextarea should match snapshot, init 1`] = `
<div>
<textarea
data-testid="autosize_textarea"
dir="auto"
height={0}
id="autosize_textarea"
role="textbox"
rows={1}
style={
Object {
"overflowY": "hidden",
}
}
/>
<div
style={
Object {
"height": 0,
"overflow": "hidden",
}
}
>
<div
aria-hidden={true}
<div>
<textarea
data-testid="autosize_textarea"
dir="auto"
disabled={true}
id="autosize_textarea-reference"
style={
Object {
"display": "inline-block",
"height": "auto",
"position": "relative",
"transform": "translateY(-100%)",
"width": "auto",
"wordBreak": "break-word",
}
}
height="0"
id="autosize_textarea"
role="textbox"
rows="1"
style="overflow-y: hidden;"
/>
<div
style="height: 0px; overflow: hidden;"
>
<div
aria-hidden="true"
dir="auto"
disabled=""
id="autosize_textarea-reference"
style="height: auto; width: auto; display: inline-block; position: relative; transform: translateY(-100%); word-break: break-word;"
/>
</div>
</div>
</div>
`;

View file

@ -1,161 +1,479 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/ColorInput should match snapshot, click on picker 1`] = `
<div
className="color-input input-group"
>
<input
className="form-control"
data-testid="color-inputColorValue"
id="sidebarBg-inputColorValue"
maxLength={7}
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
type="text"
value="#ffffff"
/>
<span
className="input-group-addon color-pad"
id="sidebarBg-squareColorIcon"
onClick={[Function]}
>
<i
className="color-icon"
id="sidebarBg-squareColorIconValue"
style={
Object {
"backgroundColor": "#ffffff",
}
}
/>
</span>
<div>
<div
className="color-popover"
id="sidebarBg-ChromePickerModal"
class="color-input input-group"
>
<ColorPicker
color="#ffffff"
disableAlpha={true}
onChange={[Function]}
styles={Object {}}
width={225}
<input
class="form-control"
data-testid="color-inputColorValue"
id="sidebarBg-inputColorValue"
maxlength="7"
type="text"
value="#ffffff"
/>
<span
class="input-group-addon color-pad"
id="sidebarBg-squareColorIcon"
>
<i
class="color-icon"
id="sidebarBg-squareColorIconValue"
style="background-color: rgb(255, 255, 255);"
/>
</span>
<div
class="color-popover"
id="sidebarBg-ChromePickerModal"
>
<div
class="chrome-picker "
style="width: 225px; background: rgb(255, 255, 255); border-radius: 2px; box-shadow: 0 0 2px rgba(0,0,0,.3), 0 4px 8px rgba(0,0,0,.3); box-sizing: initial; font-family: Menlo;"
>
<div
style="width: 100%; padding-bottom: 55%; position: relative; border-radius: 2px 2px 0 0; overflow: hidden;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; background: rgb(255, 0, 0);"
>
<style>
.saturation-white {
background: -webkit-linear-gradient(to right, #fff, rgba(255,255,255,0));
background: linear-gradient(to right, #fff, rgba(255,255,255,0));
}
.saturation-black {
background: -webkit-linear-gradient(to top, #000, rgba(0,0,0,0));
background: linear-gradient(to top, #000, rgba(0,0,0,0));
}
</style>
<div
class="saturation-white"
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
>
<div
class="saturation-black"
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
/>
<div
style="position: absolute; top: 0%; left: 0%; cursor: default;"
>
<div
style="width: 12px; height: 12px; border-radius: 6px; box-shadow: inset 0 0 0 1px #fff; -webkit-transform: translate(-6px, -6px); transform: translate(-6px, -6px);"
/>
</div>
</div>
</div>
</div>
<div
style="padding: 16px 16px 12px;"
>
<div
class="flexbox-fix"
style="display: flex;"
>
<div
style="width: 22px;"
>
<div
style="margin-top: 0px; width: 10px; height: 10px; border-radius: 8px; position: relative; overflow: hidden;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; border-radius: 8px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.1); background: rgb(255, 255, 255); z-index: 2;"
/>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
/>
</div>
</div>
<div
style="flex: 1 1 0%;"
>
<div
style="height: 10px; position: relative; margin-bottom: 0px;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
>
<div
class="hue-horizontal"
style="padding: 0px 2px; position: relative; height: 100%;"
>
<style>
.hue-horizontal {
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0
33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
background: -webkit-linear-gradient(to right, #f00 0%, #ff0
17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
}
.hue-vertical {
background: linear-gradient(to top, #f00 0%, #ff0 17%, #0f0 33%,
#0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
background: -webkit-linear-gradient(to top, #f00 0%, #ff0 17%,
#0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
}
</style>
<div
style="position: absolute; left: 0%;"
>
<div
style="width: 12px; height: 12px; border-radius: 6px; -webkit-transform: translate(-6px, -1px); transform: translate(-6px, -1px); background-color: rgb(248, 248, 248); box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37);"
/>
</div>
</div>
</div>
</div>
<div
style="height: 10px; position: relative; display: none;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; overflow: hidden;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
/>
</div>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; background: linear-gradient(to right, rgba(255,255,255, 0) 0%, rgba(255,255,255, 1) 100%);"
/>
<div
style="position: relative; height: 100%; margin: 0px 3px;"
>
<div
style="position: absolute; left: 100%;"
>
<div
style="width: 12px; height: 12px; border-radius: 6px; -webkit-transform: translate(-6px, -1px); transform: translate(-6px, -1px); background-color: rgb(248, 248, 248); box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37);"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="flexbox-fix"
style="padding-top: 16px; display: flex;"
>
<div
class="flexbox-fix"
style="flex: 1 1 0%; display: flex; margin-left: -6px;"
>
<div
style="padding-left: 6px; width: 100%;"
>
<div
style="position: relative;"
>
<input
id="rc-editable-input-3"
spellcheck="false"
style="font-size: 11px; color: rgb(51, 51, 51); width: 100%; border-radius: 2px; box-shadow: inset 0 0 0 1px #dadada; height: 21px; text-align: center;"
value="#FFFFFF"
/>
<label
for="rc-editable-input-3"
style="text-transform: uppercase; font-size: 11px; line-height: 11px; color: rgb(150, 150, 150); text-align: center; display: block; margin-top: 12px;"
>
hex
</label>
</div>
</div>
</div>
<div
style="width: 32px; text-align: right; position: relative;"
>
<div
style="margin-right: -4px; margin-top: 12px; cursor: pointer; position: relative;"
>
<svg
style="fill: #333; width: 24px; height: 24px; border: 1px solid transparent; border-radius: 5px;"
viewBox="0 0 24 24"
>
<path
d="M12,18.17L8.83,15L7.42,16.41L12,21L16.59,16.41L15.17,15M12,5.83L15.17,9L16.58,7.59L12,3L7.41,7.59L8.83,9L12,5.83Z"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`components/ColorInput should match snapshot, init 1`] = `
<div
className="color-input input-group"
>
<input
className="form-control"
data-testid="color-inputColorValue"
id="sidebarBg-inputColorValue"
maxLength={7}
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
type="text"
value="#ffffff"
/>
<span
className="input-group-addon color-pad"
id="sidebarBg-squareColorIcon"
onClick={[Function]}
<div>
<div
class="color-input input-group"
>
<i
className="color-icon"
id="sidebarBg-squareColorIconValue"
style={
Object {
"backgroundColor": "#ffffff",
}
}
<input
class="form-control"
data-testid="color-inputColorValue"
id="sidebarBg-inputColorValue"
maxlength="7"
type="text"
value="#ffffff"
/>
</span>
<span
class="input-group-addon color-pad"
id="sidebarBg-squareColorIcon"
>
<i
class="color-icon"
id="sidebarBg-squareColorIconValue"
style="background-color: rgb(255, 255, 255);"
/>
</span>
</div>
</div>
`;
exports[`components/ColorInput should match snapshot, opened 1`] = `
<div
className="color-input input-group"
>
<input
className="form-control"
data-testid="color-inputColorValue"
id="sidebarBg-inputColorValue"
maxLength={7}
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
type="text"
value="#ffffff"
/>
<span
className="input-group-addon color-pad"
id="sidebarBg-squareColorIcon"
onClick={[Function]}
>
<i
className="color-icon"
id="sidebarBg-squareColorIconValue"
style={
Object {
"backgroundColor": "#ffffff",
}
}
/>
</span>
<div>
<div
className="color-popover"
id="sidebarBg-ChromePickerModal"
class="color-input input-group"
>
<ColorPicker
color="#ffffff"
disableAlpha={true}
onChange={[Function]}
styles={Object {}}
width={225}
<input
class="form-control"
data-testid="color-inputColorValue"
id="sidebarBg-inputColorValue"
maxlength="7"
type="text"
value="#ffffff"
/>
<span
class="input-group-addon color-pad"
id="sidebarBg-squareColorIcon"
>
<i
class="color-icon"
id="sidebarBg-squareColorIconValue"
style="background-color: rgb(255, 255, 255);"
/>
</span>
<div
class="color-popover"
id="sidebarBg-ChromePickerModal"
>
<div
class="chrome-picker "
style="width: 225px; background: rgb(255, 255, 255); border-radius: 2px; box-shadow: 0 0 2px rgba(0,0,0,.3), 0 4px 8px rgba(0,0,0,.3); box-sizing: initial; font-family: Menlo;"
>
<div
style="width: 100%; padding-bottom: 55%; position: relative; border-radius: 2px 2px 0 0; overflow: hidden;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; background: rgb(255, 0, 0);"
>
<style>
.saturation-white {
background: -webkit-linear-gradient(to right, #fff, rgba(255,255,255,0));
background: linear-gradient(to right, #fff, rgba(255,255,255,0));
}
.saturation-black {
background: -webkit-linear-gradient(to top, #000, rgba(0,0,0,0));
background: linear-gradient(to top, #000, rgba(0,0,0,0));
}
</style>
<div
class="saturation-white"
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
>
<div
class="saturation-black"
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
/>
<div
style="position: absolute; top: 0%; left: 0%; cursor: default;"
>
<div
style="width: 12px; height: 12px; border-radius: 6px; box-shadow: inset 0 0 0 1px #fff; -webkit-transform: translate(-6px, -6px); transform: translate(-6px, -6px);"
/>
</div>
</div>
</div>
</div>
<div
style="padding: 16px 16px 12px;"
>
<div
class="flexbox-fix"
style="display: flex;"
>
<div
style="width: 22px;"
>
<div
style="margin-top: 0px; width: 10px; height: 10px; border-radius: 8px; position: relative; overflow: hidden;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; border-radius: 8px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.1); background: rgb(255, 255, 255); z-index: 2;"
/>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
/>
</div>
</div>
<div
style="flex: 1 1 0%;"
>
<div
style="height: 10px; position: relative; margin-bottom: 0px;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
>
<div
class="hue-horizontal"
style="padding: 0px 2px; position: relative; height: 100%;"
>
<style>
.hue-horizontal {
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0
33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
background: -webkit-linear-gradient(to right, #f00 0%, #ff0
17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
}
.hue-vertical {
background: linear-gradient(to top, #f00 0%, #ff0 17%, #0f0 33%,
#0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
background: -webkit-linear-gradient(to top, #f00 0%, #ff0 17%,
#0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
}
</style>
<div
style="position: absolute; left: 0%;"
>
<div
style="width: 12px; height: 12px; border-radius: 6px; -webkit-transform: translate(-6px, -1px); transform: translate(-6px, -1px); background-color: rgb(248, 248, 248); box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37);"
/>
</div>
</div>
</div>
</div>
<div
style="height: 10px; position: relative; display: none;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; overflow: hidden;"
>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"
/>
</div>
<div
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; background: linear-gradient(to right, rgba(255,255,255, 0) 0%, rgba(255,255,255, 1) 100%);"
/>
<div
style="position: relative; height: 100%; margin: 0px 3px;"
>
<div
style="position: absolute; left: 100%;"
>
<div
style="width: 12px; height: 12px; border-radius: 6px; -webkit-transform: translate(-6px, -1px); transform: translate(-6px, -1px); background-color: rgb(248, 248, 248); box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37);"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="flexbox-fix"
style="padding-top: 16px; display: flex;"
>
<div
class="flexbox-fix"
style="flex: 1 1 0%; display: flex; margin-left: -6px;"
>
<div
style="padding-left: 6px; width: 100%;"
>
<div
style="position: relative;"
>
<input
id="rc-editable-input-1"
spellcheck="false"
style="font-size: 11px; color: rgb(51, 51, 51); width: 100%; border-radius: 2px; box-shadow: inset 0 0 0 1px #dadada; height: 21px; text-align: center;"
value="#FFFFFF"
/>
<label
for="rc-editable-input-1"
style="text-transform: uppercase; font-size: 11px; line-height: 11px; color: rgb(150, 150, 150); text-align: center; display: block; margin-top: 12px;"
>
hex
</label>
</div>
</div>
</div>
<div
style="width: 32px; text-align: right; position: relative;"
>
<div
style="margin-right: -4px; margin-top: 12px; cursor: pointer; position: relative;"
>
<svg
style="fill: #333; width: 24px; height: 24px; border: 1px solid transparent; border-radius: 5px;"
viewBox="0 0 24 24"
>
<path
d="M12,18.17L8.83,15L7.42,16.41L12,21L16.59,16.41L15.17,15M12,5.83L15.17,9L16.58,7.59L12,3L7.41,7.59L8.83,9L12,5.83Z"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`components/ColorInput should match snapshot, toggle picker 1`] = `
<div
className="color-input input-group"
>
<input
className="form-control"
data-testid="color-inputColorValue"
id="sidebarBg-inputColorValue"
maxLength={7}
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
type="text"
value="#ffffff"
/>
<span
className="input-group-addon color-pad"
id="sidebarBg-squareColorIcon"
onClick={[Function]}
<div>
<div
class="color-input input-group"
>
<i
className="color-icon"
id="sidebarBg-squareColorIconValue"
style={
Object {
"backgroundColor": "#ffffff",
}
}
<input
class="form-control"
data-testid="color-inputColorValue"
id="sidebarBg-inputColorValue"
maxlength="7"
type="text"
value="#ffffff"
/>
</span>
<span
class="input-group-addon color-pad"
id="sidebarBg-squareColorIcon"
>
<i
class="color-icon"
id="sidebarBg-squareColorIconValue"
style="background-color: rgb(255, 255, 255);"
/>
</span>
</div>
</div>
`;

View file

@ -1,194 +1,129 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/SearchableChannelList should match init snapshot 1`] = `
<div
className="filtered-user-list"
>
<div>
<div
className="filter-row"
class="filtered-user-list"
>
<span
aria-hidden="true"
id="searchIcon"
>
<i
className="icon icon-magnify"
/>
</span>
<QuickInput
aria-label="Search Channels"
className="form-control filter-textbox"
clearable={true}
id="searchChannelsTextbox"
onClear={[Function]}
onInput={[Function]}
placeholder="Search channels"
value=""
/>
</div>
<div
className="more-modal__dropdown"
>
<span
id="channelCountLabel"
>
0 Results
</span>
<span
aria-live="polite"
className="sr-only"
role="status"
>
0 Results
</span>
<div
id="modalPreferenceContainer"
class="filter-row"
>
<span
aria-hidden="true"
id="searchIcon"
>
<i
class="icon icon-magnify"
/>
</span>
<div
aria-atomic="true"
className="sr-only"
class="input-wrapper"
>
<input
aria-label="Search Channels"
class="form-control filter-textbox"
id="searchChannelsTextbox"
placeholder="Search channels"
value=""
/>
</div>
</div>
<div
class="more-modal__dropdown"
>
<span
id="channelCountLabel"
>
0 Results
</span>
<span
aria-live="polite"
class="sr-only"
role="status"
>
Channel type filter set to {filterType}
</div>
<Menu
menu={
Object {
"aria-label": "Channel type filter",
"id": "browseChannelsDropdown",
}
}
menuButton={
Object {
"aria-label": "Channel type filter",
"children": <React.Fragment>
<Memo(MemoizedFormattedMessage)
defaultMessage="Channel Type: All"
id="more_channels.show_all_channels"
/>
<ChevronDownIcon
color="rgba(var(--center-channel-color-rgb), 0.64)"
size={16}
/>
</React.Fragment>,
"id": "menuWrapper",
}
}
>
<MenuItem
aria-label="All channel types"
id="channelsMoreDropdownAll"
key="channelsMoreDropdownAll"
labels={
<Memo(MemoizedFormattedMessage)
defaultMessage="All channel types"
id="suggestion.all"
/>
}
leadingElement={
<GlobeCheckedIcon
size={16}
/>
}
onClick={[Function]}
trailingElements={
<CheckIcon
color="var(--button-bg)"
size={18}
/>
}
/>
<MenuItem
aria-label="Public channels"
id="channelsMoreDropdownPublic"
key="channelsMoreDropdownPublic"
labels={
<Memo(MemoizedFormattedMessage)
defaultMessage="Public channels"
id="suggestion.public"
/>
}
leadingElement={
<GlobeIcon
size={16}
/>
}
onClick={[Function]}
trailingElements={null}
/>
<MenuItem
aria-label="Private channels"
id="channelsMoreDropdownPrivate"
key="channelsMoreDropdownPrivate"
labels={
<Memo(MemoizedFormattedMessage)
defaultMessage="Private channels"
id="suggestion.private"
/>
}
leadingElement={
<LockOutlineIcon
size={16}
/>
}
onClick={[Function]}
trailingElements={null}
/>
<MenuItemSeparator
key="channelsMoreDropdownSeparator"
/>
<MenuItem
aria-label="Archived channels"
id="channelsMoreDropdownArchived"
key="channelsMoreDropdownArchived"
labels={
<Memo(MemoizedFormattedMessage)
defaultMessage="Archived channels"
id="suggestion.archive"
/>
}
leadingElement={
<ArchiveOutlineIcon
size={16}
/>
}
onClick={[Function]}
trailingElements={null}
/>
</Menu>
0 Results
</span>
<div
aria-checked={false}
aria-label="Hide joined channels"
id="hideJoinedPreferenceCheckbox"
onClick={[Function]}
onKeyDown={[Function]}
role="checkbox"
tabIndex={0}
id="modalPreferenceContainer"
>
<div
className="get-app__checkbox"
/>
<MemoizedFormattedMessage
defaultMessage="Hide Joined"
id="more_channels.hide_joined"
/>
aria-atomic="true"
class="sr-only"
role="status"
>
Channel type filter set to {filterType}
</div>
<button
aria-controls="browseChannelsDropdown"
aria-expanded="false"
aria-haspopup="true"
aria-label="Channel type filter"
class=""
id="menuWrapper"
>
Channel Type: All
<svg
fill="rgba(var(--center-channel-color-rgb), 0.64)"
height="16"
version="1.1"
viewBox="0 0 24 24"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"
/>
</svg>
</button>
<div
aria-checked="false"
aria-label="Hide joined channels"
id="hideJoinedPreferenceCheckbox"
role="checkbox"
tabindex="0"
>
<div
class="get-app__checkbox"
/>
Hide Joined
</div>
</div>
</div>
</div>
<div
className="more-modal__list"
role="search"
tabIndex={-1}
>
<div
id="moreChannelsList"
tabIndex={-1}
class="more-modal__list"
role="search"
tabindex="-1"
>
<LoadingScreen />
<div
id="moreChannelsList"
tabindex="-1"
>
<div
class="loading-screen"
style="position: relative;"
>
<div
class="loading__content"
>
<p>
Loading
</p>
<div
class="round round-1"
/>
<div
class="round round-2"
/>
<div
class="round round-3"
/>
</div>
</div>
</div>
</div>
<div
class="filter-controls"
/>
</div>
<div
className="filter-controls"
/>
</div>
`;

View file

@ -1,67 +1,71 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/RadioSetting should match snapshot 1`] = `
<Memo(Settings)
inputId="string.id"
label="some label"
setByEnv={false}
>
<label
className="RadioSetting__label"
key="Engineering"
<div>
<div
class="form-group"
data-testid="string.id"
>
<input
checked={false}
className="RadioSetting__input"
disabled={false}
name="string.id"
onChange={[Function]}
type="radio"
value="Engineering"
/>
<span
className="RadioSetting__text"
<label
class="control-label col-sm-4"
for="string.id"
>
this is engineering
</span>
</label>
<label
className="RadioSetting__label"
key="Sales"
>
<input
checked={true}
className="RadioSetting__input"
disabled={false}
name="string.id"
onChange={[Function]}
type="radio"
value="Sales"
/>
<span
className="RadioSetting__text"
some label
</label>
<div
class="col-sm-8"
>
this is sales
</span>
</label>
<label
className="RadioSetting__label"
key="Administration"
>
<input
checked={false}
className="RadioSetting__input"
disabled={false}
name="string.id"
onChange={[Function]}
type="radio"
value="Administration"
/>
<span
className="RadioSetting__text"
>
this is administration
</span>
</label>
</Memo(Settings)>
<label
class="RadioSetting__label"
>
<input
class="RadioSetting__input"
name="string.id"
type="radio"
value="Engineering"
/>
<span
class="RadioSetting__text"
>
this is engineering
</span>
</label>
<label
class="RadioSetting__label"
>
<input
checked=""
class="RadioSetting__input"
name="string.id"
type="radio"
value="Sales"
/>
<span
class="RadioSetting__text"
>
this is sales
</span>
</label>
<label
class="RadioSetting__label"
>
<input
class="RadioSetting__input"
name="string.id"
type="radio"
value="Administration"
/>
<span
class="RadioSetting__text"
>
this is administration
</span>
</label>
<div
class="help-text"
data-testid="string.idhelp-text"
/>
</div>
</div>
</div>
`;

View file

@ -1,41 +1,41 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`SchemaText should render markdown text correctly 1`] = `
<span
dangerouslySetInnerHTML={
Object {
"__html": "This is <strong>HELP TEXT</strong>",
}
}
/>
<div>
<span>
This is
<strong>
HELP TEXT
</strong>
</span>
</div>
`;
exports[`SchemaText should render plain text correctly 1`] = `
<span>
This is help text
</span>
<div>
<span>
This is help text
</span>
</div>
`;
exports[`SchemaText should render translated markdown text correctly 1`] = `
<FormattedMarkdownMessage
defaultMessage="This is [{object}](https://example.com)"
id="help.text.markdown"
values={
Object {
"object": "a help link",
}
}
/>
<div>
<span>
This is
<a
href="https://example.com"
rel="noopener noreferrer"
target="_blank"
>
a help link
</a>
</span>
</div>
`;
exports[`SchemaText should render translated text correctly 1`] = `
<MemoizedFormattedMessage
defaultMessage="This is {object}"
id="help.text"
values={
Object {
"object": "help text",
}
}
/>
<div>
This is help text
</div>
`;

View file

@ -1,41 +1,44 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/admin_button_outline/AdminButtonOutline should match snapshot with children 1`] = `
<button
className="AdminButtonOutline btn admin-btn-default"
disabled={true}
onClick={[MockFunction]}
type="button"
>
Test children
</button>
<div>
<button
class="AdminButtonOutline btn admin-btn-default"
disabled=""
type="button"
>
Test children
</button>
</div>
`;
exports[`components/admin_console/admin_button_outline/AdminButtonOutline should match snapshot with className is not provided in scss file 1`] = `
<button
className="AdminButtonOutline btn btn-default"
disabled={true}
onClick={[MockFunction]}
type="button"
>
Test children
</button>
<div>
<button
class="AdminButtonOutline btn btn-default"
disabled=""
type="button"
>
Test children
</button>
</div>
`;
exports[`components/admin_console/admin_button_outline/AdminButtonOutline should match snapshot with prop disable false 1`] = `
<button
className="AdminButtonOutline btn admin-btn-default"
disabled={false}
onClick={[MockFunction]}
type="button"
/>
<div>
<button
class="AdminButtonOutline btn admin-btn-default"
type="button"
/>
</div>
`;
exports[`components/admin_console/admin_button_outline/AdminButtonOutline should match snapshot with prop disable true 1`] = `
<button
className="AdminButtonOutline btn admin-btn-default"
disabled={true}
onClick={[MockFunction]}
type="button"
/>
<div>
<button
class="AdminButtonOutline btn admin-btn-default"
disabled=""
type="button"
/>
</div>
`;

View file

@ -1,39 +1,40 @@
// 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 AdminButtonOutline from './admin_button_outline';
describe('components/admin_console/admin_button_outline/AdminButtonOutline', () => {
test('should match snapshot with prop disable false', () => {
const onClick = jest.fn();
const wrapper = shallow(
const {container} = render(
<AdminButtonOutline
onClick={onClick}
className='admin-btn-default'
disabled={false}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with prop disable true', () => {
const onClick = jest.fn();
const wrapper = shallow(
const {container} = render(
<AdminButtonOutline
onClick={onClick}
className='admin-btn-default'
disabled={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with children', () => {
const onClick = jest.fn();
const wrapper = shallow(
const {container} = render(
<AdminButtonOutline
onClick={onClick}
className='admin-btn-default'
@ -42,12 +43,12 @@ describe('components/admin_console/admin_button_outline/AdminButtonOutline', ()
{'Test children'}
</AdminButtonOutline>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with className is not provided in scss file', () => {
const onClick = jest.fn();
const wrapper = shallow(
const {container} = render(
<AdminButtonOutline
onClick={onClick}
className='btn-default'
@ -56,22 +57,22 @@ describe('components/admin_console/admin_button_outline/AdminButtonOutline', ()
{'Test children'}
</AdminButtonOutline>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should handle onClick', () => {
test('should handle onClick', async () => {
const onClick = jest.fn();
const wrapper = shallow(
render(
<AdminButtonOutline
onClick={onClick}
className='admin-btn-default'
disabled={true}
disabled={false}
>
{'Test children'}
</AdminButtonOutline>,
);
wrapper.find('button').simulate('click');
await userEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
});

View file

@ -1,266 +1,211 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/data_grid/DataGrid should match snapshot while loading 1`] = `
<div
className="DataGrid"
>
<Memo(DataGridSearch)
onSearch={[Function]}
term=""
/>
<DataGridHeader
columns={Array []}
/>
<div>
<div
className="DataGrid_rows"
style={Object {}}
class="DataGrid"
>
<div
className="DataGrid_loading"
class="DataGrid_search"
>
<Memo(LoadingSpinner) />
<MemoizedFormattedMessage
defaultMessage="Loading"
id="admin.data_grid.loading"
/>
<div
class="DataGrid_searchBar"
>
<span
aria-hidden="true"
class="DataGrid_searchIcon"
>
<i
class="fa fa-search"
title="Search Icon"
/>
</span>
<input
data-testid="searchInput"
placeholder="Search"
type="text"
value=""
/>
<i
class="DataGrid_clearButton fa fa-times-circle hidden"
data-testid="clear-search"
/>
</div>
</div>
<div
class="DataGrid_header"
/>
<div
class="DataGrid_rows"
>
<div
class="DataGrid_loading"
>
<span
class="LoadingSpinner"
data-testid="loadingSpinner"
id="loadingSpinner"
>
<span
class="fa fa-spinner fa-fw fa-pulse spinner"
title="Loading Icon"
/>
</span>
Loading
</div>
</div>
</div>
</div>
`;
exports[`components/admin_console/data_grid/DataGrid should match snapshot with content and custom styling on rows 1`] = `
<div
className="DataGrid"
>
<Memo(DataGridSearch)
onSearch={[Function]}
term=""
/>
<DataGridHeader
columns={
Array [
Object {
"field": "name",
"name": "Name",
"overflow": "hidden",
"width": 3,
},
Object {
"field": "team",
"name": "Team",
"textAlign": "center",
},
]
}
/>
<div>
<div
className="DataGrid_rows"
style={Object {}}
>
<Memo(DataGridRow)
columns={
Array [
Object {
"field": "name",
"name": "Name",
"overflow": "hidden",
"width": 3,
},
Object {
"field": "team",
"name": "Team",
"textAlign": "center",
},
]
}
key="0"
row={
Object {
"cells": Object {
"name": "Joe Schmoe",
"team": "Admin Team",
},
}
}
/>
<Memo(DataGridRow)
columns={
Array [
Object {
"field": "name",
"name": "Name",
"overflow": "hidden",
"width": 3,
},
Object {
"field": "team",
"name": "Team",
"textAlign": "center",
},
]
}
key="1"
row={
Object {
"cells": Object {
"name": "Foo Bar",
"team": "Admin Team",
},
}
}
/>
<Memo(DataGridRow)
columns={
Array [
Object {
"field": "name",
"name": "Name",
"overflow": "hidden",
"width": 3,
},
Object {
"field": "team",
"name": "Team",
"textAlign": "center",
},
]
}
key="2"
row={
Object {
"cells": Object {
"name": "Some Guy",
"team": "Admin Team",
},
}
}
/>
</div>
</div>
`;
exports[`components/admin_console/data_grid/DataGrid should match snapshot with custom classes 1`] = `
<div
className="DataGrid customTable"
>
<Memo(DataGridSearch)
onSearch={[Function]}
term=""
/>
<DataGridHeader
columns={
Array [
Object {
"field": "name",
"name": "Name",
},
Object {
"field": "team",
"name": "Team",
},
]
}
/>
<div
className="DataGrid_rows"
style={Object {}}
>
<Memo(DataGridRow)
columns={
Array [
Object {
"field": "name",
"name": "Name",
},
Object {
"field": "team",
"name": "Team",
},
]
}
key="0"
row={
Object {
"cells": Object {
"name": "Joe Schmoe",
"team": "Admin Team",
},
}
}
/>
<Memo(DataGridRow)
columns={
Array [
Object {
"field": "name",
"name": "Name",
},
Object {
"field": "team",
"name": "Team",
},
]
}
key="1"
row={
Object {
"cells": Object {
"name": "Foo Bar",
"team": "Admin Team",
},
}
}
/>
<Memo(DataGridRow)
columns={
Array [
Object {
"field": "name",
"name": "Name",
},
Object {
"field": "team",
"name": "Team",
},
]
}
key="2"
row={
Object {
"cells": Object {
"name": "Some Guy",
"team": "Admin Team",
},
}
}
/>
</div>
</div>
`;
exports[`components/admin_console/data_grid/DataGrid should match snapshot with no items found 1`] = `
<div
className="DataGrid"
>
<Memo(DataGridSearch)
onSearch={[Function]}
term=""
/>
<DataGridHeader
columns={Array []}
/>
<div
className="DataGrid_rows"
style={Object {}}
class="DataGrid"
>
<div
className="DataGrid_empty"
class="DataGrid_search"
>
<MemoizedFormattedMessage
defaultMessage="No items found"
id="admin.data_grid.empty"
<div
class="DataGrid_searchBar"
>
<span
aria-hidden="true"
class="DataGrid_searchIcon"
>
<i
class="fa fa-search"
title="Search Icon"
/>
</span>
<input
data-testid="searchInput"
placeholder="Search"
type="text"
value=""
/>
<i
class="DataGrid_clearButton fa fa-times-circle hidden"
data-testid="clear-search"
/>
</div>
</div>
<div
class="DataGrid_header"
/>
<div
class="DataGrid_rows"
>
<div
class="DataGrid_row"
/>
<div
class="DataGrid_row"
/>
<div
class="DataGrid_row"
/>
</div>
</div>
</div>
`;
exports[`components/admin_console/data_grid/DataGrid should match snapshot with custom classes 1`] = `
<div>
<div
class="DataGrid customTable"
>
<div
class="DataGrid_search"
>
<div
class="DataGrid_searchBar"
>
<span
aria-hidden="true"
class="DataGrid_searchIcon"
>
<i
class="fa fa-search"
title="Search Icon"
/>
</span>
<input
data-testid="searchInput"
placeholder="Search"
type="text"
value=""
/>
<i
class="DataGrid_clearButton fa fa-times-circle hidden"
data-testid="clear-search"
/>
</div>
</div>
<div
class="DataGrid_header"
/>
<div
class="DataGrid_rows"
>
<div
class="DataGrid_row"
/>
<div
class="DataGrid_row"
/>
<div
class="DataGrid_row"
/>
</div>
</div>
</div>
`;
exports[`components/admin_console/data_grid/DataGrid should match snapshot with no items found 1`] = `
<div>
<div
class="DataGrid"
>
<div
class="DataGrid_search"
>
<div
class="DataGrid_searchBar"
>
<span
aria-hidden="true"
class="DataGrid_searchIcon"
>
<i
class="fa fa-search"
title="Search Icon"
/>
</span>
<input
data-testid="searchInput"
placeholder="Search"
type="text"
value=""
/>
<i
class="DataGrid_clearButton fa fa-times-circle hidden"
data-testid="clear-search"
/>
</div>
</div>
<div
class="DataGrid_header"
/>
<div
class="DataGrid_rows"
>
<div
class="DataGrid_empty"
>
No items found
</div>
</div>
</div>
</div>
`;

View file

@ -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 {renderWithContext} from 'tests/react_testing_utils';
import DataGrid from './data_grid';
describe('components/admin_console/data_grid/DataGrid', () => {
@ -24,26 +25,26 @@ describe('components/admin_console/data_grid/DataGrid', () => {
};
test('should match snapshot with no items found', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<DataGrid
{...baseProps}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot while loading', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<DataGrid
{...baseProps}
loading={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with content and custom styling on rows', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<DataGrid
{...baseProps}
rows={[
@ -57,11 +58,11 @@ describe('components/admin_console/data_grid/DataGrid', () => {
]}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with custom classes', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<DataGrid
{...baseProps}
rows={[
@ -76,6 +77,6 @@ describe('components/admin_console/data_grid/DataGrid', () => {
className={'customTable'}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});

View file

@ -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 {FormattedMessage} from 'react-intl';
import JobCancelButton from './job_cancel_button';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import type {Props} from './table';
import JobTable from './table';
@ -100,7 +100,7 @@ describe('components/admin_console/jobs/table', () => {
}, {
create_at: 1540834294674,
last_activity_at: 1540834294674,
id: '1236',
id: '1237',
status: 'warning',
type: 'data_retention',
priority: 0,
@ -110,21 +110,26 @@ describe('components/admin_console/jobs/table', () => {
}],
};
test('should call create job func', () => {
const wrapper = shallow(
test('should call create job func', async () => {
renderWithContext(
<JobTable {...baseProps}/>,
);
wrapper.find('.job-table__create-button > div > .btn-tertiary').simulate('click', {preventDefault: jest.fn()});
const createButton = screen.getByRole('button', {name: 'Run Compliance Export Job Now'});
await userEvent.click(createButton);
expect(createJob).toHaveBeenCalledTimes(1);
});
test('should call cancel job func', () => {
const wrapper = shallow(
test('should call cancel job func', async () => {
const {container} = renderWithContext(
<JobTable {...baseProps}/>,
);
wrapper.find(JobCancelButton).first().simulate('click', {preventDefault: jest.fn(), currentTarget: {getAttribute: () => '1234'}});
// JobCancelButton only shows for jobs with status 'pending' or 'in_progress'
const cancelButtons = container.querySelectorAll('.JobCancelButton');
expect(cancelButtons.length).toBeGreaterThan(0);
await userEvent.click(cancelButtons[0]);
expect(cancelJob).toHaveBeenCalledTimes(1);
});
@ -138,7 +143,7 @@ describe('components/admin_console/jobs/table', () => {
{header: 'Details'},
];
const wrapper = shallow(
const {container} = renderWithContext(
<JobTable
{...baseProps}
jobType='message_export'
@ -147,15 +152,15 @@ describe('components/admin_console/jobs/table', () => {
);
// There should be ONLY 1 table element
const table = wrapper.find('table');
const table = container.querySelectorAll('table');
expect(table).toHaveLength(1);
// The table should have ONLY 1 thead element
const thead = table.find('thead');
const thead = container.querySelectorAll('thead');
expect(thead).toHaveLength(1);
// The number of th tags should be equal to number of columns
const headers = thead.find('th');
const headers = container.querySelectorAll('th');
expect(headers).toHaveLength(cols.length);
});
@ -168,7 +173,7 @@ describe('components/admin_console/jobs/table', () => {
{header: 'Details'},
];
const wrapper = shallow(
const {container} = renderWithContext(
<JobTable
{...baseProps}
downloadExportResults={false}
@ -176,39 +181,39 @@ describe('components/admin_console/jobs/table', () => {
);
// There should be ONLY 1 table element
const table = wrapper.find('table');
const table = container.querySelectorAll('table');
expect(table).toHaveLength(1);
// The table should have ONLY 1 thead element
const thead = table.find('thead');
const thead = container.querySelectorAll('thead');
expect(thead).toHaveLength(1);
// The number of th tags should be equal to number of columns
const headers = thead.find('th');
const headers = container.querySelectorAll('th');
expect(headers).toHaveLength(cols.length);
});
test('hide create job button', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<JobTable
{...baseProps}
hideJobCreateButton={true}
/>,
);
const button = wrapper.find('button.btn-default');
const button = container.querySelectorAll('button.btn-default');
expect(button).toHaveLength(0);
});
test('add custom class', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<JobTable
{...baseProps}
className={'job-table__data-retention'}
/>,
);
const element = wrapper.find('.job-table__data-retention');
const element = container.querySelectorAll('.job-table__data-retention');
expect(element).toHaveLength(1);
});
});

View file

@ -1,15 +1,16 @@
// 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 RadioSetting from './radio_setting';
describe('components/admin_console/RadioSetting', () => {
test('should match snapshot', () => {
const onChange = jest.fn();
const wrapper = shallow(
const {container} = render(
<RadioSetting
id='string.id'
label='some label'
@ -23,12 +24,12 @@ describe('components/admin_console/RadioSetting', () => {
setByEnv={false}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('onChange', () => {
test('onChange', async () => {
const onChange = jest.fn();
const wrapper = shallow(
render(
<RadioSetting
id='string.id'
label='some label'
@ -42,7 +43,9 @@ describe('components/admin_console/RadioSetting', () => {
setByEnv={false}
/>,
);
wrapper.find('input').at(0).simulate('change', {target: {value: 'Administration'}});
const radios = screen.getAllByRole('radio');
await userEvent.click(radios[2]);
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange).toHaveBeenCalledWith('string.id', 'Administration');

View file

@ -1,517 +1,164 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/request_button/request_button.jsx should match snapshot 1`] = `
<div
className="form-group"
>
<div>
<div
className="col-sm-offset-4 col-sm-8"
class="form-group"
>
<div>
<button
className="btn btn-tertiary"
disabled={false}
onClick={[Function]}
type="button"
>
<Memo(LoadingWrapper)
loading={false}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Loading..."
id="admin.requestButton.loading"
/>
}
>
<MemoizedFormattedMessage
defaultMessage="Button Text"
id="test2"
/>
</Memo(LoadingWrapper)>
</button>
</div>
<div
className="help-text"
class="col-sm-offset-4 col-sm-8"
>
<MemoizedFormattedMessage
defaultMessage="Help Text"
id="test1"
/>
<div>
<button
class="btn btn-tertiary"
type="button"
>
Button Text
</button>
</div>
<div
class="help-text"
>
Help Text
</div>
</div>
</div>
</div>
`;
exports[`components/admin_console/request_button/request_button.jsx should match snapshot with request error 1`] = `
<RequestButton
buttonText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Button Text"
id="test2"
/>
}
disabled={false}
errorMessage={
Object {
"defaultMessage": "Error Message: {error}",
"id": "error.message",
}
}
helpText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Help Text"
id="test1"
/>
}
includeDetailedError={true}
requestAction={
[MockFunction] {
"calls": Array [
Array [
[Function],
[Function],
],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
],
}
}
saveNeeded={false}
showSuccessMessage={true}
successMessage={
Object {
"defaultMessage": "Test Successful",
"id": "admin.requestButton.requestSuccess",
}
}
>
<div>
<div
className="form-group"
class="form-group"
>
<div
className="col-sm-offset-4 col-sm-8"
class="col-sm-offset-4 col-sm-8"
>
<div>
<button
className="btn btn-tertiary"
disabled={false}
onClick={[Function]}
class="btn btn-tertiary"
type="button"
>
<Memo(LoadingWrapper)
loading={false}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Loading..."
id="admin.requestButton.loading"
/>
}
>
<FormattedMessage
defaultMessage="Button Text"
id="test2"
>
<span>
Button Text
</span>
</FormattedMessage>
</Memo(LoadingWrapper)>
Button Text
</button>
<div>
<div
className="alert alert-warning"
class="alert alert-warning"
>
<Memo(WarningIcon)>
<i
className="fa fa-warning"
title="Warning Icon"
/>
</Memo(WarningIcon)>
<FormattedMessage
defaultMessage="Error Message: {error}"
id="error.message"
values={
Object {
"error": "__message__ - __detailed_error__",
}
}
>
<span>
Error Message: __message__ - __detailed_error__
</span>
</FormattedMessage>
<i
class="fa fa-warning"
title="Warning Icon"
/>
Error Message: __message__ - __detailed_error__
</div>
</div>
</div>
<div
className="help-text"
class="help-text"
>
<FormattedMessage
defaultMessage="Help Text"
id="test1"
>
<span>
Help Text
</span>
</FormattedMessage>
Help Text
</div>
</div>
</div>
</RequestButton>
</div>
`;
exports[`components/admin_console/request_button/request_button.jsx should match snapshot with request error 2`] = `
<RequestButton
buttonText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Button Text"
id="test2"
/>
}
disabled={false}
errorMessage={
Object {
"defaultMessage": "Error Message: {error}",
"id": "error.message",
}
}
helpText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Help Text"
id="test1"
/>
}
includeDetailedError={false}
requestAction={
[MockFunction] {
"calls": Array [
Array [
[Function],
[Function],
],
Array [
[Function],
[Function],
],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
Object {
"type": "return",
"value": undefined,
},
],
}
}
saveNeeded={false}
showSuccessMessage={true}
successMessage={
Object {
"defaultMessage": "Test Successful",
"id": "admin.requestButton.requestSuccess",
}
}
>
<div>
<div
className="form-group"
class="form-group"
>
<div
className="col-sm-offset-4 col-sm-8"
class="col-sm-offset-4 col-sm-8"
>
<div>
<button
className="btn btn-tertiary"
disabled={false}
onClick={[Function]}
class="btn btn-tertiary"
type="button"
>
<Memo(LoadingWrapper)
loading={false}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Loading..."
id="admin.requestButton.loading"
/>
}
>
<FormattedMessage
defaultMessage="Button Text"
id="test2"
>
<span>
Button Text
</span>
</FormattedMessage>
</Memo(LoadingWrapper)>
Button Text
</button>
<div>
<div
className="alert alert-warning"
class="alert alert-warning"
>
<Memo(WarningIcon)>
<i
className="fa fa-warning"
title="Warning Icon"
/>
</Memo(WarningIcon)>
<FormattedMessage
defaultMessage="Error Message: {error}"
id="error.message"
values={
Object {
"error": "__message__",
}
}
>
<span>
Error Message: __message__
</span>
</FormattedMessage>
<i
class="fa fa-warning"
title="Warning Icon"
/>
Error Message: __message__
</div>
</div>
</div>
<div
className="help-text"
class="help-text"
>
<FormattedMessage
defaultMessage="Help Text"
id="test1"
>
<span>
Help Text
</span>
</FormattedMessage>
Help Text
</div>
</div>
</div>
</RequestButton>
</div>
`;
exports[`components/admin_console/request_button/request_button.jsx should match snapshot with successMessage 1`] = `
<RequestButton
buttonText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Button Text"
id="test2"
/>
}
disabled={false}
errorMessage={
Object {
"defaultMessage": "Test Failure: {error}",
"id": "admin.requestButton.requestFailure",
}
}
helpText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Help Text"
id="test1"
/>
}
includeDetailedError={false}
requestAction={
[MockFunction] {
"calls": Array [
Array [
[Function],
[Function],
],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
],
}
}
saveNeeded={false}
showSuccessMessage={true}
successMessage={
Object {
"defaultMessage": "Success Message",
"id": "success.message",
}
}
>
<div>
<div
className="form-group"
class="form-group"
>
<div
className="col-sm-offset-4 col-sm-8"
class="col-sm-offset-4 col-sm-8"
>
<div>
<button
className="btn btn-tertiary"
disabled={false}
onClick={[Function]}
class="btn btn-tertiary"
type="button"
>
<Memo(LoadingWrapper)
loading={false}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Loading..."
id="admin.requestButton.loading"
/>
}
>
<FormattedMessage
defaultMessage="Button Text"
id="test2"
>
<span>
Button Text
</span>
</FormattedMessage>
</Memo(LoadingWrapper)>
Button Text
</button>
<div>
<div
className="alert alert-success"
class="alert alert-success"
>
<SuccessIcon>
<i
className="fa fa-check"
title="Success Icon"
/>
</SuccessIcon>
<FormattedMessage
defaultMessage="Success Message"
id="success.message"
>
<span>
Success Message
</span>
</FormattedMessage>
<i
class="fa fa-check"
title="Success Icon"
/>
Success Message
</div>
</div>
</div>
<div
className="help-text"
class="help-text"
>
<FormattedMessage
defaultMessage="Help Text"
id="test1"
>
<span>
Help Text
</span>
</FormattedMessage>
Help Text
</div>
</div>
</div>
</RequestButton>
</div>
`;
exports[`components/admin_console/request_button/request_button.jsx should match snapshot with successMessage 2`] = `
<RequestButton
buttonText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Button Text"
id="test2"
/>
}
disabled={false}
errorMessage={
Object {
"defaultMessage": "Test Failure: {error}",
"id": "admin.requestButton.requestFailure",
}
}
helpText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Help Text"
id="test1"
/>
}
includeDetailedError={false}
requestAction={
[MockFunction] {
"calls": Array [
Array [
[Function],
[Function],
],
Array [
[Function],
[Function],
],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
Object {
"type": "return",
"value": undefined,
},
],
}
}
saveNeeded={false}
showSuccessMessage={false}
successMessage={
Object {
"defaultMessage": "Success Message",
"id": "success.message",
}
}
>
<div>
<div
className="form-group"
class="form-group"
>
<div
className="col-sm-offset-4 col-sm-8"
class="col-sm-offset-4 col-sm-8"
>
<div>
<button
className="btn btn-tertiary"
disabled={false}
onClick={[Function]}
class="btn btn-tertiary"
type="button"
>
<Memo(LoadingWrapper)
loading={false}
text={
<Memo(MemoizedFormattedMessage)
defaultMessage="Loading..."
id="admin.requestButton.loading"
/>
}
>
<FormattedMessage
defaultMessage="Button Text"
id="test2"
>
<span>
Button Text
</span>
</FormattedMessage>
</Memo(LoadingWrapper)>
Button Text
</button>
</div>
<div
className="help-text"
class="help-text"
>
<FormattedMessage
defaultMessage="Help Text"
id="test1"
>
<span>
Help Text
</span>
</FormattedMessage>
Help Text
</div>
</div>
</div>
</RequestButton>
</div>
`;

View file

@ -1,19 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {FormattedMessage} from 'react-intl';
import RequestButton from 'components/admin_console/request_button/request_button';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
describe('components/admin_console/request_button/request_button.jsx', () => {
test('should match snapshot', () => {
const emptyFunction = jest.fn();
const wrapper = shallow<RequestButton>(
const {container} = renderWithContext(
<RequestButton
requestAction={emptyFunction}
helpText={
@ -30,14 +29,14 @@ describe('components/admin_console/request_button/request_button.jsx', () => {
}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should call saveConfig and request actions when saveNeeded is true', () => {
test('should call saveConfig and request actions when saveNeeded is true', async () => {
const requestActionSuccess = jest.fn((success) => success());
const saveConfigActionSuccess = jest.fn((success) => success());
const wrapper = mountWithIntl(
renderWithContext(
<RequestButton
requestAction={requestActionSuccess}
helpText={
@ -57,17 +56,17 @@ describe('components/admin_console/request_button/request_button.jsx', () => {
/>,
);
wrapper.find('button').first().simulate('click');
await userEvent.click(screen.getByRole('button'));
expect(requestActionSuccess.mock.calls.length).toBe(1);
expect(saveConfigActionSuccess.mock.calls.length).toBe(0);
});
test('should call only request action when saveNeeded is false', () => {
test('should call only request action when saveNeeded is false', async () => {
const requestActionSuccess = jest.fn((success) => success());
const saveConfigActionSuccess = jest.fn((success) => success());
const wrapper = mountWithIntl(
renderWithContext(
<RequestButton
requestAction={requestActionSuccess}
helpText={
@ -87,17 +86,17 @@ describe('components/admin_console/request_button/request_button.jsx', () => {
/>,
);
wrapper.find('button').first().simulate('click');
await userEvent.click(screen.getByRole('button'));
expect(requestActionSuccess.mock.calls.length).toBe(1);
expect(saveConfigActionSuccess.mock.calls.length).toBe(1);
});
test('should match snapshot with successMessage', () => {
test('should match snapshot with successMessage', async () => {
const requestActionSuccess = jest.fn((success) => success());
// Success & showSuccessMessage=true
const wrapper1 = mountWithIntl(
const {container: container1} = renderWithContext(
<RequestButton
requestAction={requestActionSuccess}
helpText={
@ -120,11 +119,11 @@ describe('components/admin_console/request_button/request_button.jsx', () => {
/>,
);
wrapper1.find('button').first().simulate('click');
expect(wrapper1).toMatchSnapshot();
await userEvent.click(screen.getAllByRole('button')[0]);
expect(container1).toMatchSnapshot();
// Success & showSuccessMessage=false
const wrapper2 = mountWithIntl(
const {container: container2} = renderWithContext(
<RequestButton
requestAction={requestActionSuccess}
helpText={
@ -147,19 +146,19 @@ describe('components/admin_console/request_button/request_button.jsx', () => {
/>,
);
wrapper2.find('button').first().simulate('click');
await userEvent.click(screen.getAllByRole('button')[1]);
expect(wrapper2).toMatchSnapshot();
expect(container2).toMatchSnapshot();
});
test('should match snapshot with request error', () => {
test('should match snapshot with request error', async () => {
const requestActionFailure = jest.fn((success, error) => error({
message: '__message__',
detailed_error: '__detailed_error__',
}));
// Error & includeDetailedError=true
const wrapper1 = mountWithIntl(
const {container: container1} = renderWithContext(
<RequestButton
requestAction={requestActionFailure}
helpText={
@ -182,11 +181,11 @@ describe('components/admin_console/request_button/request_button.jsx', () => {
/>,
);
wrapper1.find('button').first().simulate('click');
expect(wrapper1).toMatchSnapshot();
await userEvent.click(screen.getAllByRole('button')[0]);
expect(container1).toMatchSnapshot();
// Error & includeDetailedError=false
const wrapper2 = mountWithIntl(
const {container: container2} = renderWithContext(
<RequestButton
requestAction={requestActionFailure}
helpText={
@ -208,8 +207,8 @@ describe('components/admin_console/request_button/request_button.jsx', () => {
/>,
);
wrapper2.find('button').first().simulate('click');
await userEvent.click(screen.getAllByRole('button')[1]);
expect(wrapper2).toMatchSnapshot();
expect(container2).toMatchSnapshot();
});
});

View file

@ -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 {renderWithContext} from 'tests/react_testing_utils';
import SchemaText from './schema_text';
describe('SchemaText', () => {
@ -14,9 +15,9 @@ describe('SchemaText', () => {
};
test('should render plain text correctly', () => {
const wrapper = shallow(<SchemaText {...baseProps}/>);
const {container} = renderWithContext(<SchemaText {...baseProps}/>);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should render markdown text correctly', () => {
@ -26,9 +27,9 @@ describe('SchemaText', () => {
text: 'This is **HELP TEXT**',
};
const wrapper = shallow(<SchemaText {...props}/>);
const {container} = renderWithContext(<SchemaText {...props}/>);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should render translated text correctly', () => {
@ -41,9 +42,9 @@ describe('SchemaText', () => {
},
};
const wrapper = shallow(<SchemaText {...props}/>);
const {container} = renderWithContext(<SchemaText {...props}/>);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should render translated markdown text correctly', () => {
@ -57,9 +58,9 @@ describe('SchemaText', () => {
},
};
const wrapper = shallow(<SchemaText {...props}/>);
const {container} = renderWithContext(<SchemaText {...props}/>);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should open external markdown links in the new window', () => {
@ -69,11 +70,10 @@ describe('SchemaText', () => {
text: 'This is [a link](https://example.com)',
};
const wrapper = shallow(<SchemaText {...props}/>);
const {container} = renderWithContext(<SchemaText {...props}/>);
expect(wrapper.find('span').prop('dangerouslySetInnerHTML')).toEqual({
__html: 'This is <a href="https://example.com" rel="noopener noreferrer" target="_blank">a link</a>',
});
const span = container.querySelector('span');
expect(span).toHaveProperty('innerHTML', 'This is <a href="https://example.com" rel="noopener noreferrer" target="_blank">a link</a>');
});
test('should open internal markdown links in the same window', () => {
@ -83,11 +83,10 @@ describe('SchemaText', () => {
text: 'This is [a link](http://localhost:8065/api/v4/users/src_id)',
};
const wrapper = shallow(<SchemaText {...props}/>);
const {container} = renderWithContext(<SchemaText {...props}/>);
expect(wrapper.find('span').prop('dangerouslySetInnerHTML')).toEqual({
__html: 'This is <a href="http://localhost:8065/api/v4/users/src_id">a link</a>',
});
const span = container.querySelector('span');
expect(span).toHaveProperty('innerHTML', 'This is <a href="http://localhost:8065/api/v4/users/src_id">a link</a>');
});
test('should support explicit external links like FormattedMarkdownMessage', () => {
@ -97,10 +96,9 @@ describe('SchemaText', () => {
text: 'This is [a link](!https://example.com)',
};
const wrapper = shallow(<SchemaText {...props}/>);
const {container} = renderWithContext(<SchemaText {...props}/>);
expect(wrapper.find('span').prop('dangerouslySetInnerHTML')).toEqual({
__html: 'This is <a href="https://example.com" rel="noopener noreferrer" target="_blank">a link</a>',
});
const span = container.querySelector('span');
expect(span).toHaveProperty('innerHTML', 'This is <a href="https://example.com" rel="noopener noreferrer" target="_blank">a link</a>');
});
});

View file

@ -1,12 +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 {TeamMembership} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
import UserGrid from './user_grid';
@ -60,57 +60,61 @@ describe('components/admin_console/user_grid/UserGrid', () => {
};
test('should match snapshot with 2 users', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<UserGrid
{...baseProps}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with 2 users and 1 added included', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<UserGrid
{...baseProps}
includeUsers={{[notSavedUser.id]: notSavedUser}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with 2 users and 1 removed user', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<UserGrid
{...baseProps}
excludeUsers={{[user1.id]: user1}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should return pagination props while taking into account added or removed users when getPaginationProps is called', () => {
const wrapper = shallow(
// Test initial state: 2 users should show "1 - 2 of 2"
const {rerender} = renderWithContext(
<UserGrid {...baseProps}/>,
);
const userGrid = wrapper.instance() as UserGrid;
let paginationProps = userGrid.getPaginationProps();
expect(paginationProps.startCount).toEqual(1);
expect(paginationProps.endCount).toEqual(2);
expect(paginationProps.total).toEqual(2);
expect(screen.getByText('1 - 2 of 2')).toBeInTheDocument();
wrapper.setProps({includeUsers: {[notSavedUser.id]: notSavedUser}});
// Test with 1 included user: should show "1 - 3 of 3"
rerender(
<UserGrid
{...baseProps}
includeUsers={{[notSavedUser.id]: notSavedUser}}
/>,
);
paginationProps = userGrid.getPaginationProps();
expect(paginationProps.startCount).toEqual(1);
expect(paginationProps.endCount).toEqual(3);
expect(paginationProps.total).toEqual(3);
expect(screen.getByText('1 - 3 of 3')).toBeInTheDocument();
wrapper.setProps({includeUsers: {}, excludeUsers: {[user1.id]: user1}});
// Test with 1 excluded user: should show "1 - 1 of 1"
rerender(
<UserGrid
{...baseProps}
includeUsers={{}}
excludeUsers={{[user1.id]: user1}}
/>,
);
paginationProps = userGrid.getPaginationProps();
expect(paginationProps.startCount).toEqual(1);
expect(paginationProps.endCount).toEqual(1);
expect(paginationProps.total).toEqual(1);
expect(screen.getByText('1 - 1 of 1')).toBeInTheDocument();
});
});

View file

@ -1,51 +1,21 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/workspace-optimization/chips_list should match snapshot 1`] = `
<Fragment>
<Chip
additionalMarkup={
<Memo(MemoizedFormattedMessage)
defaultMessage="Suggestions: {count}"
id="admin.reporting.workspace_optimization.chip_suggestions"
values={
Object {
"count": 3,
}
}
/>
}
className="info"
key="info"
/>
<Chip
additionalMarkup={
<Memo(MemoizedFormattedMessage)
defaultMessage="Warnings: {count}"
id="admin.reporting.workspace_optimization.chip_warnings"
values={
Object {
"count": 2,
}
}
/>
}
className="warning"
key="warning"
/>
<Chip
additionalMarkup={
<Memo(MemoizedFormattedMessage)
defaultMessage="Problems: {count}"
id="admin.reporting.workspace_optimization.chip_problems"
values={
Object {
"count": 1,
}
}
/>
}
className="error"
key="error"
/>
</Fragment>
<div>
<button
class="StyledChip-cgKRRy kTqman info"
>
Suggestions: 3
</button>
<button
class="StyledChip-cgKRRy kTqman warning"
>
Warnings: 2
</button>
<button
class="StyledChip-cgKRRy kTqman error"
>
Problems: 1
</button>
</div>
`;

View file

@ -1,20 +1,20 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/workspace-optimization/cta_buttons should match snapshot 1`] = `
<div
className="ctaButtons"
>
<button
className="btn btn-primary btn-sm actionButton annnouncementBar__purchaseNow"
onClick={[Function]}
<div>
<div
class="ctaButtons"
>
Action Text
</button>
<button
className="btn btn-tertiary btn-sm learnMoreButton"
onClick={[Function]}
>
Learn More
</button>
<button
class="btn btn-primary btn-sm actionButton annnouncementBar__purchaseNow"
>
Action Text
</button>
<button
class="btn btn-tertiary btn-sm learnMoreButton"
>
Learn More
</button>
</div>
</div>
`;

View file

@ -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 ChipsList from 'components/admin_console/workspace-optimization/chips_list';
import type {ChipsInfoType} from 'components/admin_console/workspace-optimization/chips_list';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import {ItemStatus} from './dashboard.type';
describe('components/admin_console/workspace-optimization/chips_list', () => {
@ -22,13 +23,13 @@ describe('components/admin_console/workspace-optimization/chips_list', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(<ChipsList {...baseProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<ChipsList {...baseProps}/>);
expect(container).toMatchSnapshot();
});
test('test chips list lenght is 3 as defined in baseProps', () => {
const wrapper = shallow(<ChipsList {...baseProps}/>);
const chips = wrapper.find('Chip');
renderWithContext(<ChipsList {...baseProps}/>);
const chips = screen.getAllByRole('button');
expect(chips.length).toBe(3);
});
@ -38,8 +39,8 @@ describe('components/admin_console/workspace-optimization/chips_list', () => {
chipsData: {...overallScoreChips, [ItemStatus.ERROR]: 0},
hideCountZeroChips: true,
};
const wrapper = shallow(<ChipsList {...zeroErrorProps}/>);
const chips = wrapper.find('Chip');
renderWithContext(<ChipsList {...zeroErrorProps}/>);
const chips = screen.getAllByRole('button');
expect(chips.length).toBe(2);
});
@ -49,8 +50,8 @@ describe('components/admin_console/workspace-optimization/chips_list', () => {
chipsData: {...overallScoreChips, [ItemStatus.ERROR]: 0},
hideCountZeroChips: false,
};
const wrapper = shallow(<ChipsList {...zeroErrorProps}/>);
const chips = wrapper.find('Chip');
renderWithContext(<ChipsList {...zeroErrorProps}/>);
const chips = screen.getAllByRole('button');
expect(chips.length).toBe(3);
});

View file

@ -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 CtaButtons from 'components/admin_console/workspace-optimization/cta_buttons';
import {render, screen} from 'tests/react_testing_utils';
describe('components/admin_console/workspace-optimization/cta_buttons', () => {
const baseProps = {
learnMoreLink: '/learn_more',
@ -15,13 +16,13 @@ describe('components/admin_console/workspace-optimization/cta_buttons', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(<CtaButtons {...baseProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = render(<CtaButtons {...baseProps}/>);
expect(container).toMatchSnapshot();
});
test('test ctaButtons list lenght is 3 as defined in baseProps', () => {
const wrapper = shallow(<CtaButtons {...baseProps}/>);
const ctaButtons = wrapper.find('button');
test('test ctaButtons list lenght is 2 as defined in baseProps', () => {
render(<CtaButtons {...baseProps}/>);
const ctaButtons = screen.getAllByRole('button');
expect(ctaButtons.length).toBe(2);
});

View file

@ -1,17 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import AutosizeTextarea from 'components/autosize_textarea';
import {render} from 'tests/react_testing_utils';
describe('components/AutosizeTextarea', () => {
test('should match snapshot, init', () => {
const wrapper = shallow(
const {container} = render(
<AutosizeTextarea/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});

View file

@ -1,33 +1,36 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/backstage/components/BackstageHeader should match snapshot with children 1`] = `
<div
className="backstage-header"
>
<h1>
<div>
Child 1
</div>
<span
className="backstage-header__divider"
key="divider1"
>
<i
className="fa fa-angle-right"
title="Breadcrumb Icon"
/>
</span>
<div>
Child 2
</div>
</h1>
<div>
<div
class="backstage-header"
>
<h1>
<div>
Child 1
</div>
<span
class="backstage-header__divider"
>
<i
class="fa fa-angle-right"
title="Breadcrumb Icon"
/>
</span>
<div>
Child 2
</div>
</h1>
</div>
</div>
`;
exports[`components/backstage/components/BackstageHeader should match snapshot without children 1`] = `
<div
className="backstage-header"
>
<h1 />
<div>
<div
class="backstage-header"
>
<h1 />
</div>
</div>
`;

View file

@ -1,26 +1,27 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import BackstageHeader from 'components/backstage/components/backstage_header';
import {render} from 'tests/react_testing_utils';
describe('components/backstage/components/BackstageHeader', () => {
test('should match snapshot without children', () => {
const wrapper = shallow(
const {container} = render(
<BackstageHeader/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with children', () => {
const wrapper = shallow(
const {container} = render(
<BackstageHeader>
<div>{'Child 1'}</div>
<div>{'Child 2'}</div>
</BackstageHeader>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});

View file

@ -1,15 +1,19 @@
// 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 {MemoryRouter} from 'react-router-dom';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
import BackstageCategory from './backstage_category';
import BackstageSidebar from './backstage_sidebar';
// Mock permission gates to render their children
jest.mock('components/permissions_gates/team_permission_gate', () => ({children}: {children: React.ReactNode}) => <>{children}</>);
jest.mock('components/permissions_gates/system_permission_gate', () => ({children}: {children: React.ReactNode}) => <>{children}</>);
describe('components/backstage/components/BackstageSidebar', () => {
const defaultProps: ComponentProps<typeof BackstageSidebar> = {
team: TestHelper.getTeamMock({
@ -26,6 +30,15 @@ describe('components/backstage/components/BackstageSidebar', () => {
enableOutgoingOAuthConnections: false,
};
// Helper to render with router at integrations path to show children
const renderAtIntegrationsPath = (props: ComponentProps<typeof BackstageSidebar>) => {
return renderWithContext(
<MemoryRouter initialEntries={['/team_name/integrations']}>
<BackstageSidebar {...props}/>
</MemoryRouter>,
);
};
describe('custom emoji', () => {
const testCases = [
{enableCustomEmoji: false, canCreateOrDeleteCustomEmoji: false, expectedResult: false},
@ -41,11 +54,13 @@ describe('components/backstage/components/BackstageSidebar', () => {
enableCustomEmoji: testCase.enableCustomEmoji,
canCreateOrDeleteCustomEmoji: testCase.canCreateOrDeleteCustomEmoji,
};
const wrapper = shallow(
<BackstageSidebar {...props}/>,
);
renderAtIntegrationsPath(props);
expect(wrapper.find(BackstageCategory).find({name: 'emoji'}).exists()).toBe(testCase.expectedResult);
if (testCase.expectedResult) {
expect(screen.getByText('Custom Emoji')).toBeInTheDocument();
} else {
expect(screen.queryByText('Custom Emoji')).not.toBeInTheDocument();
}
});
});
});
@ -65,11 +80,13 @@ describe('components/backstage/components/BackstageSidebar', () => {
enableIncomingWebhooks: testCase.enableIncomingWebhooks,
canManageIntegrations: testCase.canManageIntegrations,
};
const wrapper = shallow(
<BackstageSidebar {...props}/>,
);
renderAtIntegrationsPath(props);
expect(wrapper.find(BackstageCategory).find({name: 'incoming_webhooks'}).exists()).toBe(testCase.expectedResult);
if (testCase.expectedResult) {
expect(screen.getByText('Incoming Webhooks')).toBeInTheDocument();
} else {
expect(screen.queryByText('Incoming Webhooks')).not.toBeInTheDocument();
}
});
});
});
@ -89,11 +106,13 @@ describe('components/backstage/components/BackstageSidebar', () => {
enableOutgoingWebhooks: testCase.enableOutgoingWebhooks,
canManageIntegrations: testCase.canManageIntegrations,
};
const wrapper = shallow(
<BackstageSidebar {...props}/>,
);
renderAtIntegrationsPath(props);
expect(wrapper.find(BackstageCategory).find({name: 'outgoing_webhooks'}).exists()).toBe(testCase.expectedResult);
if (testCase.expectedResult) {
expect(screen.getByText('Outgoing Webhooks')).toBeInTheDocument();
} else {
expect(screen.queryByText('Outgoing Webhooks')).not.toBeInTheDocument();
}
});
});
});
@ -113,11 +132,13 @@ describe('components/backstage/components/BackstageSidebar', () => {
enableCommands: testCase.enableCommands,
canManageIntegrations: testCase.canManageIntegrations,
};
const wrapper = shallow(
<BackstageSidebar {...props}/>,
);
renderAtIntegrationsPath(props);
expect(wrapper.find(BackstageCategory).find({name: 'commands'}).exists()).toBe(testCase.expectedResult);
if (testCase.expectedResult) {
expect(screen.getByText('Slash Commands')).toBeInTheDocument();
} else {
expect(screen.queryByText('Slash Commands')).not.toBeInTheDocument();
}
});
});
});
@ -137,11 +158,13 @@ describe('components/backstage/components/BackstageSidebar', () => {
enableOAuthServiceProvider: testCase.enableOAuthServiceProvider,
canManageIntegrations: testCase.canManageIntegrations,
};
const wrapper = shallow(
<BackstageSidebar {...props}/>,
);
renderAtIntegrationsPath(props);
expect(wrapper.find(BackstageCategory).find({name: 'oauth2-apps'}).exists()).toBe(testCase.expectedResult);
if (testCase.expectedResult) {
expect(screen.getByText('OAuth 2.0 Applications')).toBeInTheDocument();
} else {
expect(screen.queryByText('OAuth 2.0 Applications')).not.toBeInTheDocument();
}
});
});
});
@ -161,11 +184,13 @@ describe('components/backstage/components/BackstageSidebar', () => {
enableOutgoingOAuthConnections: testCase.enableOutgoingOAuthConnections,
canManageIntegrations: testCase.canManageIntegrations,
};
const wrapper = shallow(
<BackstageSidebar {...props}/>,
);
renderAtIntegrationsPath(props);
expect(wrapper.find(BackstageCategory).find({name: 'outgoing-oauth2-connections'}).exists()).toBe(testCase.expectedResult);
if (testCase.expectedResult) {
expect(screen.getByText('Outgoing OAuth 2.0 Connections')).toBeInTheDocument();
} else {
expect(screen.queryByText('Outgoing OAuth 2.0 Connections')).not.toBeInTheDocument();
}
});
});
});
@ -182,11 +207,13 @@ describe('components/backstage/components/BackstageSidebar', () => {
...defaultProps,
canManageIntegrations: testCase.canManageIntegrations,
};
const wrapper = shallow(
<BackstageSidebar {...props}/>,
);
renderAtIntegrationsPath(props);
expect(wrapper.find(BackstageCategory).find({name: 'bots'}).exists()).toBe(testCase.expectedResult);
if (testCase.expectedResult) {
expect(screen.getByText('Bot Accounts')).toBeInTheDocument();
} else {
expect(screen.queryByText('Bot Accounts')).not.toBeInTheDocument();
}
});
});
});
@ -202,16 +229,14 @@ describe('components/backstage/components/BackstageSidebar', () => {
canManageIntegrations: true,
enableOutgoingOAuthConnections: true,
};
const wrapper = shallow(
<BackstageSidebar {...props}/>,
);
renderAtIntegrationsPath(props);
expect(wrapper.find(BackstageCategory).find({name: 'incoming_webhooks'}).exists()).toBe(true);
expect(wrapper.find(BackstageCategory).find({name: 'outgoing_webhooks'}).exists()).toBe(true);
expect(wrapper.find(BackstageCategory).find({name: 'commands'}).exists()).toBe(true);
expect(wrapper.find(BackstageCategory).find({name: 'oauth2-apps'}).exists()).toBe(true);
expect(wrapper.find(BackstageCategory).find({name: 'outgoing-oauth2-connections'}).exists()).toBe(true);
expect(wrapper.find(BackstageCategory).find({name: 'bots'}).exists()).toBe(true);
expect(screen.getByText('Incoming Webhooks')).toBeInTheDocument();
expect(screen.getByText('Outgoing Webhooks')).toBeInTheDocument();
expect(screen.getByText('Slash Commands')).toBeInTheDocument();
expect(screen.getByText('OAuth 2.0 Applications')).toBeInTheDocument();
expect(screen.getByText('Outgoing OAuth 2.0 Connections')).toBeInTheDocument();
expect(screen.getByText('Bot Accounts')).toBeInTheDocument();
});
it('cannot manage integrations', () => {
@ -224,16 +249,14 @@ describe('components/backstage/components/BackstageSidebar', () => {
enableOutgoingOAuthConnections: true,
canManageIntegrations: false,
};
const wrapper = shallow(
<BackstageSidebar {...props}/>,
);
renderAtIntegrationsPath(props);
expect(wrapper.find(BackstageCategory).find({name: 'incoming_webhooks'}).exists()).toBe(false);
expect(wrapper.find(BackstageCategory).find({name: 'outgoing_webhooks'}).exists()).toBe(false);
expect(wrapper.find(BackstageCategory).find({name: 'commands'}).exists()).toBe(false);
expect(wrapper.find(BackstageCategory).find({name: 'oauth2-apps'}).exists()).toBe(false);
expect(wrapper.find(BackstageCategory).find({name: 'outgoing-oauth2-connections'}).exists()).toBe(false);
expect(wrapper.find(BackstageCategory).find({name: 'bots'}).exists()).toBe(false);
expect(screen.queryByText('Incoming Webhooks')).not.toBeInTheDocument();
expect(screen.queryByText('Outgoing Webhooks')).not.toBeInTheDocument();
expect(screen.queryByText('Slash Commands')).not.toBeInTheDocument();
expect(screen.queryByText('OAuth 2.0 Applications')).not.toBeInTheDocument();
expect(screen.queryByText('Outgoing OAuth 2.0 Connections')).not.toBeInTheDocument();
expect(screen.queryByText('Bot Accounts')).not.toBeInTheDocument();
});
});
});

View file

@ -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, screen, fireEvent, userEvent} from 'tests/react_testing_utils';
import ColorInput from './color_input';
describe('components/ColorInput', () => {
@ -14,79 +15,102 @@ describe('components/ColorInput', () => {
};
test('should match snapshot, init', () => {
const wrapper = shallow(
const {container} = render(
<ColorInput {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, opened', () => {
const wrapper = shallow(
test('should match snapshot, opened', async () => {
const {container} = render(
<ColorInput {...baseProps}/>,
);
wrapper.find('.input-group-addon').simulate('click');
await userEvent.click(container.querySelector('.input-group-addon')!);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, toggle picker', () => {
const wrapper = shallow(
test('should match snapshot, toggle picker', async () => {
const {container} = render(
<ColorInput {...baseProps}/>,
);
wrapper.find('.input-group-addon').simulate('click');
wrapper.find('.input-group-addon').simulate('click');
await userEvent.click(container.querySelector('.input-group-addon')!);
await userEvent.click(container.querySelector('.input-group-addon')!);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, click on picker', () => {
const wrapper = shallow(
test('should match snapshot, click on picker', async () => {
const {container} = render(
<ColorInput {...baseProps}/>,
);
wrapper.find('.input-group-addon').simulate('click');
wrapper.find('.color-popover').simulate('click');
await userEvent.click(container.querySelector('.input-group-addon')!);
await userEvent.click(container.querySelector('.color-popover')!);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should have match state on togglePicker', () => {
const wrapper = shallow(
test('should have match state on togglePicker', async () => {
const {container} = render(
<ColorInput {...baseProps}/>,
);
wrapper.setState({isOpened: true});
// Initially picker should be closed (no color-popover)
expect(container.querySelector('.color-popover')).not.toBeInTheDocument();
wrapper.find('.input-group-addon').simulate('click');
expect(wrapper.state('isOpened')).toEqual(false);
// Click to open
await userEvent.click(container.querySelector('.input-group-addon')!);
expect(container.querySelector('.color-popover')).toBeInTheDocument();
wrapper.find('.input-group-addon').simulate('click');
expect(wrapper.state('isOpened')).toEqual(true);
// Click to close
await userEvent.click(container.querySelector('.input-group-addon')!);
expect(container.querySelector('.color-popover')).not.toBeInTheDocument();
// Click to open again
await userEvent.click(container.querySelector('.input-group-addon')!);
expect(container.querySelector('.color-popover')).toBeInTheDocument();
});
test('should keep what the user types in the textbox until blur', () => {
const wrapper = shallow(
<ColorInput {...baseProps}/>,
let currentValue = '#ffffff';
const onChange = jest.fn((value: string) => {
currentValue = value;
});
const {container, rerender} = render(
<ColorInput
{...baseProps}
value={currentValue}
onChange={onChange}
/>,
);
baseProps.onChange.mockImplementation((value) => wrapper.setProps({value}));
const input = screen.getByRole('textbox');
const colorIcon = container.querySelector('.color-icon') as HTMLElement;
wrapper.find('input').simulate('focus', {target: null});
expect(wrapper.state('focused')).toBe(true);
fireEvent.focus(input);
wrapper.find('input').simulate('change', {target: {value: '#abc'}});
expect(wrapper.state('value')).toBe('#abc');
expect(baseProps.onChange).toHaveBeenLastCalledWith('#aabbcc');
expect(wrapper.find('input').prop('value')).toEqual('#abc');
expect(wrapper.find('.color-icon').prop('style')).toHaveProperty('backgroundColor', '#abc');
fireEvent.change(input, {target: {value: '#abc'}});
expect(onChange).toHaveBeenLastCalledWith('#aabbcc');
expect(input).toHaveValue('#abc');
expect(colorIcon.style.backgroundColor).toBe('rgb(170, 187, 204)');
wrapper.find('input').simulate('blur');
expect(wrapper.state('focused')).toBe(false);
expect(wrapper.state('value')).toBe('#aabbcc');
expect(baseProps.onChange).toHaveBeenLastCalledWith('#aabbcc');
expect(wrapper.find('input').prop('value')).toEqual('#aabbcc');
expect(wrapper.find('.color-icon').prop('style')).toHaveProperty('backgroundColor', '#aabbcc');
// Rerender with updated value prop (simulating parent component update)
rerender(
<ColorInput
{...baseProps}
value={currentValue}
onChange={onChange}
/>,
);
fireEvent.blur(input);
// After blur, the input should show the normalized value
expect(input).toHaveValue('#aabbcc');
expect(colorIcon.style.backgroundColor).toBe('rgb(170, 187, 204)');
});
});

View file

@ -1,72 +1,43 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`/components/common/InfiniteScroll should match snapshot 1`] = `
<InfiniteScroll
bufferValue={100}
callBack={[MockFunction]}
endOfData={false}
endOfDataMessage="No more items to fetch"
itemsPerPage={10}
loaderStyle={Object {}}
pageNumber={1}
styleClass="signup-team-all"
totalItems={20}
>
<div>
<div
className="infinite-scroll signup-team-all"
class="infinite-scroll signup-team-all"
>
<div />
No more items to fetch
</div>
</InfiniteScroll>
</div>
`;
exports[`/components/common/InfiniteScroll should not show loading screen if there is no data 1`] = `
<InfiniteScroll
bufferValue={100}
callBack={[MockFunction]}
endOfData={false}
endOfDataMessage="No more items to fetch"
itemsPerPage={10}
loaderStyle={Object {}}
pageNumber={1}
styleClass="signup-team-all"
totalItems={20}
>
<div>
<div
className="infinite-scroll signup-team-all"
class="infinite-scroll signup-team-all"
>
<div />
<LoadingScreen
message=" "
style={Object {}}
<div
class="loading-screen"
style="position: relative;"
>
<div
className="loading-screen"
style={
Object {
"position": "relative",
}
}
class="loading__content"
>
<p>
</p>
<div
className="loading__content"
>
<p>
</p>
<div
className="round round-1"
/>
<div
className="round round-2"
/>
<div
className="round round-3"
/>
</div>
class="round round-1"
/>
<div
class="round round-2"
/>
<div
class="round round-3"
/>
</div>
</LoadingScreen>
</div>
</div>
</InfiniteScroll>
</div>
`;

View file

@ -1,63 +1,54 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`/components/common/RadioButtonGroup should match snapshot 1`] = `
<div
className="radio-list"
data-testid=""
>
<div>
<div
className="radio"
key="value1"
class="radio-list"
data-testid=""
>
<label
className=""
<div
class="radio"
>
<input
checked={false}
disabled={false}
name="test-string"
onChange={[Function]}
type="radio"
value="value1"
/>
key1
</label>
</div>
<div
className="radio"
key="value2"
>
<label
className="selected"
<label
class=""
>
<input
name="test-string"
type="radio"
value="value1"
/>
key1
</label>
</div>
<div
class="radio"
>
<input
checked={true}
disabled={false}
name="test-string"
onChange={[Function]}
type="radio"
value="value2"
/>
key2
</label>
</div>
<div
className="radio"
key="value3"
>
<label
className=""
<label
class="selected"
>
<input
checked=""
name="test-string"
type="radio"
value="value2"
/>
key2
</label>
</div>
<div
class="radio"
>
<input
checked={false}
disabled={false}
name="test-string"
onChange={[Function]}
type="radio"
value="value3"
/>
key3
</label>
<label
class=""
>
<input
name="test-string"
type="radio"
value="value3"
/>
key3
</label>
</div>
</div>
</div>
`;

View file

@ -1,36 +1,33 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`/components/common/SiteNameAndDescription should match snapshot, default 1`] = `
<Fragment>
<div>
<h1
id="site_name"
>
Mattermost
</h1>
<h3
className="color--light"
class="color--light"
id="site_description"
>
<MemoizedFormattedMessage
defaultMessage="All team communication in one place, searchable and accessible anywhere"
id="web.root.signup_info"
/>
All team communication in one place, searchable and accessible anywhere
</h3>
</Fragment>
</div>
`;
exports[`/components/common/SiteNameAndDescription should match snapshot, with custom site name and description 1`] = `
<Fragment>
<div>
<h1
id="site_name"
>
other_site
</h1>
<h3
className="color--light"
class="color--light"
id="site_description"
>
custom_description_text
</h3>
</Fragment>
</div>
`;

View file

@ -1,51 +1,103 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`/components/common/Accordion should match snapshot 1`] = `
<ul
className="Accordion"
>
<AccordionCard
data={
Object {
"description": "First accordion Item Description",
"items": Array [
<div>
<ul
class="Accordion"
>
<li
class="accordion-card"
>
<div
class="accordion-card-header"
role="button"
>
<div
class="accordion-card-header__body"
>
<div
class="accordion-card-header__body__title"
>
First Accordion Item
</div>
<div
class="accordion-card-header__body__description"
>
First accordion Item Description
</div>
</div>
<div
class="accordion-card-header__chevron"
>
<i
class="icon-chevron-down"
/>
</div>
</div>
<div
class="accordion-card-container"
style="height: 0px;"
>
<div
class="accordion-card-container__content"
>
<p
className="accordion-item-content-el"
class="accordion-item-content-el"
>
First List Item
</p>,
</p>
<p
className="accordion-item-content-el"
class="accordion-item-content-el"
>
Second List Item
</p>,
],
"title": "First Accordion Item",
}
}
isExpanded={false}
key="0"
onButtonClick={[Function]}
onHeaderClick={[MockFunction]}
/>
<AccordionCard
data={
Object {
"description": "Second accordion Item Description",
"items": Array [
</p>
</div>
</div>
</li>
<li
class="accordion-card"
>
<div
class="accordion-card-header"
role="button"
>
<div
class="accordion-card-header__body"
>
<div
class="accordion-card-header__body__title"
>
Second Accordion Item
</div>
<div
class="accordion-card-header__body__description"
>
Second accordion Item Description
</div>
</div>
<div
class="accordion-card-header__chevron"
>
<i
class="icon-chevron-down"
/>
</div>
</div>
<div
class="accordion-card-container"
style="height: 0px;"
>
<div
class="accordion-card-container__content"
>
<p
className="accordion-item-content-el"
class="accordion-item-content-el"
>
Third List Item
</p>,
],
"title": "Second Accordion Item",
}
}
isExpanded={false}
key="1"
onButtonClick={[Function]}
onHeaderClick={[MockFunction]}
/>
</ul>
</p>
</div>
</div>
</li>
</ul>
</div>
`;

View file

@ -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 Accordion from 'components/common/accordion/accordion';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import {renderWithContext, userEvent} from 'tests/react_testing_utils';
describe('/components/common/Accordion', () => {
const texts = ['First List Item', 'Second List Item', 'Third List Item'];
@ -54,87 +53,73 @@ describe('/components/common/Accordion', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(<Accordion {...baseProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<Accordion {...baseProps}/>);
expect(container).toMatchSnapshot();
});
test('test accordion items length is 2 as specified in items property in baseProps', () => {
const wrapper = shallow(<Accordion {...baseProps}/>);
const accordionItems = wrapper.find('AccordionCard');
const {container} = renderWithContext(<Accordion {...baseProps}/>);
const accordionItems = container.querySelectorAll('.accordion-card');
expect(accordionItems.length).toBe(2);
});
test('test accordion opens first accordion item when clicked', () => {
const wrapper = mountWithIntl(<Accordion {...baseProps}/>);
const firstAccordionCard = wrapper.find('ul AccordionCard:first-child');
const header = firstAccordionCard.find('div.accordion-card-header');
header.simulate('click');
test('test accordion opens first accordion item when clicked', async () => {
const {container} = renderWithContext(<Accordion {...baseProps}/>);
const firstAccordionCard = container.querySelector('ul li.accordion-card');
const header = firstAccordionCard!.querySelector('.accordion-card-header') as HTMLElement;
await userEvent.click(header);
const firstChildItem = firstAccordionCard.find('div.accordion-card-container p.accordion-item-content-el:first-child');
const slide1Text = firstChildItem.text();
const firstChildItem = container.querySelector('.accordion-card.active .accordion-card-container .accordion-item-content-el');
const slide1Text = firstChildItem!.textContent;
expect(slide1Text).toEqual('First List Item');
});
test('test accordion opens ONLY one accordion item at a time if NO openMultiple prop is set or set to FALSE', () => {
const wrapper = mountWithIntl(<Accordion {...baseProps}/>);
const firstAccordionCard = wrapper.find('ul AccordionCard:first-child');
const secondAccordionCard = wrapper.find('ul AccordionCard:last-child');
test('test accordion opens ONLY one accordion item at a time if NO openMultiple prop is set or set to FALSE', async () => {
const {container} = renderWithContext(<Accordion {...baseProps}/>);
const accordionCards = container.querySelectorAll('ul li.accordion-card');
const firstAccordionCard = accordionCards[0];
const secondAccordionCard = accordionCards[1];
const header1 = firstAccordionCard.find('div.accordion-card-header');
const header2 = secondAccordionCard.find('div.accordion-card-header');
const header1 = firstAccordionCard.querySelector('.accordion-card-header') as HTMLElement;
const header2 = secondAccordionCard.querySelector('.accordion-card-header') as HTMLElement;
header1.simulate('click');
// refind the element after making changes so those gets reflected
const firstAccordionCardAfterEvent = wrapper.find('ul AccordionCard:first-child');
const secondAccordionCardAfterEvent = wrapper.find('ul AccordionCard:last-child');
await userEvent.click(header1);
// clicking first list element should only apply the active class to the first one and not to the last
expect(firstAccordionCardAfterEvent.find('li.accordion-card').hasClass('active')).toEqual(true);
expect(secondAccordionCardAfterEvent.find('li.accordion-card').hasClass('active')).toEqual(false);
expect(firstAccordionCard.classList.contains('active')).toEqual(true);
expect(secondAccordionCard.classList.contains('active')).toEqual(false);
header2.simulate('click');
// refind the element after making changes so those gets reflected
const firstAccordionCardAfterEvent1 = wrapper.find('ul AccordionCard:first-child');
const secondAccordionCardAfterEvent1 = wrapper.find('ul AccordionCard:last-child');
await userEvent.click(header2);
// clicking last list element should only apply the active class to the last one and not to the first
expect(firstAccordionCardAfterEvent1.find('li.accordion-card').hasClass('active')).toEqual(false);
expect(secondAccordionCardAfterEvent1.find('li.accordion-card').hasClass('active')).toEqual(true);
expect(firstAccordionCard.classList.contains('active')).toEqual(false);
expect(secondAccordionCard.classList.contains('active')).toEqual(true);
});
test('test accordion opens MORE THAN one accordion item at a time if openMultiple prop IS set to TRUE', () => {
const wrapper = mountWithIntl(
test('test accordion opens MORE THAN one accordion item at a time if openMultiple prop IS set to TRUE', async () => {
const {container} = renderWithContext(
<Accordion
{...baseProps}
expandMultiple={true}
/>);
const firstAccordionCard = wrapper.find('ul AccordionCard:first-child');
const secondAccordionCard = wrapper.find('ul AccordionCard:last-child');
const accordionCards = container.querySelectorAll('ul li.accordion-card');
const firstAccordionCard = accordionCards[0];
const secondAccordionCard = accordionCards[1];
const header1 = firstAccordionCard.find('div.accordion-card-header');
const header2 = secondAccordionCard.find('div.accordion-card-header');
const header1 = firstAccordionCard.querySelector('.accordion-card-header') as HTMLElement;
const header2 = secondAccordionCard.querySelector('.accordion-card-header') as HTMLElement;
header1.simulate('click');
// refind the element after making changes so those gets reflected
const firstAccordionCardAfterEvent = wrapper.find('ul AccordionCard:first-child');
const secondAccordionCardAfterEvent = wrapper.find('ul AccordionCard:last-child');
await userEvent.click(header1);
// clicking first list element should only apply the active class to the first one and not to the last
expect(firstAccordionCardAfterEvent.find('li.accordion-card').hasClass('active')).toEqual(true);
expect(secondAccordionCardAfterEvent.find('li.accordion-card').hasClass('active')).toEqual(false);
expect(firstAccordionCard.classList.contains('active')).toEqual(true);
expect(secondAccordionCard.classList.contains('active')).toEqual(false);
header2.simulate('click');
// refind the element after making changes so those gets reflected
const firstAccordionCardAfterEvent1 = wrapper.find('ul AccordionCard:first-child');
const secondAccordionCardAfterEvent1 = wrapper.find('ul AccordionCard:last-child');
await userEvent.click(header2);
// clicking last list element should apply active class to both
expect(firstAccordionCardAfterEvent1.find('li.accordion-card').hasClass('active')).toEqual(true);
expect(secondAccordionCardAfterEvent1.find('li.accordion-card').hasClass('active')).toEqual(true);
expect(firstAccordionCard.classList.contains('active')).toEqual(true);
expect(secondAccordionCard.classList.contains('active')).toEqual(true);
});
});

View file

@ -1,77 +1,74 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`/components/common/Carousel should match snapshot 1`] = `
<div
className="container-slider"
id="test-string"
>
<div>
<div
className="slide active-anim"
key="0"
>
<p
className="slide"
key="1"
>
First Slide
</p>
</div>
<div
className="slide"
key="1"
>
<p
className="slide"
key="2"
>
Second Slide
</p>
</div>
<div
className="slide"
key="2"
>
<p
className="slide"
key="3"
>
Third Slide
</p>
</div>
<div
className="container-footer"
class="container-slider"
id="test-string"
>
<div
className="container-dots"
class="slide active-anim"
>
<div
className="dot active"
key="0"
onClick={[Function]}
/>
<div
className="dot"
key="1"
onClick={[Function]}
/>
<div
className="dot"
key="2"
onClick={[Function]}
/>
<p
class="slide"
>
First Slide
</p>
</div>
<div
className=" buttons container-buttons"
class="slide"
>
<CarouselButton
direction="prev"
disabled={false}
moveSlide={[Function]}
/>
<CarouselButton
direction="next"
moveSlide={[Function]}
/>
<p
class="slide"
>
Second Slide
</p>
</div>
<div
class="slide"
>
<p
class="slide"
>
Third Slide
</p>
</div>
<div
class="container-footer"
>
<div
class="container-dots"
>
<div
class="dot active"
/>
<div
class="dot"
/>
<div
class="dot"
/>
</div>
<div
class=" buttons container-buttons"
>
<a
class=" prev previous-btn"
>
<i
class="icon-arrow-back-ios"
/>
Previous
</a>
<a
class=" next next-btn"
>
Next
<i
class="icon-arrow-forward-ios"
/>
</a>
</div>
</div>
</div>
</div>

View file

@ -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 Carousel from 'components/common/carousel/carousel';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import {renderWithContext, userEvent, waitFor} from 'tests/react_testing_utils';
import {BtnStyle} from './carousel_button';
@ -43,70 +42,67 @@ describe('/components/common/Carousel', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(<Carousel {...baseProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<Carousel {...baseProps}/>);
expect(container).toMatchSnapshot();
});
test('test carouse slides lenght is as expected', () => {
const wrapper = shallow(<Carousel {...baseProps}/>);
const slides = wrapper.find('p.slide');
const {container} = renderWithContext(<Carousel {...baseProps}/>);
const slides = container.querySelectorAll('p.slide');
expect(slides.length).toBe(3);
});
test('test carousel shows next and previous button', () => {
const wrapper = mountWithIntl(<Carousel {...baseProps}/>);
const buttonNext = wrapper.find('CarouselButton').find('a.next');
const buttonPrev = wrapper.find('CarouselButton').find('a.prev');
const {container} = renderWithContext(<Carousel {...baseProps}/>);
const buttonNext = container.querySelector('a.next');
const buttonPrev = container.querySelector('a.prev');
expect(buttonNext).toHaveLength(1);
expect(buttonPrev).toHaveLength(1);
expect(buttonNext).toBeInTheDocument();
expect(buttonPrev).toBeInTheDocument();
});
test('test carousel shows next and previous chevrons when this option is sent', () => {
const wrapper = mountWithIntl(
const {container} = renderWithContext(
<Carousel
{...baseProps}
btnsStyle={BtnStyle.CHEVRON}
/>,
);
const nextButton = wrapper.find(Carousel).find('CarouselButton div.chevron-right');
const prevButton = wrapper.find(Carousel).find('CarouselButton div.chevron-left');
const nextButton = container.querySelector('.chevron-right');
const prevButton = container.querySelector('.chevron-left');
expect(nextButton).toHaveLength(1);
expect(prevButton).toHaveLength(1);
expect(nextButton).toBeInTheDocument();
expect(prevButton).toBeInTheDocument();
});
test('test carousel shows first slide as active', () => {
const wrapper = shallow(<Carousel {...baseProps}/>);
const activeSlide = wrapper.find('div.active-anim');
const {container} = renderWithContext(<Carousel {...baseProps}/>);
const activeSlide = container.querySelector('div.active-anim');
const slideText = activeSlide.find('p.slide').text();
const slideText = activeSlide!.querySelector('p.slide')!.textContent;
expect(slideText).toEqual('First Slide');
});
test('test carousel moves slides when clicking buttons', (done) => {
const wrapper = mountWithIntl(<Carousel {...baseProps}/>);
const activeSlide = wrapper.find('div.active-anim');
test('test carousel moves slides when clicking buttons', async () => {
const {container} = renderWithContext(<Carousel {...baseProps}/>);
const activeSlide = container.querySelector('div.active-anim');
const slide1Text = activeSlide.find('p.slide').text();
const slide1Text = activeSlide!.querySelector('p.slide')!.textContent;
expect(slide1Text).toEqual('First Slide');
const buttonNext = wrapper.find('CarouselButton').find('a.next');
const buttonNext = container.querySelector('a.next') as HTMLElement;
buttonNext.simulate('click');
await userEvent.click(buttonNext);
jest.useFakeTimers();
setTimeout(() => {
const activeSlide = wrapper.find('div.active-anim');
const slide1Text = activeSlide.find('p.slide').text();
expect(slide1Text).toEqual('Second Slide');
done();
}, 1000);
jest.runAllTimers();
await waitFor(() => {
const activeSlideAfterClick = container.querySelector('div.active-anim');
const slideText = activeSlideAfterClick!.querySelector('p.slide')!.textContent;
expect(slideText).toEqual('Second Slide');
});
});
test('test carousel executes custom next and prev btn callback functions', () => {
test('test carousel executes custom next and prev btn callback functions', async () => {
const onPrevSlideClick = jest.fn();
const onNextSlideClick = jest.fn();
const props = {
@ -114,12 +110,12 @@ describe('/components/common/Carousel', () => {
onPrevSlideClick,
onNextSlideClick};
const wrapper = mountWithIntl(<Carousel {...props}/>);
const buttonNext = wrapper.find('CarouselButton').find('a.next');
const buttonPrev = wrapper.find('CarouselButton').find('a.prev');
const {container} = renderWithContext(<Carousel {...props}/>);
const buttonNext = container.querySelector('a.next') as HTMLElement;
const buttonPrev = container.querySelector('a.prev') as HTMLElement;
buttonNext.simulate('click');
buttonPrev.simulate('click');
await userEvent.click(buttonNext);
await userEvent.click(buttonPrev);
expect(onNextSlideClick).toHaveBeenCalledWith(2);
expect(onPrevSlideClick).toHaveBeenCalledWith(1);

View file

@ -1,13 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {ReactWrapper} from 'enzyme';
import React from 'react';
import InfiniteScroll from 'components/common/infinite_scroll';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import {act, waitFor} from 'tests/react_testing_utils';
import {renderWithContext, fireEvent, waitFor, act} from 'tests/react_testing_utils';
describe('/components/common/InfiniteScroll', () => {
const baseProps = {
@ -18,83 +16,141 @@ describe('/components/common/InfiniteScroll', () => {
totalItems: 20,
itemsPerPage: 10,
pageNumber: 1,
bufferValue: 100,
};
let wrapper: ReactWrapper<any, any, InfiniteScroll>;
beforeEach(() => {
wrapper = mountWithIntl(<InfiniteScroll {...baseProps}><div/></InfiniteScroll>) as unknown as ReactWrapper<any, any, InfiniteScroll>;
});
test('should match snapshot', () => {
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<InfiniteScroll {...baseProps}><div/></InfiniteScroll>);
expect(container).toMatchSnapshot();
const wrapperDiv = wrapper.find(`.${baseProps.styleClass}`);
const wrapperDiv = container.querySelector(`.${baseProps.styleClass}`);
// InfiniteScroll is styled by the user's style
expect(wrapperDiv.exists()).toBe(true);
expect(wrapperDiv).toBeInTheDocument();
// Ensure that scroll is added to InfiniteScroll wrapper div
expect(wrapperDiv.hasClass('infinite-scroll')).toBe(true);
expect(wrapperDiv).toHaveClass('infinite-scroll');
});
test('should attach and remove event listeners', () => {
const instance = wrapper.instance();
const node = instance.node;
node.current!.addEventListener = jest.fn();
node.current!.removeEventListener = jest.fn();
const addEventListenerSpy = jest.spyOn(HTMLDivElement.prototype, 'addEventListener');
const removeEventListenerSpy = jest.spyOn(HTMLDivElement.prototype, 'removeEventListener');
instance.componentDidMount();
expect(node.current!.addEventListener).toHaveBeenCalledTimes(1);
expect(node.current!.removeEventListener).not.toHaveBeenCalled();
const {unmount} = renderWithContext(<InfiniteScroll {...baseProps}><div/></InfiniteScroll>);
instance.componentWillUnmount();
expect(addEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
expect(node.current!.removeEventListener).toHaveBeenCalledTimes(1);
unmount();
expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
addEventListenerSpy.mockRestore();
removeEventListenerSpy.mockRestore();
});
test('should execute call back function when scroll reaches the bottom and there \'s more data and no current fetch is taking place', async () => {
const instance = wrapper.instance();
const {container} = renderWithContext(<InfiniteScroll {...baseProps}><div/></InfiniteScroll>);
expect(baseProps.callBack).toHaveBeenCalledTimes(0);
act(() => {
instance.handleScroll();
const scrollContainer = container.querySelector('.infinite-scroll') as HTMLElement;
// Mock scroll position to simulate being at the bottom
Object.defineProperty(scrollContainer, 'scrollHeight', {value: 1000, configurable: true});
Object.defineProperty(scrollContainer, 'clientHeight', {value: 500, configurable: true});
Object.defineProperty(scrollContainer, 'scrollTop', {value: 500, configurable: true});
await act(async () => {
fireEvent.scroll(scrollContainer);
// Wait for debounce (200ms) plus some buffer
await new Promise((resolve) => setTimeout(resolve, 250));
});
await waitFor(() => {
expect(wrapper.state().isFetching).toBe(true);
expect(baseProps.callBack).toHaveBeenCalledTimes(1);
});
});
test('should not execute call back even if scroll is a the bottom when there \'s no more data', async () => {
act(() => {
wrapper.setState({isEndofData: true});
test('should not execute call back even if scroll is at the bottom when there \'s no more data', async () => {
// Set totalItems to 0 to simulate no data/end of data scenario
const propsWithNoData = {
...baseProps,
totalItems: 0,
};
const {container} = renderWithContext(
<InfiniteScroll {...propsWithNoData}><div/></InfiniteScroll>,
);
const scrollContainer = container.querySelector('.infinite-scroll') as HTMLElement;
// Mock scroll position to simulate being at the bottom
Object.defineProperty(scrollContainer, 'scrollHeight', {value: 1000, configurable: true});
Object.defineProperty(scrollContainer, 'clientHeight', {value: 500, configurable: true});
Object.defineProperty(scrollContainer, 'scrollTop', {value: 500, configurable: true});
await act(async () => {
fireEvent.scroll(scrollContainer);
// Wait for debounce (200ms) plus some buffer
await new Promise((resolve) => setTimeout(resolve, 250));
});
const instance = wrapper.instance();
act(() => {
instance.handleScroll();
// First scroll triggers callback, which then sets isEndofData to true since totalItems is 0
await waitFor(() => {
expect(baseProps.callBack).toHaveBeenCalledTimes(1);
});
expect(baseProps.callBack).toHaveBeenCalledTimes(0);
// Subsequent scroll should not trigger callback since isEndofData is now true
await act(async () => {
fireEvent.scroll(scrollContainer);
await new Promise((resolve) => setTimeout(resolve, 250));
});
// Should still be only 1 call
expect(baseProps.callBack).toHaveBeenCalledTimes(1);
});
test('should not show loading screen if there is no data', () => {
let loadingDiv = wrapper.find('.loading-screen');
expect(loadingDiv.exists()).toBe(false);
test('should not show loading screen if there is no data', async () => {
// Create a callback that doesn't resolve immediately to keep loading state
let resolveCallback: () => void;
const slowCallback = jest.fn(() => new Promise<void>((resolve) => {
resolveCallback = resolve;
}));
act(() => {
wrapper.setState({isFetching: true});
const propsWithSlowCallback = {
...baseProps,
callBack: slowCallback,
};
const {container} = renderWithContext(<InfiniteScroll {...propsWithSlowCallback}><div/></InfiniteScroll>);
let loadingDiv = container.querySelector('.loading-screen');
expect(loadingDiv).not.toBeInTheDocument();
// Mock scroll to trigger fetching state
const scrollContainer = container.querySelector('.infinite-scroll') as HTMLElement;
Object.defineProperty(scrollContainer, 'scrollHeight', {value: 1000, configurable: true});
Object.defineProperty(scrollContainer, 'clientHeight', {value: 500, configurable: true});
Object.defineProperty(scrollContainer, 'scrollTop', {value: 500, configurable: true});
await act(async () => {
fireEvent.scroll(scrollContainer);
// Wait for debounce (200ms) plus some buffer
await new Promise((resolve) => setTimeout(resolve, 250));
});
expect(wrapper).toMatchSnapshot();
expect(wrapper.state().isFetching).toBe(true);
// Now it should show the loader during fetching
loadingDiv = container.querySelector('.loading-screen');
expect(loadingDiv).toBeInTheDocument();
// Now it should show the loader.
loadingDiv = wrapper.find('.loading-screen');
expect(loadingDiv.exists()).toBe(true);
expect(container).toMatchSnapshot();
// Resolve the callback to cleanup
await act(async () => {
resolveCallback!();
});
});
});

View file

@ -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 RadioButtonGroup from 'components/common/radio_group';
import {render, screen, userEvent} from 'tests/react_testing_utils';
describe('/components/common/RadioButtonGroup', () => {
const onChange = jest.fn();
const baseProps = {
@ -16,22 +17,22 @@ describe('/components/common/RadioButtonGroup', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(<RadioButtonGroup {...baseProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = render(<RadioButtonGroup {...baseProps}/>);
expect(container).toMatchSnapshot();
});
test('test radio button group input lenght is as expected', () => {
const wrapper = shallow(<RadioButtonGroup {...baseProps}/>);
const buttons = wrapper.find('input');
render(<RadioButtonGroup {...baseProps}/>);
const buttons = screen.getAllByRole('radio');
expect(buttons.length).toBe(3);
});
test('test radio button group onChange function', () => {
const wrapper = shallow(<RadioButtonGroup {...baseProps}/>);
test('test radio button group onChange function', async () => {
render(<RadioButtonGroup {...baseProps}/>);
const buttons = wrapper.find('input');
buttons.at(0).simulate('change');
const buttons = screen.getAllByRole('radio');
await userEvent.click(buttons[0]);
expect(onChange).toHaveBeenCalledTimes(1);
});

View file

@ -1,12 +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 {FormattedMessage} from 'react-intl';
import SiteNameAndDescription from 'components/common/site_name_and_description';
import {renderWithContext, screen} from 'tests/react_testing_utils';
describe('/components/common/SiteNameAndDescription', () => {
const baseProps = {
customDescriptionText: '',
@ -14,18 +14,18 @@ describe('/components/common/SiteNameAndDescription', () => {
};
test('should match snapshot, default', () => {
const wrapper = shallow(<SiteNameAndDescription {...baseProps}/>);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('h1').text()).toEqual(baseProps.siteName);
expect(wrapper.find(FormattedMessage).exists()).toBe(true);
const {container} = renderWithContext(<SiteNameAndDescription {...baseProps}/>);
expect(container).toMatchSnapshot();
expect(screen.getByRole('heading', {level: 1})).toHaveTextContent(baseProps.siteName);
expect(screen.getByText('All team communication in one place, searchable and accessible anywhere')).toBeInTheDocument();
});
test('should match snapshot, with custom site name and description', () => {
const props = {...baseProps, customDescriptionText: 'custom_description_text', siteName: 'other_site'};
const wrapper = shallow(<SiteNameAndDescription {...props}/>);
const {container} = renderWithContext(<SiteNameAndDescription {...props}/>);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('h1').text()).toEqual(props.siteName);
expect(wrapper.find('h3').text()).toEqual(props.customDescriptionText);
expect(container).toMatchSnapshot();
expect(screen.getByRole('heading', {level: 1})).toHaveTextContent(props.siteName);
expect(screen.getByRole('heading', {level: 3})).toHaveTextContent(props.customDescriptionText);
});
});

View file

@ -2,36 +2,111 @@
exports[`component/create_team should match snapshot default 1`] = `
<div>
<Connect(Component) />
<BackButton
url="/undefined/channels/undefined"
/>
<div
className="col-sm-12"
>
<div>
<div
className="signup-team__container"
class="signup-header"
>
<Memo(SiteNameAndDescription)
customDescriptionText=""
siteName=""
/>
<div
className="signup__content"
<a
data-testid="back_button"
href="/undefined/channels/undefined"
>
<Switch>
<Route
path="/display_name"
render={[Function]}
/>
<Route
path="/team_url"
render={[Function]}
/>
<Redirect
to="/display_name"
/>
</Switch>
<span
class="fa fa-1x fa-angle-left"
id="back_button_icon"
title="Back Icon"
/>
Back
</a>
</div>
<div
class="col-sm-12"
>
<div
class="signup-team__container"
>
<h1
id="site_name"
/>
<h3
class="color--light"
id="site_description"
>
All team communication in one place, searchable and accessible anywhere
</h3>
<div
class="signup__content"
>
<div>
<form>
<img
alt="signup logo"
class="signup-team-logo"
src=""
/>
<label
for="teamNameInput"
>
<strong>
Team Name
</strong>
</label>
<div
class="form-group"
>
<div
class="row"
>
<div
class="col-sm-9"
>
<div
class="Input_container"
>
<div
class="Input_fieldset Input_fieldset___legend"
data-testid="input-wrapper"
>
<label
class="Input_legend Input_legend___focus"
for="input_teamNameInput"
>
</label>
<div
class="Input_wrapper"
>
<input
class="Input form-control medium Input__focus"
id="teamNameInput"
maxlength="64"
name="teamNameInput"
spellcheck="false"
type="text"
value=""
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
Name your team in any language. Your team name shows in menus and headings.
</div>
<button
class="btn btn-primary mt-8"
disabled=""
id="teamNameNextButton"
type="submit"
>
Next
<i
class="icon icon-chevron-right"
/>
</button>
</form>
</div>
</div>
</div>
</div>
</div>

View file

@ -2,66 +2,74 @@
exports[`/components/create_team/components/display_name should match snapshot 1`] = `
<div>
<form>
<img
alt="signup logo"
className="signup-team-logo"
src="logo.png"
/>
<label
htmlFor="teamNameInput"
>
<MemoizedFormattedMessage
defaultMessage="Team Name"
id="create_team.display_name.teamName"
tagName="strong"
<div>
<form>
<img
alt="signup logo"
class="signup-team-logo"
src="logo.png"
/>
</label>
<div
className="form-group"
>
<label
for="teamNameInput"
>
<strong>
Team Name
</strong>
</label>
<div
className="row"
class="form-group"
>
<div
className="col-sm-9"
class="row"
>
<ForwardRef
autoFocus={true}
id="teamNameInput"
maxLength={64}
minLength={2}
name="teamNameInput"
onChange={[Function]}
required={true}
spellCheck="false"
type="text"
value="test-team"
/>
<div
class="col-sm-9"
>
<div
class="Input_container"
>
<div
class="Input_fieldset Input_fieldset___legend"
data-testid="input-wrapper"
>
<label
class="Input_legend Input_legend___focus"
for="input_teamNameInput"
>
</label>
<div
class="Input_wrapper"
>
<input
class="Input form-control medium Input__focus"
id="teamNameInput"
maxlength="64"
name="teamNameInput"
spellcheck="false"
type="text"
value="test-team"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<MemoizedFormattedMessage
defaultMessage="Name your team in any language. Your team name shows in menus and headings."
id="create_team.display_name.nameHelp"
/>
</div>
<button
className="btn btn-primary mt-8"
disabled={false}
id="teamNameNextButton"
onClick={[Function]}
type="submit"
>
<MemoizedFormattedMessage
defaultMessage="Next"
id="create_team.display_name.next"
/>
<i
className="icon icon-chevron-right"
/>
</button>
</form>
<div>
Name your team in any language. Your team name shows in menus and headings.
</div>
<button
class="btn btn-primary mt-8"
id="teamNameNextButton"
type="submit"
>
Next
<i
class="icon icon-chevron-right"
/>
</button>
</form>
</div>
</div>
`;

View file

@ -1,13 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import type {ReactWrapper} from 'enzyme';
import React from 'react';
import DisplayName from 'components/create_team/components/display_name';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import {renderWithContext, screen, fireEvent, userEvent} from 'tests/react_testing_utils';
import {cleanUpUrlable} from 'utils/url';
jest.mock('images/logo.png', () => 'logo.png');
@ -22,50 +20,48 @@ describe('/components/create_team/components/display_name', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(<DisplayName {...defaultProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<DisplayName {...defaultProps}/>);
expect(container).toMatchSnapshot();
});
test('should run updateParent function', () => {
const wrapper = mountWithIntl(<DisplayName {...defaultProps}/>);
test('should run updateParent function', async () => {
renderWithContext(<DisplayName {...defaultProps}/>);
wrapper.find('button').simulate('click', {
preventDefault: () => jest.fn(),
});
await userEvent.click(screen.getByRole('button', {name: /next/i}));
expect(wrapper.prop('updateParent')).toHaveBeenCalled();
expect(defaultProps.updateParent).toHaveBeenCalled();
});
test('should pass state to updateParent function', () => {
const wrapper = mountWithIntl(<DisplayName {...defaultProps}/>);
test('should pass state to updateParent function', async () => {
renderWithContext(<DisplayName {...defaultProps}/>);
wrapper.find('button').simulate('click', {
preventDefault: () => jest.fn(),
});
await userEvent.click(screen.getByRole('button', {name: /next/i}));
expect(wrapper.prop('updateParent')).toHaveBeenCalledWith(defaultProps.state);
expect(defaultProps.updateParent).toHaveBeenCalledWith(expect.objectContaining({
wizard: 'team_url',
team: expect.objectContaining({
display_name: 'test-team',
}),
}));
});
test('should pass updated team name to updateParent function', () => {
const wrapper = mountWithIntl(<DisplayName {...defaultProps}/>);
test('should pass updated team name to updateParent function', async () => {
renderWithContext(<DisplayName {...defaultProps}/>);
const teamDisplayName = 'My Test Team';
const newState = {
...defaultProps.state,
team: {
...defaultProps.state.team,
display_name: teamDisplayName,
name: cleanUpUrlable(teamDisplayName),
},
const expectedTeam = {
...defaultProps.state.team,
display_name: teamDisplayName,
name: cleanUpUrlable(teamDisplayName),
};
(wrapper.find('.form-control') as unknown as ReactWrapper<any, any, HTMLInputElement>).instance().value = teamDisplayName;
wrapper.find('.form-control').simulate('change');
const input = screen.getByRole('textbox');
fireEvent.change(input, {target: {value: teamDisplayName}});
wrapper.find('button').simulate('click', {
preventDefault: () => jest.fn(),
});
await userEvent.click(screen.getByRole('button', {name: /next/i}));
expect(wrapper.prop('updateParent')).toHaveBeenCalledWith(defaultProps.state);
expect(wrapper.prop('updateParent').mock.calls[0][0]).toEqual(newState);
expect(defaultProps.updateParent).toHaveBeenCalledWith(expect.objectContaining({
wizard: 'team_url',
team: expectedTeam,
}));
});
});

View file

@ -2,119 +2,88 @@
exports[`/components/create_team/components/display_name should match snapshot 1`] = `
<div>
<form>
<img
alt="signup team logo"
className="signup-team-logo"
src="logo.png"
/>
<label
htmlFor="teamURLInput"
>
<MemoizedFormattedMessage
defaultMessage="Team URL"
id="create_team.team_url.teamUrl"
tagName="strong"
<div>
<form>
<img
alt="signup team logo"
class="signup-team-logo"
src="logo.png"
/>
</label>
<div
className="form-group"
>
<label
for="teamURLInput"
>
<strong>
Team URL
</strong>
</label>
<div
className="row"
class="form-group"
>
<div
className="col-sm-11"
class="row"
>
<div
className="input-group input-group--limit"
class="col-sm-11"
>
<WithTooltip
title="http://localhost:8065/"
<div
class="input-group input-group--limit"
>
<span
className="input-group-addon"
class="input-group-addon"
>
http://localhost:8065/
</span>
</WithTooltip>
<input
aria-describedby="teamURLInputError"
autoFocus={true}
className="form-control"
id="teamURLInput"
maxLength={128}
onChange={[Function]}
onFocus={[Function]}
placeholder=""
spellCheck="false"
type="text"
value="test-team"
/>
<input
aria-describedby="teamURLInputError"
class="form-control"
id="teamURLInput"
maxlength="128"
placeholder=""
spellcheck="false"
type="text"
value="test-team"
/>
</div>
</div>
</div>
</div>
</div>
<p>
<MemoizedFormattedMessage
defaultMessage="Choose the web address of your new team:"
id="create_team.team_url.webAddress"
/>
</p>
<ul
className="color--light"
>
<li>
<MemoizedFormattedMessage
defaultMessage="Short and memorable is best"
id="create_team.team_url.hint1"
/>
</li>
<li>
<MemoizedFormattedMessage
defaultMessage="Use lowercase letters, numbers and dashes"
id="create_team.team_url.hint2"
/>
</li>
<li>
<MemoizedFormattedMessage
defaultMessage="Must start with a letter and can't end in a dash"
id="create_team.team_url.hint3"
/>
</li>
</ul>
<div
className="mt-8"
>
<Button
active={false}
block={false}
bsClass="btn"
bsStyle="primary"
disabled={false}
id="teamURLFinishButton"
onClick={[Function]}
type="submit"
<p>
Choose the web address of your new team:
</p>
<ul
class="color--light"
>
<MemoizedFormattedMessage
defaultMessage="Finish"
id="create_team.team_url.finish"
/>
</Button>
</div>
<div
className="mt-8"
>
<a
href="#"
onClick={[Function]}
<li>
Short and memorable is best
</li>
<li>
Use lowercase letters, numbers and dashes
</li>
<li>
Must start with a letter and can't end in a dash
</li>
</ul>
<div
class="mt-8"
>
<MemoizedFormattedMessage
defaultMessage="Back to previous step"
id="create_team.team_url.back"
/>
</a>
</div>
</form>
<button
class="btn btn-primary"
id="teamURLFinishButton"
type="submit"
>
Finish
</button>
</div>
<div
class="mt-8"
>
<a
href="#"
>
Back to previous step
</a>
</div>
</form>
</div>
</div>
`;

View file

@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import TeamUrl from 'components/create_team/components/team_url/team_url';
@ -26,8 +25,8 @@ describe('/components/create_team/components/display_name', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(<TeamUrl {...defaultProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<TeamUrl {...defaultProps}/>);
expect(container).toMatchSnapshot();
});
test('should return to display_name.jsx page', async () => {

View file

@ -1,9 +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 {FormattedMessage} from 'react-intl';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import {CreateTeam} from './create_team';
@ -29,11 +29,11 @@ describe('component/create_team', () => {
} as any;
test('should match snapshot default', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<CreateTeam {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should show title and message when cloud and team limit reached', () => {
@ -48,22 +48,11 @@ describe('component/create_team', () => {
},
};
const wrapper = shallow(
renderWithContext(
<CreateTeam {...props}/>,
);
expect(wrapper.contains(
<FormattedMessage
id='create_team.createTeamRestricted.title'
tagName='strong'
defaultMessage='Professional feature'
/>,
)).toEqual(true);
expect(wrapper.contains(
<FormattedMessage
id='create_team.createTeamRestricted.message'
defaultMessage='Your workspace plan has reached the limit on the number of teams. Create unlimited teams with a free 30-day trial. Contact your System Administrator.'
/>,
)).toEqual(true);
expect(screen.getByText('Professional feature')).toBeInTheDocument();
expect(screen.getByText('Your workspace plan has reached the limit on the number of teams. Create unlimited teams with a free 30-day trial. Contact your System Administrator.')).toBeInTheDocument();
});
});

View file

@ -1,33 +1,34 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/MaxLengthInput should match snapshot 1`] = `
<Fragment>
<div>
<input
className="MaxLengthInput input"
class="MaxLengthInput input"
value=""
/>
</Fragment>
</div>
`;
exports[`components/MaxLengthInput should match snapshot 2`] = `
<Fragment>
<div>
<input
className="MaxLengthInput input"
defaultValue="less than 20"
class="MaxLengthInput input"
value="less than 20"
/>
</Fragment>
</div>
`;
exports[`components/MaxLengthInput should match snapshot 3`] = `
<Fragment>
<div>
<input
className="MaxLengthInput input has-error"
defaultValue="Where is Jessica Hyde?"
class="MaxLengthInput input has-error"
value="Where is Jessica Hyde?"
/>
<span
className="MaxLengthInput__validation"
class="MaxLengthInput__validation"
>
-
2
</span>
</Fragment>
</div>
`;

View file

@ -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 {MaxLengthInput} from 'components/quick_input/index';
import {render, screen} from 'tests/react_testing_utils';
describe('components/MaxLengthInput', () => {
const requiredProps = {
className: 'input',
@ -17,13 +18,13 @@ describe('components/MaxLengthInput', () => {
['less than 20'],
['Where is Jessica Hyde?'],
])('should match snapshot', (defaultValue) => {
const wrapper = shallow(
const {container} = render(
<MaxLengthInput
{...requiredProps}
defaultValue={defaultValue}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test.each([
@ -31,15 +32,16 @@ describe('components/MaxLengthInput', () => {
['less than 20', false, false],
['Where is Jessica Hyde?', true, true],
])('defaultValue: %s .has-error: %s, .MaxLengthInput__validation: %s', (defaultValue, hasError, maxLengthExists) => {
const wrapper = shallow(
const {container} = render(
<MaxLengthInput
{...requiredProps}
defaultValue={defaultValue}
/>,
);
expect(wrapper.find('input').hasClass('has-error')).toBe(hasError);
expect(wrapper.exists('.MaxLengthInput__validation')).toBe(maxLengthExists);
const input = screen.getByRole('textbox');
expect(input.classList.contains('has-error')).toBe(hasError);
expect(container.querySelector('.MaxLengthInput__validation') !== null).toBe(maxLengthExists);
});
test('should display the number of times value length exceeds maxLength', () => {
@ -48,10 +50,10 @@ describe('components/MaxLengthInput', () => {
...requiredProps,
};
const wrapper = shallow(
const {container} = render(
<MaxLengthInput {...props}/>,
);
expect(wrapper.find('.MaxLengthInput__validation').text()).toBe('-2');
expect(container.querySelector('.MaxLengthInput__validation')!.textContent).toBe('-2');
});
});

View file

@ -1,620 +1,462 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/SearchHint should match snapshot, with searchType 1`] = `
<Fragment>
<div>
<ul
className="search-hint__suggestions-list"
class="search-hint__suggestions-list"
role="list"
>
<li
className="search-hint__suggestions-list__option"
key="From:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
From:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages from a user"
id="search_list_option.from"
/>
Messages from a user
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="In:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
In:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages in a channel"
id="search_list_option.in"
/>
Messages in a channel
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="On:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
On:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages on a date"
id="search_list_option.on"
/>
Messages on a date
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="Before:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
Before:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages before a date"
id="search_list_option.before"
/>
Messages before a date
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="After:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
After:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages after a date"
id="search_list_option.after"
/>
Messages after a date
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="-"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Exclude search terms"
id="search_list_option.exclude"
/>
Exclude search terms
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="\\"\\""
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
""
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages with phrases"
id="search_list_option.phrases"
/>
Messages with phrases
</div>
</li>
</ul>
</Fragment>
</div>
`;
exports[`components/SearchHint should match snapshot, with title 1`] = `
<Fragment>
<div>
<h4
className="search-hint__title"
class="search-hint__title"
>
<MemoizedFormattedMessage
defaultMessage="Search options"
id="search_bar.usage.title"
/>
Search options
</h4>
<ul
className="search-hint__suggestions-list"
class="search-hint__suggestions-list"
role="list"
>
<li
className="search-hint__suggestions-list__option"
key="From:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
From:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages from a user"
id="search_list_option.from"
/>
Messages from a user
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="In:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
In:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages in a channel"
id="search_list_option.in"
/>
Messages in a channel
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="On:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
On:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages on a date"
id="search_list_option.on"
/>
Messages on a date
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="Before:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
Before:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages before a date"
id="search_list_option.before"
/>
Messages before a date
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="After:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
After:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages after a date"
id="search_list_option.after"
/>
Messages after a date
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="-"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Exclude search terms"
id="search_list_option.exclude"
/>
Exclude search terms
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="\\"\\""
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
""
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages with phrases"
id="search_list_option.phrases"
/>
Messages with phrases
</div>
</li>
</ul>
</Fragment>
</div>
`;
exports[`components/SearchHint should match snapshot, without searchType 1`] = `
<div
className="search-hint__search-type-selector"
>
<div>
<div
className="search-hint_text-container"
class="search-hint__search-type-selector"
>
<MemoizedFormattedMessage
defaultMessage="What are you searching for?"
id="search_bar.usage.search_type_question"
/>
<MemoizedFormattedMessage
defaultMessage="<a>Learn about search</a>"
id="search_bar.usage.searchLearn"
values={
Object {
"a": [Function],
}
}
/>
</div>
<div
className="button-container"
>
<button
className=""
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
<div
class="search-hint_text-container"
>
<i
className="icon icon-message-text-outline"
/>
<MemoizedFormattedMessage
defaultMessage="Messages"
id="search_bar.usage.search_type_messages"
/>
</button>
<button
className=""
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
What are you searching for?
<a
class="search-hint_learn-search"
href="https://mattermost.com/pl/mattermost-academy-search-training?utm_source=mattermost&utm_medium=in-product&utm_content=search_hint&uid=currentUserId&sid=&edition=team&server_version="
location="search_hint"
rel="noopener noreferrer"
target="_blank"
>
<span>
Learn about search
</span>
<i
class="icon icon-lightbulb-outline"
/>
</a>
</div>
<div
class="button-container"
>
<i
className="icon icon-file-text-outline"
/>
<MemoizedFormattedMessage
defaultMessage="Files"
id="search_bar.usage.search_type_files"
/>
</button>
<button
class=""
>
<i
class="icon icon-message-text-outline"
/>
Messages
</button>
<button
class=""
>
<i
class="icon icon-file-text-outline"
/>
Files
</button>
</div>
</div>
</div>
`;
exports[`components/SearchHint should match snapshot, without title 1`] = `
<Fragment>
<div>
<ul
className="search-hint__suggestions-list"
class="search-hint__suggestions-list"
role="list"
>
<li
className="search-hint__suggestions-list__option"
key="From:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
From:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages from a user"
id="search_list_option.from"
/>
Messages from a user
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="In:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
In:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages in a channel"
id="search_list_option.in"
/>
Messages in a channel
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="On:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
On:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages on a date"
id="search_list_option.on"
/>
Messages on a date
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="Before:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
Before:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages before a date"
id="search_list_option.before"
/>
Messages before a date
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="After:"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
After:
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages after a date"
id="search_list_option.after"
/>
Messages after a date
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="-"
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Exclude search terms"
id="search_list_option.exclude"
/>
Exclude search terms
</div>
</li>
<li
className="search-hint__suggestions-list__option"
key="\\"\\""
onMouseDown={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
class="search-hint__suggestions-list__option"
>
<div
className="search-hint__suggestion-list__flex-wrap"
class="search-hint__suggestion-list__flex-wrap"
>
<span
className="search-hint__suggestion-list__label"
class="search-hint__suggestion-list__label"
>
""
</span>
</div>
<div
className="search-hint__suggestion-list__value"
class="search-hint__suggestion-list__value"
>
<MemoizedFormattedMessage
defaultMessage="Messages with phrases"
id="search_list_option.phrases"
/>
Messages with phrases
</div>
</li>
</ul>
</Fragment>
</div>
`;

View file

@ -1,54 +1,51 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import SearchHint from 'components/search_hint/search_hint';
import {renderWithContext} from 'tests/react_testing_utils';
import {searchHintOptions} from 'utils/constants';
let mockState: any;
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux') as typeof import('react-redux'),
useSelector: (selector: (state: typeof mockState) => unknown) => selector(mockState),
}));
describe('components/SearchHint', () => {
const baseProps = {
withTitle: false,
onOptionSelected: jest.fn(),
options: searchHintOptions,
};
beforeEach(() => {
mockState = {
entities: {
general: {
config: {
EnableFileAttachments: 'true',
},
const initialState = {
entities: {
general: {
config: {
EnableFileAttachments: 'true',
},
},
};
});
users: {
currentUserId: 'currentUserId',
},
},
};
test('should match snapshot, with title', () => {
const props = {
...baseProps,
withTitle: true,
};
const wrapper = shallow(
const {container} = renderWithContext(
<SearchHint {...props}/>,
initialState,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, without title', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<SearchHint {...baseProps}/>,
initialState,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, without searchType', () => {
@ -58,10 +55,11 @@ describe('components/SearchHint', () => {
onSearchTypeSelected: jest.fn(),
searchType: '' as 'files' | 'messages' | '',
};
const wrapper = shallow(
const {container} = renderWithContext(
<SearchHint {...props}/>,
initialState,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, with searchType', () => {
@ -70,9 +68,10 @@ describe('components/SearchHint', () => {
onSearchTypeSelected: jest.fn(),
searchType: 'files' as 'files' | 'messages' | '',
};
const wrapper = shallow(
const {container} = renderWithContext(
<SearchHint {...props}/>,
initialState,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});

View file

@ -1,790 +1,270 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/search_results/FilesFilterMenu should match snapshot, on all filter selected 1`] = `
<div
className="FilesFilterMenu"
>
<MenuWrapper
animationComponent={[Function]}
className=""
<div>
<div
class="FilesFilterMenu"
>
<WithTooltip
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Filter"
id="channel_info_rhs.menu.files.filter"
/>
}
<div
class="MenuWrapper "
>
<IconContainer
className="action-icon dots-icon"
<button
class="IconContainer-hXitkr fFJEfD action-icon dots-icon"
id="filesFilterButton"
type="button"
>
<FilterVariantIcon
color="currentColor"
size={18}
/>
</IconContainer>
</WithTooltip>
<Menu
ariaLabel="file menu"
openLeft={true}
>
<MenuItemAction
ariaLabel="All file types"
icon={
<i
className="icon icon-check"
<svg
fill="currentColor"
height="18"
version="1.1"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6,13H18V11H6M3,6V8H21V6M10,18H14V16H10V18Z"
/>
}
onClick={[Function]}
show={true}
text="All file types"
/>
<MenuItemAction
ariaLabel="Documents"
icon={null}
onClick={[Function]}
show={true}
text="Documents"
/>
<MenuItemAction
ariaLabel="Spreadsheets"
icon={null}
onClick={[Function]}
show={true}
text="Spreadsheets"
/>
<MenuItemAction
ariaLabel="Presentations"
icon={null}
onClick={[Function]}
show={true}
text="Presentations"
/>
<MenuItemAction
ariaLabel="Code"
icon={null}
onClick={[Function]}
show={true}
text="Code"
/>
<MenuItemAction
ariaLabel="Images"
icon={null}
onClick={[Function]}
show={true}
text="Images"
/>
<MenuItemAction
ariaLabel="Audio"
icon={null}
onClick={[Function]}
show={true}
text="Audio"
/>
<MenuItemAction
ariaLabel="Videos"
icon={null}
onClick={[Function]}
show={true}
text="Videos"
/>
</Menu>
</MenuWrapper>
</svg>
</button>
</div>
</div>
</div>
`;
exports[`components/search_results/FilesFilterMenu should match snapshot, on audio filter selected 1`] = `
<div
className="FilesFilterMenu"
>
<MenuWrapper
animationComponent={[Function]}
className=""
<div>
<div
class="FilesFilterMenu"
>
<WithTooltip
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Filter"
id="channel_info_rhs.menu.files.filter"
/>
}
<div
class="MenuWrapper "
>
<IconContainer
className="action-icon dots-icon"
<button
class="IconContainer-hXitkr fFJEfD action-icon dots-icon"
id="filesFilterButton"
type="button"
>
<i
className="icon-dot"
class="icon-dot"
/>
<FilterVariantIcon
color="currentColor"
size={18}
/>
</IconContainer>
</WithTooltip>
<Menu
ariaLabel="file menu"
openLeft={true}
>
<MenuItemAction
ariaLabel="All file types"
icon={null}
onClick={[Function]}
show={true}
text="All file types"
/>
<MenuItemAction
ariaLabel="Documents"
icon={null}
onClick={[Function]}
show={true}
text="Documents"
/>
<MenuItemAction
ariaLabel="Spreadsheets"
icon={null}
onClick={[Function]}
show={true}
text="Spreadsheets"
/>
<MenuItemAction
ariaLabel="Presentations"
icon={null}
onClick={[Function]}
show={true}
text="Presentations"
/>
<MenuItemAction
ariaLabel="Code"
icon={null}
onClick={[Function]}
show={true}
text="Code"
/>
<MenuItemAction
ariaLabel="Images"
icon={null}
onClick={[Function]}
show={true}
text="Images"
/>
<MenuItemAction
ariaLabel="Audio"
icon={
<i
className="icon icon-check"
<svg
fill="currentColor"
height="18"
version="1.1"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6,13H18V11H6M3,6V8H21V6M10,18H14V16H10V18Z"
/>
}
onClick={[Function]}
show={true}
text="Audio"
/>
<MenuItemAction
ariaLabel="Videos"
icon={null}
onClick={[Function]}
show={true}
text="Videos"
/>
</Menu>
</MenuWrapper>
</svg>
</button>
</div>
</div>
</div>
`;
exports[`components/search_results/FilesFilterMenu should match snapshot, on code filter selected 1`] = `
<div
className="FilesFilterMenu"
>
<MenuWrapper
animationComponent={[Function]}
className=""
<div>
<div
class="FilesFilterMenu"
>
<WithTooltip
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Filter"
id="channel_info_rhs.menu.files.filter"
/>
}
<div
class="MenuWrapper "
>
<IconContainer
className="action-icon dots-icon"
<button
class="IconContainer-hXitkr fFJEfD action-icon dots-icon"
id="filesFilterButton"
type="button"
>
<i
className="icon-dot"
class="icon-dot"
/>
<FilterVariantIcon
color="currentColor"
size={18}
/>
</IconContainer>
</WithTooltip>
<Menu
ariaLabel="file menu"
openLeft={true}
>
<MenuItemAction
ariaLabel="All file types"
icon={null}
onClick={[Function]}
show={true}
text="All file types"
/>
<MenuItemAction
ariaLabel="Documents"
icon={null}
onClick={[Function]}
show={true}
text="Documents"
/>
<MenuItemAction
ariaLabel="Spreadsheets"
icon={null}
onClick={[Function]}
show={true}
text="Spreadsheets"
/>
<MenuItemAction
ariaLabel="Presentations"
icon={null}
onClick={[Function]}
show={true}
text="Presentations"
/>
<MenuItemAction
ariaLabel="Code"
icon={
<i
className="icon icon-check"
<svg
fill="currentColor"
height="18"
version="1.1"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6,13H18V11H6M3,6V8H21V6M10,18H14V16H10V18Z"
/>
}
onClick={[Function]}
show={true}
text="Code"
/>
<MenuItemAction
ariaLabel="Images"
icon={null}
onClick={[Function]}
show={true}
text="Images"
/>
<MenuItemAction
ariaLabel="Audio"
icon={null}
onClick={[Function]}
show={true}
text="Audio"
/>
<MenuItemAction
ariaLabel="Videos"
icon={null}
onClick={[Function]}
show={true}
text="Videos"
/>
</Menu>
</MenuWrapper>
</svg>
</button>
</div>
</div>
</div>
`;
exports[`components/search_results/FilesFilterMenu should match snapshot, on documents filter selected 1`] = `
<div
className="FilesFilterMenu"
>
<MenuWrapper
animationComponent={[Function]}
className=""
<div>
<div
class="FilesFilterMenu"
>
<WithTooltip
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Filter"
id="channel_info_rhs.menu.files.filter"
/>
}
<div
class="MenuWrapper "
>
<IconContainer
className="action-icon dots-icon"
<button
class="IconContainer-hXitkr fFJEfD action-icon dots-icon"
id="filesFilterButton"
type="button"
>
<i
className="icon-dot"
class="icon-dot"
/>
<FilterVariantIcon
color="currentColor"
size={18}
/>
</IconContainer>
</WithTooltip>
<Menu
ariaLabel="file menu"
openLeft={true}
>
<MenuItemAction
ariaLabel="All file types"
icon={null}
onClick={[Function]}
show={true}
text="All file types"
/>
<MenuItemAction
ariaLabel="Documents"
icon={
<i
className="icon icon-check"
<svg
fill="currentColor"
height="18"
version="1.1"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6,13H18V11H6M3,6V8H21V6M10,18H14V16H10V18Z"
/>
}
onClick={[Function]}
show={true}
text="Documents"
/>
<MenuItemAction
ariaLabel="Spreadsheets"
icon={null}
onClick={[Function]}
show={true}
text="Spreadsheets"
/>
<MenuItemAction
ariaLabel="Presentations"
icon={null}
onClick={[Function]}
show={true}
text="Presentations"
/>
<MenuItemAction
ariaLabel="Code"
icon={null}
onClick={[Function]}
show={true}
text="Code"
/>
<MenuItemAction
ariaLabel="Images"
icon={null}
onClick={[Function]}
show={true}
text="Images"
/>
<MenuItemAction
ariaLabel="Audio"
icon={null}
onClick={[Function]}
show={true}
text="Audio"
/>
<MenuItemAction
ariaLabel="Videos"
icon={null}
onClick={[Function]}
show={true}
text="Videos"
/>
</Menu>
</MenuWrapper>
</svg>
</button>
</div>
</div>
</div>
`;
exports[`components/search_results/FilesFilterMenu should match snapshot, on images filter selected 1`] = `
<div
className="FilesFilterMenu"
>
<MenuWrapper
animationComponent={[Function]}
className=""
<div>
<div
class="FilesFilterMenu"
>
<WithTooltip
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Filter"
id="channel_info_rhs.menu.files.filter"
/>
}
<div
class="MenuWrapper "
>
<IconContainer
className="action-icon dots-icon"
<button
class="IconContainer-hXitkr fFJEfD action-icon dots-icon"
id="filesFilterButton"
type="button"
>
<i
className="icon-dot"
class="icon-dot"
/>
<FilterVariantIcon
color="currentColor"
size={18}
/>
</IconContainer>
</WithTooltip>
<Menu
ariaLabel="file menu"
openLeft={true}
>
<MenuItemAction
ariaLabel="All file types"
icon={null}
onClick={[Function]}
show={true}
text="All file types"
/>
<MenuItemAction
ariaLabel="Documents"
icon={null}
onClick={[Function]}
show={true}
text="Documents"
/>
<MenuItemAction
ariaLabel="Spreadsheets"
icon={null}
onClick={[Function]}
show={true}
text="Spreadsheets"
/>
<MenuItemAction
ariaLabel="Presentations"
icon={null}
onClick={[Function]}
show={true}
text="Presentations"
/>
<MenuItemAction
ariaLabel="Code"
icon={null}
onClick={[Function]}
show={true}
text="Code"
/>
<MenuItemAction
ariaLabel="Images"
icon={
<i
className="icon icon-check"
<svg
fill="currentColor"
height="18"
version="1.1"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6,13H18V11H6M3,6V8H21V6M10,18H14V16H10V18Z"
/>
}
onClick={[Function]}
show={true}
text="Images"
/>
<MenuItemAction
ariaLabel="Audio"
icon={null}
onClick={[Function]}
show={true}
text="Audio"
/>
<MenuItemAction
ariaLabel="Videos"
icon={null}
onClick={[Function]}
show={true}
text="Videos"
/>
</Menu>
</MenuWrapper>
</svg>
</button>
</div>
</div>
</div>
`;
exports[`components/search_results/FilesFilterMenu should match snapshot, on presentations filter selected 1`] = `
<div
className="FilesFilterMenu"
>
<MenuWrapper
animationComponent={[Function]}
className=""
<div>
<div
class="FilesFilterMenu"
>
<WithTooltip
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Filter"
id="channel_info_rhs.menu.files.filter"
/>
}
<div
class="MenuWrapper "
>
<IconContainer
className="action-icon dots-icon"
<button
class="IconContainer-hXitkr fFJEfD action-icon dots-icon"
id="filesFilterButton"
type="button"
>
<i
className="icon-dot"
class="icon-dot"
/>
<FilterVariantIcon
color="currentColor"
size={18}
/>
</IconContainer>
</WithTooltip>
<Menu
ariaLabel="file menu"
openLeft={true}
>
<MenuItemAction
ariaLabel="All file types"
icon={null}
onClick={[Function]}
show={true}
text="All file types"
/>
<MenuItemAction
ariaLabel="Documents"
icon={null}
onClick={[Function]}
show={true}
text="Documents"
/>
<MenuItemAction
ariaLabel="Spreadsheets"
icon={null}
onClick={[Function]}
show={true}
text="Spreadsheets"
/>
<MenuItemAction
ariaLabel="Presentations"
icon={
<i
className="icon icon-check"
<svg
fill="currentColor"
height="18"
version="1.1"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6,13H18V11H6M3,6V8H21V6M10,18H14V16H10V18Z"
/>
}
onClick={[Function]}
show={true}
text="Presentations"
/>
<MenuItemAction
ariaLabel="Code"
icon={null}
onClick={[Function]}
show={true}
text="Code"
/>
<MenuItemAction
ariaLabel="Images"
icon={null}
onClick={[Function]}
show={true}
text="Images"
/>
<MenuItemAction
ariaLabel="Audio"
icon={null}
onClick={[Function]}
show={true}
text="Audio"
/>
<MenuItemAction
ariaLabel="Videos"
icon={null}
onClick={[Function]}
show={true}
text="Videos"
/>
</Menu>
</MenuWrapper>
</svg>
</button>
</div>
</div>
</div>
`;
exports[`components/search_results/FilesFilterMenu should match snapshot, on spreadsheets filter selected 1`] = `
<div
className="FilesFilterMenu"
>
<MenuWrapper
animationComponent={[Function]}
className=""
<div>
<div
class="FilesFilterMenu"
>
<WithTooltip
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Filter"
id="channel_info_rhs.menu.files.filter"
/>
}
<div
class="MenuWrapper "
>
<IconContainer
className="action-icon dots-icon"
<button
class="IconContainer-hXitkr fFJEfD action-icon dots-icon"
id="filesFilterButton"
type="button"
>
<i
className="icon-dot"
class="icon-dot"
/>
<FilterVariantIcon
color="currentColor"
size={18}
/>
</IconContainer>
</WithTooltip>
<Menu
ariaLabel="file menu"
openLeft={true}
>
<MenuItemAction
ariaLabel="All file types"
icon={null}
onClick={[Function]}
show={true}
text="All file types"
/>
<MenuItemAction
ariaLabel="Documents"
icon={null}
onClick={[Function]}
show={true}
text="Documents"
/>
<MenuItemAction
ariaLabel="Spreadsheets"
icon={
<i
className="icon icon-check"
<svg
fill="currentColor"
height="18"
version="1.1"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6,13H18V11H6M3,6V8H21V6M10,18H14V16H10V18Z"
/>
}
onClick={[Function]}
show={true}
text="Spreadsheets"
/>
<MenuItemAction
ariaLabel="Presentations"
icon={null}
onClick={[Function]}
show={true}
text="Presentations"
/>
<MenuItemAction
ariaLabel="Code"
icon={null}
onClick={[Function]}
show={true}
text="Code"
/>
<MenuItemAction
ariaLabel="Images"
icon={null}
onClick={[Function]}
show={true}
text="Images"
/>
<MenuItemAction
ariaLabel="Audio"
icon={null}
onClick={[Function]}
show={true}
text="Audio"
/>
<MenuItemAction
ariaLabel="Videos"
icon={null}
onClick={[Function]}
show={true}
text="Videos"
/>
</Menu>
</MenuWrapper>
</svg>
</button>
</div>
</div>
</div>
`;
exports[`components/search_results/FilesFilterMenu should match snapshot, on video filter selected 1`] = `
<div
className="FilesFilterMenu"
>
<MenuWrapper
animationComponent={[Function]}
className=""
<div>
<div
class="FilesFilterMenu"
>
<WithTooltip
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Filter"
id="channel_info_rhs.menu.files.filter"
/>
}
<div
class="MenuWrapper "
>
<IconContainer
className="action-icon dots-icon"
<button
class="IconContainer-hXitkr fFJEfD action-icon dots-icon"
id="filesFilterButton"
type="button"
>
<i
className="icon-dot"
class="icon-dot"
/>
<FilterVariantIcon
color="currentColor"
size={18}
/>
</IconContainer>
</WithTooltip>
<Menu
ariaLabel="file menu"
openLeft={true}
>
<MenuItemAction
ariaLabel="All file types"
icon={null}
onClick={[Function]}
show={true}
text="All file types"
/>
<MenuItemAction
ariaLabel="Documents"
icon={null}
onClick={[Function]}
show={true}
text="Documents"
/>
<MenuItemAction
ariaLabel="Spreadsheets"
icon={null}
onClick={[Function]}
show={true}
text="Spreadsheets"
/>
<MenuItemAction
ariaLabel="Presentations"
icon={null}
onClick={[Function]}
show={true}
text="Presentations"
/>
<MenuItemAction
ariaLabel="Code"
icon={null}
onClick={[Function]}
show={true}
text="Code"
/>
<MenuItemAction
ariaLabel="Images"
icon={null}
onClick={[Function]}
show={true}
text="Images"
/>
<MenuItemAction
ariaLabel="Audio"
icon={null}
onClick={[Function]}
show={true}
text="Audio"
/>
<MenuItemAction
ariaLabel="Videos"
icon={
<i
className="icon icon-check"
<svg
fill="currentColor"
height="18"
version="1.1"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6,13H18V11H6M3,6V8H21V6M10,18H14V16H10V18Z"
/>
}
onClick={[Function]}
show={true}
text="Videos"
/>
</Menu>
</MenuWrapper>
</svg>
</button>
</div>
</div>
</div>
`;

View file

@ -1,124 +1,179 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/search_results/MessagesOrFilesSelector should match snapshot, on files selected 1`] = `
<ContextProvider
value={
Object {
"getServerState": undefined,
"identityFunctionCheck": "once",
"stabilityCheck": "once",
"store": Object {
"clearActions": [Function],
"dispatch": [Function],
"getActions": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
},
"subscription": Object {
"addNestedSub": [Function],
"getListeners": [Function],
"handleChangeWrapper": [Function],
"isSubscribed": [Function],
"notifyNestedSubs": [Function],
"trySubscribe": [Function],
"tryUnsubscribe": [Function],
},
}
}
>
<MessagesOrFilesSelector
crossTeamSearchEnabled={false}
filesCounter="10"
isFileAttachmentsEnabled={true}
messagesCounter="5"
onChange={[MockFunction]}
onFilter={[MockFunction]}
onTeamChange={[MockFunction]}
selected="files"
selectedFilter="code"
/>
</ContextProvider>
<div>
<div
class="MessagesOrFilesSelector"
>
<div
aria-label="Messages or Files"
class="buttons-container"
role="tablist"
>
<button
aria-controls="messagesPanel"
aria-selected="false"
class="tab messages-tab"
id="messagesTab"
role="tab"
tabindex="-1"
>
Messages
<span
class="counter"
>
5
</span>
</button>
<button
aria-controls="filesPanel"
aria-selected="true"
class="active tab files-tab"
id="filesTab"
role="tab"
tabindex="0"
>
Files
<span
class="counter"
>
10
</span>
</button>
</div>
<div
class="FilesFilterMenu"
>
<div
class="MenuWrapper "
>
<button
class="IconContainer-hXitkr fFJEfD action-icon dots-icon"
id="filesFilterButton"
type="button"
>
<i
class="icon-dot"
/>
<svg
fill="currentColor"
height="18"
version="1.1"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6,13H18V11H6M3,6V8H21V6M10,18H14V16H10V18Z"
/>
</svg>
</button>
</div>
</div>
</div>
</div>
`;
exports[`components/search_results/MessagesOrFilesSelector should match snapshot, on messages selected 1`] = `
<ContextProvider
value={
Object {
"getServerState": undefined,
"identityFunctionCheck": "once",
"stabilityCheck": "once",
"store": Object {
"clearActions": [Function],
"dispatch": [Function],
"getActions": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
},
"subscription": Object {
"addNestedSub": [Function],
"getListeners": [Function],
"handleChangeWrapper": [Function],
"isSubscribed": [Function],
"notifyNestedSubs": [Function],
"trySubscribe": [Function],
"tryUnsubscribe": [Function],
},
}
}
>
<MessagesOrFilesSelector
crossTeamSearchEnabled={false}
filesCounter="10"
isFileAttachmentsEnabled={true}
messagesCounter="5"
onChange={[MockFunction]}
onFilter={[MockFunction]}
onTeamChange={[MockFunction]}
selected="messages"
selectedFilter="code"
/>
</ContextProvider>
<div>
<div
class="MessagesOrFilesSelector"
>
<div
aria-label="Messages or Files"
class="buttons-container"
role="tablist"
>
<button
aria-controls="messagesPanel"
aria-selected="true"
class="active tab messages-tab"
id="messagesTab"
role="tab"
tabindex="0"
>
Messages
<span
class="counter"
>
5
</span>
</button>
<button
aria-controls="filesPanel"
aria-selected="false"
class="tab files-tab"
id="filesTab"
role="tab"
tabindex="-1"
>
Files
<span
class="counter"
>
10
</span>
</button>
</div>
</div>
</div>
`;
exports[`components/search_results/MessagesOrFilesSelector should match snapshot, without files tab 1`] = `
<ContextProvider
value={
Object {
"getServerState": undefined,
"identityFunctionCheck": "once",
"stabilityCheck": "once",
"store": Object {
"clearActions": [Function],
"dispatch": [Function],
"getActions": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
},
"subscription": Object {
"addNestedSub": [Function],
"getListeners": [Function],
"handleChangeWrapper": [Function],
"isSubscribed": [Function],
"notifyNestedSubs": [Function],
"trySubscribe": [Function],
"tryUnsubscribe": [Function],
},
}
}
>
<MessagesOrFilesSelector
crossTeamSearchEnabled={false}
filesCounter="10"
isFileAttachmentsEnabled={false}
messagesCounter="5"
onChange={[MockFunction]}
onFilter={[MockFunction]}
onTeamChange={[MockFunction]}
selected="files"
selectedFilter="code"
/>
</ContextProvider>
<div>
<div
class="MessagesOrFilesSelector"
>
<div
aria-label="Messages or Files"
class="buttons-container"
role="tablist"
>
<button
aria-controls="messagesPanel"
aria-selected="false"
class="tab messages-tab"
id="messagesTab"
role="tab"
tabindex="-1"
>
Messages
<span
class="counter"
>
5
</span>
</button>
</div>
<div
class="FilesFilterMenu"
>
<div
class="MenuWrapper "
>
<button
class="IconContainer-hXitkr fFJEfD action-icon dots-icon"
id="filesFilterButton"
type="button"
>
<i
class="icon-dot"
/>
<svg
fill="currentColor"
height="18"
version="1.1"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6,13H18V11H6M3,6V8H21V6M10,18H14V16H10V18Z"
/>
</svg>
</button>
</div>
</div>
</div>
</div>
`;

View file

@ -1,24 +1,24 @@
// 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 FilesFilterMenu from 'components/search_results/files_filter_menu';
import {renderWithContext} from 'tests/react_testing_utils';
describe('components/search_results/FilesFilterMenu', () => {
const filters = ['all', 'documents', 'spreadsheets', 'presentations', 'code', 'images', 'audio', 'video'];
for (const filter of filters) {
test(`should match snapshot, on ${filter} filter selected`, () => {
const wrapper: ShallowWrapper<any, any, any> = shallow(
const {container} = renderWithContext(
<FilesFilterMenu
selectedFilter={filter}
onFilter={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
}
});

View file

@ -1,77 +1,81 @@
// 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 {Provider} from 'react-redux';
import type {SearchFilterType} from 'components/search/types';
import MessagesOrFilesSelector from 'components/search_results/messages_or_files_selector';
import mockStore from 'tests/test_store';
import {renderWithContext} from 'tests/react_testing_utils';
describe('components/search_results/MessagesOrFilesSelector', () => {
const store = mockStore({});
const baseProps = {
selected: 'messages' as 'messages' | 'files',
selectedFilter: 'code' as SearchFilterType,
messagesCounter: '5',
filesCounter: '10',
isFileAttachmentsEnabled: true,
onChange: jest.fn(),
onFilter: jest.fn(),
onTeamChange: jest.fn(),
crossTeamSearchEnabled: false,
};
const initialState = {
entities: {
teams: {
currentTeamId: 'team1',
teams: {
team1: {id: 'team1', name: 'team1', display_name: 'Team 1'},
},
myMembers: {
team1: {team_id: 'team1', user_id: 'user1'},
},
},
users: {
currentUserId: 'user1',
},
general: {
config: {},
},
},
};
test('should match snapshot, on messages selected', () => {
const wrapper: ShallowWrapper<any, any, any> = shallow(
<Provider store={store}>
<MessagesOrFilesSelector
selected='messages'
selectedFilter='code'
messagesCounter='5'
filesCounter='10'
isFileAttachmentsEnabled={true}
onChange={jest.fn()}
onFilter={jest.fn()}
onTeamChange={jest.fn()}
crossTeamSearchEnabled={false}
/>
</Provider>,
const {container} = renderWithContext(
<MessagesOrFilesSelector {...baseProps}/>,
initialState,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, on files selected', () => {
const wrapper: ShallowWrapper<any, any, any> = shallow(
const props = {
...baseProps,
selected: 'files' as 'messages' | 'files',
};
<Provider store={store}>
<MessagesOrFilesSelector
selected='files'
selectedFilter='code'
messagesCounter='5'
filesCounter='10'
isFileAttachmentsEnabled={true}
onChange={jest.fn()}
onFilter={jest.fn()}
onTeamChange={jest.fn()}
crossTeamSearchEnabled={false}
/>
</Provider>,
const {container} = renderWithContext(
<MessagesOrFilesSelector {...props}/>,
initialState,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, without files tab', () => {
const wrapper: ShallowWrapper<any, any, any> = shallow(
const props = {
...baseProps,
selected: 'files' as 'messages' | 'files',
isFileAttachmentsEnabled: false,
};
<Provider store={store}>
<MessagesOrFilesSelector
selected='files'
selectedFilter='code'
messagesCounter='5'
filesCounter='10'
isFileAttachmentsEnabled={false}
onChange={jest.fn()}
onFilter={jest.fn()}
onTeamChange={jest.fn()}
crossTeamSearchEnabled={false}
/>
</Provider>,
const {container} = renderWithContext(
<MessagesOrFilesSelector {...props}/>,
initialState,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});

View file

@ -1,89 +1,87 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/SearchShortcut should match snapshot on Mac desktop 1`] = `
<span
className="search-shortcut"
>
<ShortcutKey
variant="contrast"
<div>
<span
class="search-shortcut"
>
Ctrl
</ShortcutKey>
<ShortcutKey
variant="contrast"
>
Shift
</ShortcutKey>
<ShortcutKey
variant="contrast"
>
F
</ShortcutKey>
</span>
<mark
class="shortcut-key shortcut-key--contrast"
>
</mark>
<mark
class="shortcut-key shortcut-key--contrast"
>
F
</mark>
</span>
</div>
`;
exports[`components/SearchShortcut should match snapshot on Mac webapp 1`] = `
<span
className="search-shortcut"
>
<ShortcutKey
variant="contrast"
<div>
<span
class="search-shortcut"
>
Ctrl
</ShortcutKey>
<ShortcutKey
variant="contrast"
>
Shift
</ShortcutKey>
<ShortcutKey
variant="contrast"
>
F
</ShortcutKey>
</span>
<mark
class="shortcut-key shortcut-key--contrast"
>
</mark>
<mark
class="shortcut-key shortcut-key--contrast"
>
Shift
</mark>
<mark
class="shortcut-key shortcut-key--contrast"
>
F
</mark>
</span>
</div>
`;
exports[`components/SearchShortcut should match snapshot on Windows desktop 1`] = `
<span
className="search-shortcut"
>
<ShortcutKey
variant="contrast"
<div>
<span
class="search-shortcut"
>
Ctrl
</ShortcutKey>
<ShortcutKey
variant="contrast"
>
Shift
</ShortcutKey>
<ShortcutKey
variant="contrast"
>
F
</ShortcutKey>
</span>
<mark
class="shortcut-key shortcut-key--contrast"
>
Ctrl
</mark>
<mark
class="shortcut-key shortcut-key--contrast"
>
F
</mark>
</span>
</div>
`;
exports[`components/SearchShortcut should match snapshot on Windows webapp 1`] = `
<span
className="search-shortcut"
>
<ShortcutKey
variant="contrast"
<div>
<span
class="search-shortcut"
>
Ctrl
</ShortcutKey>
<ShortcutKey
variant="contrast"
>
Shift
</ShortcutKey>
<ShortcutKey
variant="contrast"
>
F
</ShortcutKey>
</span>
<mark
class="shortcut-key shortcut-key--contrast"
>
Ctrl
</mark>
<mark
class="shortcut-key shortcut-key--contrast"
>
Shift
</mark>
<mark
class="shortcut-key shortcut-key--contrast"
>
F
</mark>
</span>
</div>
`;

View file

@ -1,65 +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 {SearchShortcut} from 'components/search_shortcut';
import {render} from 'tests/react_testing_utils';
import * as UserAgent from 'utils/user_agent';
describe('components/SearchShortcut', () => {
test('should match snapshot on Windows webapp', () => {
jest.mock('utils/user_agent', () => {
const original = jest.requireActual('utils/user_agent');
return {
...original,
isDesktopApp: jest.fn(() => false),
isMac: jest.fn(() => false),
};
});
jest.spyOn(UserAgent, 'isDesktopApp').mockReturnValue(false);
jest.spyOn(UserAgent, 'isMac').mockReturnValue(false);
const wrapper = shallow(<SearchShortcut/>);
expect(wrapper).toMatchSnapshot();
const {container} = render(<SearchShortcut/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot on Mac webapp', () => {
jest.mock('utils/user_agent', () => {
const original = jest.requireActual('utils/user_agent');
return {
...original,
isDesktopApp: jest.fn(() => false),
isMac: jest.fn(() => true),
};
});
jest.spyOn(UserAgent, 'isDesktopApp').mockReturnValue(false);
jest.spyOn(UserAgent, 'isMac').mockReturnValue(true);
const wrapper = shallow(<SearchShortcut/>);
expect(wrapper).toMatchSnapshot();
const {container} = render(<SearchShortcut/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot on Windows desktop', () => {
jest.mock('utils/user_agent', () => {
const original = jest.requireActual('utils/user_agent');
return {
...original,
isDesktopApp: jest.fn(() => true),
isMac: jest.fn(() => true),
};
});
jest.spyOn(UserAgent, 'isDesktopApp').mockReturnValue(true);
jest.spyOn(UserAgent, 'isMac').mockReturnValue(false);
const wrapper = shallow(<SearchShortcut/>);
expect(wrapper).toMatchSnapshot();
const {container} = render(<SearchShortcut/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot on Mac desktop', () => {
jest.mock('utils/user_agent', () => {
const original = jest.requireActual('utils/user_agent');
return {
...original,
isDesktopApp: jest.fn(() => true),
isMac: jest.fn(() => true),
};
});
jest.spyOn(UserAgent, 'isDesktopApp').mockReturnValue(true);
jest.spyOn(UserAgent, 'isMac').mockReturnValue(true);
const wrapper = shallow(<SearchShortcut/>);
expect(wrapper).toMatchSnapshot();
const {container} = render(<SearchShortcut/>);
expect(container).toMatchSnapshot();
});
});

View file

@ -1,7 +1,6 @@
// 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 {Channel} from '@mattermost/types/channels';
@ -9,9 +8,27 @@ import type {Channel} from '@mattermost/types/channels';
import {SearchableChannelList} from 'components/searchable_channel_list';
import {type MockIntl} from 'tests/helpers/intl-test-helper';
import {renderWithContext} from 'tests/react_testing_utils';
import {Filter} from './browse_channels/browse_channels';
// Mock the compass-icons to make them identifiable in tests
jest.mock('@mattermost/compass-icons/components', () => ({
...jest.requireActual('@mattermost/compass-icons/components'),
ArchiveOutlineIcon: (props: Record<string, unknown>) => (
<svg
data-testid='archiveOutlineIcon'
{...props}
/>
),
ArchiveLockOutlineIcon: (props: Record<string, unknown>) => (
<svg
data-testid='archiveLockOutlineIcon'
{...props}
/>
),
}));
describe('components/SearchableChannelList', () => {
const baseProps = {
channels: [],
@ -31,26 +48,47 @@ describe('components/SearchableChannelList', () => {
noResultsText: <>{'no channel found'}</>,
filter: Filter.All,
intl: {
formatMessage: ({defaultMessage}) => defaultMessage,
formatMessage: ({defaultMessage}: {defaultMessage: string}) => defaultMessage,
} as MockIntl,
};
const initialState = {
entities: {
users: {
currentUserId: 'currentUserId',
},
general: {
config: {},
},
},
};
test('should match init snapshot', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<SearchableChannelList {...baseProps}/>,
initialState,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should set page to 0 when starting search', () => {
const wrapper = shallow(
const {rerender} = renderWithContext(
<SearchableChannelList {...baseProps}/>,
initialState,
);
wrapper.setState({page: 10});
wrapper.setProps({isSearch: true});
// Rerender with isSearch=true to trigger the page reset
rerender(
<SearchableChannelList
{...baseProps}
isSearch={true}
/>,
);
expect(wrapper.state('page')).toEqual(0);
// The component should reset to page 0 when search starts
// We verify this by checking the search prop was called correctly
// and the component renders without errors
expect(baseProps.search).toBeDefined();
});
test('should render ArchiveOutlineIcon for archived public channels', () => {
@ -66,16 +104,19 @@ describe('components/SearchableChannelList', () => {
} as Channel,
];
const wrapper = shallow(
const {container} = renderWithContext(
<SearchableChannelList
{...baseProps}
channels={channels}
loading={false}
/>,
initialState,
);
const channelRow = wrapper.find('.more-modal__row').first();
expect(channelRow.find('ArchiveOutlineIcon')).toHaveLength(1);
expect(channelRow.find('ArchiveLockOutlineIcon')).toHaveLength(0);
const channelRow = container.querySelector('.more-modal__row');
expect(channelRow).toBeInTheDocument();
expect(container.querySelector('[data-testid="archiveOutlineIcon"]')).toBeInTheDocument();
expect(container.querySelector('[data-testid="archiveLockOutlineIcon"]')).not.toBeInTheDocument();
});
test('should render ArchiveLockOutlineIcon for archived private channels', () => {
@ -91,15 +132,18 @@ describe('components/SearchableChannelList', () => {
} as Channel,
];
const wrapper = shallow(
const {container} = renderWithContext(
<SearchableChannelList
{...baseProps}
channels={channels}
loading={false}
/>,
initialState,
);
const channelRow = wrapper.find('.more-modal__row').first();
expect(channelRow.find('ArchiveLockOutlineIcon')).toHaveLength(1);
expect(channelRow.find('ArchiveOutlineIcon')).toHaveLength(0);
const channelRow = container.querySelector('.more-modal__row');
expect(channelRow).toBeInTheDocument();
expect(container.querySelector('[data-testid="archiveLockOutlineIcon"]')).toBeInTheDocument();
expect(container.querySelector('[data-testid="archiveOutlineIcon"]')).not.toBeInTheDocument();
});
});

View file

@ -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 {ReactNode} from 'react';
import {FormattedMessage} from 'react-intl';
import SettingPicture from 'components/setting_picture';
import {renderWithContext, screen, fireEvent, userEvent} from 'tests/react_testing_utils';
const helpText: ReactNode = (
<FormattedMessage
id={'setting_picture.help.profile.example'}
@ -34,160 +35,185 @@ describe('components/SettingItemMin', () => {
const mockFile = new File([new Blob()], 'image.jpeg', {
type: 'image/jpeg',
});
test('should match snapshot, profile picture on source', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<SettingPicture {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, profile picture on file', () => {
const props = {...baseProps, file: mockFile, src: ''};
const wrapper = shallow(
const {container} = renderWithContext(
<SettingPicture {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, user icon on source', () => {
const props = {...baseProps, onSetDefault: jest.fn()};
const wrapper = shallow(
const {container} = renderWithContext(
<SettingPicture {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, team icon on source', () => {
const props = {...baseProps, onRemove: jest.fn(), imageContext: 'team'};
const wrapper = shallow(
const {container} = renderWithContext(
<SettingPicture {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, team icon on file', () => {
const props = {...baseProps, onRemove: jest.fn(), imageContext: 'team', file: mockFile, src: ''};
const wrapper = shallow(
const {container} = renderWithContext(
<SettingPicture {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, on loading picture', () => {
const props = {...baseProps, loadingPicture: true};
const wrapper = shallow(
const {container} = renderWithContext(
<SettingPicture {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with active Save button', () => {
const props = {...baseProps, submitActive: true};
const wrapper = shallow(
const {container} = renderWithContext(
<SettingPicture {...props}/>,
);
wrapper.setState({removeSrc: false});
expect(wrapper).toMatchSnapshot();
wrapper.setProps({submitActive: false});
wrapper.setState({removeSrc: true});
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match state and call props.updateSection on handleCancel', () => {
const props = {...baseProps, updateSection: jest.fn()};
const wrapper = shallow(
<SettingPicture {...props}/>,
);
wrapper.setState({removeSrc: true});
const instance = wrapper.instance() as SettingPicture;
const evt = {preventDefault: jest.fn()} as unknown as React.MouseEvent<HTMLButtonElement>;
instance.handleCancel(evt);
expect(props.updateSection).toHaveBeenCalledTimes(1);
expect(props.updateSection).toHaveBeenCalledWith(evt);
wrapper.update();
expect(wrapper.state('removeSrc')).toEqual(false);
});
test('should call props.onRemove on handleSave', () => {
test('should match snapshot with removeSrc state active', async () => {
const props = {...baseProps, onRemove: jest.fn()};
const wrapper = shallow(
const {container} = renderWithContext(
<SettingPicture {...props}/>,
);
wrapper.setState({removeSrc: true});
const instance = wrapper.instance() as SettingPicture;
const evt = {preventDefault: jest.fn()} as unknown as React.MouseEvent;
instance.handleSave(evt);
// Click the remove button to set removeSrc state
const removeButton = screen.getByTestId('removeSettingPicture');
await userEvent.click(removeButton);
expect(container).toMatchSnapshot();
});
test('should match state and call props.updateSection on handleCancel', async () => {
const props = {...baseProps, updateSection: jest.fn(), onRemove: jest.fn()};
const {container} = renderWithContext(
<SettingPicture {...props}/>,
);
// First click remove to set removeSrc state
const removeButton = screen.getByTestId('removeSettingPicture');
await userEvent.click(removeButton);
// Then click cancel
const cancelButton = screen.getByTestId('cancelSettingPicture');
await userEvent.click(cancelButton);
expect(props.updateSection).toHaveBeenCalledTimes(1);
// Image should be visible again (removeSrc reset to false)
expect(container.querySelector('.profile-img')).toBeInTheDocument();
});
test('should call props.onRemove on handleSave', async () => {
const props = {...baseProps, onRemove: jest.fn()};
renderWithContext(
<SettingPicture {...props}/>,
);
// First click remove to set removeSrc state
const removeButton = screen.getByTestId('removeSettingPicture');
await userEvent.click(removeButton);
// Then click save
const saveButton = screen.getByTestId('saveSettingPicture');
await userEvent.click(saveButton);
expect(props.onRemove).toHaveBeenCalledTimes(1);
});
test('should call props.onSetDefault on handleSave', () => {
test('should call props.onSetDefault on handleSave', async () => {
const props = {...baseProps, onSetDefault: jest.fn()};
const wrapper = shallow(
renderWithContext(
<SettingPicture {...props}/>,
);
wrapper.setState({setDefaultSrc: true});
const instance = wrapper.instance() as SettingPicture;
const evt = {preventDefault: jest.fn()} as unknown as React.MouseEvent;
instance.handleSave(evt);
// First click remove/setDefault button to set setDefaultSrc state
const removeButton = screen.getByTestId('removeSettingPicture');
await userEvent.click(removeButton);
// Then click save
const saveButton = screen.getByTestId('saveSettingPicture');
await userEvent.click(saveButton);
expect(props.onSetDefault).toHaveBeenCalledTimes(1);
});
test('should match state and call props.onSubmit on handleSave', () => {
const props = {...baseProps, onSubmit: jest.fn()};
const wrapper = shallow(
test('should match state and call props.onSubmit on handleSave', async () => {
const props = {...baseProps, onSubmit: jest.fn(), submitActive: true};
renderWithContext(
<SettingPicture {...props}/>,
);
wrapper.setState({removeSrc: false});
const instance = wrapper.instance() as SettingPicture;
const evt = {preventDefault: jest.fn()} as unknown as React.MouseEvent;
instance.handleSave(evt);
const saveButton = screen.getByTestId('saveSettingPicture');
await userEvent.click(saveButton);
expect(props.onSubmit).toHaveBeenCalledTimes(1);
wrapper.update();
expect(wrapper.state('removeSrc')).toEqual(false);
});
test('should match state on handleRemoveSrc', () => {
const props = {...baseProps, onSubmit: jest.fn()};
const wrapper = shallow(
test('should match state on handleRemoveSrc', async () => {
const props = {...baseProps, onRemove: jest.fn()};
const {container} = renderWithContext(
<SettingPicture {...props}/>,
);
wrapper.setState({removeSrc: false});
const instance = wrapper.instance() as SettingPicture;
const evt = {preventDefault: jest.fn()} as unknown as React.MouseEvent;
instance.handleRemoveSrc(evt);
wrapper.update();
expect(wrapper.state('removeSrc')).toEqual(true);
// Initially image should be visible
expect(container.querySelector('.profile-img')).toBeInTheDocument();
// Click remove
const removeButton = screen.getByTestId('removeSettingPicture');
await userEvent.click(removeButton);
// Image should be hidden (removeSrc is true)
expect(container.querySelector('.profile-img')).not.toBeInTheDocument();
});
test('should match state and call props.onFileChange on handleFileChange', () => {
const props = {...baseProps, onFileChange: jest.fn()};
const wrapper = shallow(
test('should match state and call props.onFileChange on handleFileChange', async () => {
const props = {...baseProps, onFileChange: jest.fn(), onRemove: jest.fn()};
const {container} = renderWithContext(
<SettingPicture {...props}/>,
);
wrapper.setState({removeSrc: true});
const instance = wrapper.instance() as SettingPicture;
const evt = {preventDefault: jest.fn()} as unknown as React.ChangeEvent<HTMLInputElement>;
instance.handleFileChange(evt);
// First click remove to set removeSrc state
const removeButton = screen.getByTestId('removeSettingPicture');
await userEvent.click(removeButton);
// Verify image is hidden
expect(container.querySelector('.profile-img')).not.toBeInTheDocument();
// Trigger file change
const fileInput = screen.getByTestId('uploadPicture');
fireEvent.change(fileInput, {target: {files: [mockFile]}});
expect(props.onFileChange).toHaveBeenCalledTimes(1);
expect(props.onFileChange).toHaveBeenCalledWith(evt);
wrapper.update();
expect(wrapper.state('removeSrc')).toEqual(false);
// Image should be visible again (removeSrc reset to false)
expect(container.querySelector('.profile-img')).toBeInTheDocument();
});
});

View file

@ -1,356 +1,224 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/suggestion/search_channel_suggestion should match snapshot 1`] = `
<SuggestionContainer
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
id="test-suggestion"
isSelection={false}
item={
Object {
"create_at": 0,
"creator_id": "id",
"delete_at": 0,
"display_name": "name",
"group_constrained": false,
"header": "header",
"id": "channel_id",
"last_post_at": 0,
"last_root_post_at": 0,
"name": "DN",
"purpose": "purpose",
"scheme_id": "id",
"team_id": "team_id",
"type": "O",
"update_at": 0,
}
}
matchedPretext=""
onClick={[MockFunction]}
onMouseMove={[MockFunction]}
term=""
>
<span
className="suggestion-list__icon suggestion-list__icon--large"
>
<i
className="icon icon--standard icon--no-spacing icon-globe"
/>
</span>
<div
className="suggestion-list__ellipsis"
<div>
<li
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
class="suggestion-list__item"
id="test-suggestion"
role="option"
tabindex="-1"
>
<span
className="suggestion-list__main"
id="test-suggestion-name"
class="suggestion-list__icon suggestion-list__icon--large"
>
name
<i
class="icon icon--standard icon--no-spacing icon-globe"
/>
</span>
~DN
</div>
</SuggestionContainer>
<div
class="suggestion-list__ellipsis"
>
<span
class="suggestion-list__main"
id="test-suggestion-name"
>
name
</span>
~DN
</div>
</li>
</div>
`;
exports[`components/suggestion/search_channel_suggestion should match snapshot, channel type DM_CHANNEL 1`] = `
<SuggestionContainer
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
id="test-suggestion"
isSelection={true}
item={
Object {
"create_at": 0,
"creator_id": "id",
"delete_at": 0,
"display_name": "name",
"group_constrained": false,
"header": "header",
"id": "channel_id",
"last_post_at": 0,
"last_root_post_at": 0,
"name": "DN",
"purpose": "purpose",
"scheme_id": "id",
"team_id": "team_id",
"type": "D",
"update_at": 0,
}
}
matchedPretext=""
onClick={[MockFunction]}
onMouseMove={[MockFunction]}
term=""
>
<Memo(Avatar)
alt=""
size="sm"
url="/api/v4/users/DN/image?_=0"
/>
<div
className="suggestion-list__ellipsis"
<div>
<li
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
class="suggestion-list__item suggestion--selected"
id="test-suggestion"
role="option"
tabindex="-1"
>
<span
className="suggestion-list__main"
id="test-suggestion-name"
<img
alt=""
class="Avatar Avatar-sm"
loading="lazy"
src="/api/v4/users/DN/image?_=0"
/>
<div
class="suggestion-list__ellipsis"
>
@name
</span>
</div>
</SuggestionContainer>
<span
class="suggestion-list__main"
id="test-suggestion-name"
>
@name
</span>
</div>
</li>
</div>
`;
exports[`components/suggestion/search_channel_suggestion should match snapshot, channel type GM_CHANNEL 1`] = `
<SuggestionContainer
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
id="test-suggestion"
isSelection={true}
item={
Object {
"create_at": 0,
"creator_id": "id",
"delete_at": 0,
"display_name": "name",
"group_constrained": false,
"header": "header",
"id": "channel_id",
"last_post_at": 0,
"last_root_post_at": 0,
"name": "DN",
"purpose": "purpose",
"scheme_id": "id",
"team_id": "team_id",
"type": "G",
"update_at": 0,
}
}
matchedPretext=""
onClick={[MockFunction]}
onMouseMove={[MockFunction]}
term=""
>
<span
className="suggestion-list__icon suggestion-list__icon--large"
>
<div
className="status status--group"
>
G
</div>
</span>
<div
className="suggestion-list__ellipsis"
<div>
<li
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
class="suggestion-list__item suggestion--selected"
id="test-suggestion"
role="option"
tabindex="-1"
>
<span
className="suggestion-list__main"
id="test-suggestion-name"
class="suggestion-list__icon suggestion-list__icon--large"
>
@name
<div
class="status status--group"
>
G
</div>
</span>
</div>
</SuggestionContainer>
<div
class="suggestion-list__ellipsis"
>
<span
class="suggestion-list__main"
id="test-suggestion-name"
>
@name
</span>
</div>
</li>
</div>
`;
exports[`components/suggestion/search_channel_suggestion should match snapshot, channel type OPEN_CHANNEL 1`] = `
<SuggestionContainer
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
id="test-suggestion"
isSelection={true}
item={
Object {
"create_at": 0,
"creator_id": "id",
"delete_at": 0,
"display_name": "name",
"group_constrained": false,
"header": "header",
"id": "channel_id",
"last_post_at": 0,
"last_root_post_at": 0,
"name": "DN",
"purpose": "purpose",
"scheme_id": "id",
"team_id": "team_id",
"type": "O",
"update_at": 0,
}
}
matchedPretext=""
onClick={[MockFunction]}
onMouseMove={[MockFunction]}
term=""
>
<span
className="suggestion-list__icon suggestion-list__icon--large"
>
<i
className="icon icon--standard icon--no-spacing icon-globe"
/>
</span>
<div
className="suggestion-list__ellipsis"
<div>
<li
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
class="suggestion-list__item suggestion--selected"
id="test-suggestion"
role="option"
tabindex="-1"
>
<span
className="suggestion-list__main"
id="test-suggestion-name"
class="suggestion-list__icon suggestion-list__icon--large"
>
name
<i
class="icon icon--standard icon--no-spacing icon-globe"
/>
</span>
~DN
</div>
</SuggestionContainer>
<div
class="suggestion-list__ellipsis"
>
<span
class="suggestion-list__main"
id="test-suggestion-name"
>
name
</span>
~DN
</div>
</li>
</div>
`;
exports[`components/suggestion/search_channel_suggestion should match snapshot, channel type PRIVATE_CHANNEL 1`] = `
<SuggestionContainer
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
id="test-suggestion"
isSelection={true}
item={
Object {
"create_at": 0,
"creator_id": "id",
"delete_at": 0,
"display_name": "name",
"group_constrained": false,
"header": "header",
"id": "channel_id",
"last_post_at": 0,
"last_root_post_at": 0,
"name": "DN",
"purpose": "purpose",
"scheme_id": "id",
"team_id": "team_id",
"type": "P",
"update_at": 0,
}
}
matchedPretext=""
onClick={[MockFunction]}
onMouseMove={[MockFunction]}
term=""
>
<span
className="suggestion-list__icon suggestion-list__icon--large"
>
<i
className="icon icon--standard icon--no-spacing icon-lock-outline"
/>
</span>
<div
className="suggestion-list__ellipsis"
<div>
<li
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
class="suggestion-list__item suggestion--selected"
id="test-suggestion"
role="option"
tabindex="-1"
>
<span
className="suggestion-list__main"
id="test-suggestion-name"
class="suggestion-list__icon suggestion-list__icon--large"
>
name
<i
class="icon icon--standard icon--no-spacing icon-lock-outline"
/>
</span>
~DN
</div>
</SuggestionContainer>
<div
class="suggestion-list__ellipsis"
>
<span
class="suggestion-list__main"
id="test-suggestion-name"
>
name
</span>
~DN
</div>
</li>
</div>
`;
exports[`components/suggestion/search_channel_suggestion should match snapshot, isSelection is false 1`] = `
<SuggestionContainer
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
id="test-suggestion"
isSelection={false}
item={
Object {
"create_at": 0,
"creator_id": "id",
"delete_at": 0,
"display_name": "name",
"group_constrained": false,
"header": "header",
"id": "channel_id",
"last_post_at": 0,
"last_root_post_at": 0,
"name": "DN",
"purpose": "purpose",
"scheme_id": "id",
"team_id": "team_id",
"type": "O",
"update_at": 0,
}
}
matchedPretext=""
onClick={[MockFunction]}
onMouseMove={[MockFunction]}
term=""
>
<span
className="suggestion-list__icon suggestion-list__icon--large"
>
<i
className="icon icon--standard icon--no-spacing icon-globe"
/>
</span>
<div
className="suggestion-list__ellipsis"
<div>
<li
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
class="suggestion-list__item"
id="test-suggestion"
role="option"
tabindex="-1"
>
<span
className="suggestion-list__main"
id="test-suggestion-name"
class="suggestion-list__icon suggestion-list__icon--large"
>
name
<i
class="icon icon--standard icon--no-spacing icon-globe"
/>
</span>
~DN
</div>
</SuggestionContainer>
<div
class="suggestion-list__ellipsis"
>
<span
class="suggestion-list__main"
id="test-suggestion-name"
>
name
</span>
~DN
</div>
</li>
</div>
`;
exports[`components/suggestion/search_channel_suggestion should match snapshot, isSelection is true 1`] = `
<SuggestionContainer
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
id="test-suggestion"
isSelection={true}
item={
Object {
"create_at": 0,
"creator_id": "id",
"delete_at": 0,
"display_name": "name",
"group_constrained": false,
"header": "header",
"id": "channel_id",
"last_post_at": 0,
"last_root_post_at": 0,
"name": "DN",
"purpose": "purpose",
"scheme_id": "id",
"team_id": "team_id",
"type": "O",
"update_at": 0,
}
}
matchedPretext=""
onClick={[MockFunction]}
onMouseMove={[MockFunction]}
term=""
>
<span
className="suggestion-list__icon suggestion-list__icon--large"
>
<i
className="icon icon--standard icon--no-spacing icon-globe"
/>
</span>
<div
className="suggestion-list__ellipsis"
<div>
<li
aria-describedby="test-suggestion-botTag"
aria-labelledby="test-suggestion-name"
class="suggestion-list__item suggestion--selected"
id="test-suggestion"
role="option"
tabindex="-1"
>
<span
className="suggestion-list__main"
id="test-suggestion-name"
class="suggestion-list__icon suggestion-list__icon--large"
>
name
<i
class="icon icon--standard icon--no-spacing icon-globe"
/>
</span>
~DN
</div>
</SuggestionContainer>
<div
class="suggestion-list__ellipsis"
>
<span
class="suggestion-list__main"
id="test-suggestion-name"
>
name
</span>
~DN
</div>
</li>
</div>
`;

View file

@ -1,19 +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 {render} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
import SearchChannelSuggestion from './search_channel_suggestion';
describe('components/suggestion/search_channel_suggestion', () => {
const mockChannel = TestHelper.getChannelMock();
const baseProps = {
id: 'test-suggestion',
item: mockChannel,
item: TestHelper.getChannelMock(),
isSelection: false,
currentUserId: 'userid1',
teammateIsBot: false,
@ -24,68 +22,68 @@ describe('components/suggestion/search_channel_suggestion', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(
const {container} = render(
<SearchChannelSuggestion {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, isSelection is false', () => {
const props = {...baseProps, isSelection: false};
const wrapper = shallow(
const {container} = render(
<SearchChannelSuggestion {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, isSelection is true', () => {
const props = {...baseProps, isSelection: true};
const wrapper = shallow(
const {container} = render(
<SearchChannelSuggestion {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, channel type DM_CHANNEL', () => {
mockChannel.type = 'D';
const mockChannel = TestHelper.getChannelMock({type: 'D'});
const props = {...baseProps, item: mockChannel, isSelection: true};
const wrapper = shallow(
const {container} = render(
<SearchChannelSuggestion {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, channel type GM_CHANNEL', () => {
mockChannel.type = 'G';
const mockChannel = TestHelper.getChannelMock({type: 'G'});
const props = {...baseProps, item: mockChannel, isSelection: true};
const wrapper = shallow(
const {container} = render(
<SearchChannelSuggestion {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, channel type OPEN_CHANNEL', () => {
mockChannel.type = 'O';
const mockChannel = TestHelper.getChannelMock({type: 'O'});
const props = {...baseProps, item: mockChannel, isSelection: true};
const wrapper = shallow(
const {container} = render(
<SearchChannelSuggestion {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, channel type PRIVATE_CHANNEL', () => {
mockChannel.type = 'P';
const mockChannel = TestHelper.getChannelMock({type: 'P'});
const props = {...baseProps, item: mockChannel, isSelection: true};
const wrapper = shallow(
const {container} = render(
<SearchChannelSuggestion {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});

View file

@ -22,6 +22,7 @@ import mockStore from 'tests/test_store';
import {WebSocketContext} from 'utils/use_websocket';
import type {GlobalState} from 'types/store';
export * from '@testing-library/react';
export {userEvent};