diff --git a/webapp/channels/src/components/__snapshots__/autocomplete_selector.test.tsx.snap b/webapp/channels/src/components/__snapshots__/autocomplete_selector.test.tsx.snap new file mode 100644 index 00000000000..e348e63cbcb --- /dev/null +++ b/webapp/channels/src/components/__snapshots__/autocomplete_selector.test.tsx.snap @@ -0,0 +1,70 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`components/widgets/settings/AutocompleteSelector check snapshot with value prop and changing focus 1`] = ` +
+
+ +
+ +
+
+
+`; + +exports[`components/widgets/settings/AutocompleteSelector check snapshot with value prop and changing focus 2`] = ` +
+
+ +
+ +
+
+
+`; + +exports[`components/widgets/settings/AutocompleteSelector render component with required props 1`] = ` +
+
+ +
+ +
+
+
+`; diff --git a/webapp/channels/src/components/actions_menu/__snapshots__/actions_menu_mobile.test.tsx.snap b/webapp/channels/src/components/actions_menu/__snapshots__/actions_menu_mobile.test.tsx.snap index 62e0d1ff30f..78304b130d7 100644 --- a/webapp/channels/src/components/actions_menu/__snapshots__/actions_menu_mobile.test.tsx.snap +++ b/webapp/channels/src/components/actions_menu/__snapshots__/actions_menu_mobile.test.tsx.snap @@ -1,7 +1,3 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`components/actions_menu/ActionsMenu on mobile view should match snapshot 1`] = ` - - - -`; +exports[`components/actions_menu/ActionsMenu on mobile view should match snapshot 1`] = `
`; diff --git a/webapp/channels/src/components/actions_menu/actions_menu_mobile.test.tsx b/webapp/channels/src/components/actions_menu/actions_menu_mobile.test.tsx index 12615165a75..431af622faf 100644 --- a/webapp/channels/src/components/actions_menu/actions_menu_mobile.test.tsx +++ b/webapp/channels/src/components/actions_menu/actions_menu_mobile.test.tsx @@ -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 ActionsMenu from 'components/actions_menu/actions_menu'; import type {Props} from 'components/actions_menu/actions_menu'; +import {renderWithContext} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; jest.mock('utils/utils', () => { @@ -46,10 +46,10 @@ describe('components/actions_menu/ActionsMenu on mobile view', () => { pluginMenuItemComponents: [], }; - const wrapper = shallow( + const {container} = renderWithContext( , ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); }); diff --git a/webapp/channels/src/components/autocomplete_selector.test.tsx b/webapp/channels/src/components/autocomplete_selector.test.tsx index 347e893ea8c..49c9ae9f121 100644 --- a/webapp/channels/src/components/autocomplete_selector.test.tsx +++ b/webapp/channels/src/components/autocomplete_selector.test.tsx @@ -1,14 +1,36 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; +import {act, render, screen, userEvent} from 'tests/react_testing_utils'; + import AutocompleteSelector from './autocomplete_selector'; +let mockOnItemSelected: ((selected: any) => void) | undefined; + +jest.mock('components/suggestion/suggestion_box', () => { + const ReactMod = require('react'); + const MockSuggestionBox = ReactMod.forwardRef(function MockSuggestionBox(props: any, ref: any) { + mockOnItemSelected = props.onItemSelected; + return ( + props.onChange?.({target: e.target})} + onFocus={props.onFocus} + onBlur={props.onBlur} + placeholder={props.placeholder} + /> + ); + }); + return {__esModule: true, default: MockSuggestionBox}; +}); + describe('components/widgets/settings/AutocompleteSelector', () => { test('render component with required props', () => { - const wrapper = shallow( + const {container} = render( { providers={[]} />, ); - expect(wrapper).toMatchInlineSnapshot(` -
- -
- -
-
- `); + expect(container).toMatchSnapshot(); }); - test('check snapshot with value prop and changing focus', () => { - const wrapper = shallow( + test('check snapshot with value prop and changing focus', async () => { + const {container} = render( { />, ); - wrapper.instance().onBlur(); + const input = screen.getByRole('textbox'); - expect(wrapper).toMatchInlineSnapshot(` -
- -
- -
-
- `); + // Initially not focused, shows prop value + expect(input).toHaveValue('value from prop'); + expect(container).toMatchSnapshot(); - wrapper.instance().onChange(({target: {value: 'value from input'} as HTMLInputElement})); - wrapper.instance().onFocus(); + // Focus the input and type a new value + await userEvent.click(input); + await userEvent.clear(input); + await userEvent.type(input, 'value from input'); - expect(wrapper).toMatchInlineSnapshot(` -
- -
- -
-
- `); + expect(input).toHaveValue('value from input'); + expect(container).toMatchSnapshot(); + + // Blur the input - value should revert to prop value + await userEvent.tab(); + expect(input).toHaveValue('value from prop'); }); test('onSelected', () => { const onSelected = jest.fn(); - const wrapper = shallow( + render( { ); const selected = {text: 'sometext', value: 'somevalue', id: '', username: '', display_name: ''}; - wrapper.instance().handleSelected(selected); + act(() => { + mockOnItemSelected!(selected); + }); expect(onSelected).toHaveBeenCalledTimes(1); expect(onSelected).toHaveBeenCalledWith(selected); diff --git a/webapp/channels/src/components/emoji/__snapshots__/emoji_page.test.tsx.snap b/webapp/channels/src/components/emoji/__snapshots__/emoji_page.test.tsx.snap index 8b4c32888ae..aca4c2e5507 100644 --- a/webapp/channels/src/components/emoji/__snapshots__/emoji_page.test.tsx.snap +++ b/webapp/channels/src/components/emoji/__snapshots__/emoji_page.test.tsx.snap @@ -1,43 +1,35 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`EmojiPage should render without crashing 1`] = ` -
+
-

- -

- - + Custom Emoji + +
- - - + + +
+
+
-
`; diff --git a/webapp/channels/src/components/emoji/emoji_page.test.tsx b/webapp/channels/src/components/emoji/emoji_page.test.tsx index 8d3e625ce90..b45f063a7be 100644 --- a/webapp/channels/src/components/emoji/emoji_page.test.tsx +++ b/webapp/channels/src/components/emoji/emoji_page.test.tsx @@ -1,20 +1,26 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import type {ShallowWrapper} from 'enzyme'; -import {shallow} from 'enzyme'; import React from 'react'; -import {Link} from 'react-router-dom'; -import AnyTeamPermissionGate from 'components/permissions_gates/any_team_permission_gate'; +import {renderWithContext, screen} from 'tests/react_testing_utils'; -import EmojiList from './emoji_list'; import EmojiPage from './emoji_page'; jest.mock('utils/utils', () => ({ localizeMessage: jest.fn().mockReturnValue('Custom Emoji'), })); +jest.mock('./emoji_list', () => ({ + __esModule: true, + default: () =>
, +})); + +jest.mock('components/permissions_gates/any_team_permission_gate', () => ({ + __esModule: true, + default: ({children}: {children: React.ReactNode}) =>
{children}
, +})); + describe('EmojiPage', () => { const mockLoadRolesIfNeeded = jest.fn(); const mockScrollToTop = jest.fn(); @@ -30,27 +36,31 @@ describe('EmojiPage', () => { }; it('should render without crashing', () => { - const wrapper: ShallowWrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext(); + expect(container).toMatchSnapshot(); }); it('should render the emoji list and the add button with permission', () => { - const wrapper: ShallowWrapper = shallow(); - expect(wrapper.find(EmojiList).exists()).toBe(true); - expect(wrapper.find(AnyTeamPermissionGate).exists()).toBe(true); - expect(wrapper.find(Link).prop('to')).toBe('/team/emoji/add'); + renderWithContext(); + expect(screen.getByTestId('emoji-list')).toBeInTheDocument(); + expect(screen.getByTestId('permission-gate')).toBeInTheDocument(); + expect(screen.getByRole('link')).toHaveAttribute('href', '/team/emoji/add'); }); it('should not render the add button if permission is not granted', () => { - const wrapper: ShallowWrapper = shallow( - , - ).setProps({teamName: '', actions: {loadRolesIfNeeded: mockLoadRolesIfNeeded}}); - expect(wrapper.find(AnyTeamPermissionGate).exists()).toBe(true); - expect(wrapper.find(Link).exists()).toBe(true); // Update this to match your permission setup + renderWithContext( + , + ); + expect(screen.getByTestId('permission-gate')).toBeInTheDocument(); + expect(screen.getByRole('link')).toBeInTheDocument(); }); it('should render EmojiList component', () => { - const wrapper = shallow(); - expect(wrapper.find(EmojiList).exists()).toBe(true); + renderWithContext(); + expect(screen.getByTestId('emoji-list')).toBeInTheDocument(); }); }); diff --git a/webapp/channels/src/components/emoji_picker/components/__snapshots__/emoji_picker_header.test.tsx.snap b/webapp/channels/src/components/emoji_picker/components/__snapshots__/emoji_picker_header.test.tsx.snap index 0a61752ad1d..665578db51a 100644 --- a/webapp/channels/src/components/emoji_picker/components/__snapshots__/emoji_picker_header.test.tsx.snap +++ b/webapp/channels/src/components/emoji_picker/components/__snapshots__/emoji_picker_header.test.tsx.snap @@ -1,69 +1,59 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`components/emoji_picker/components/EmojiPickerHeader handleEmojiPickerClose, should have called props.handleEmojiPickerClose 1`] = ` -
- + +
`; exports[`components/emoji_picker/components/EmojiPickerHeader should match snapshot, 1`] = ` -
- + +
`; diff --git a/webapp/channels/src/components/emoji_picker/components/emoji_picker_header.test.tsx b/webapp/channels/src/components/emoji_picker/components/emoji_picker_header.test.tsx index 82ec393855e..fa70cb2d9b3 100644 --- a/webapp/channels/src/components/emoji_picker/components/emoji_picker_header.test.tsx +++ b/webapp/channels/src/components/emoji_picker/components/emoji_picker_header.test.tsx @@ -1,35 +1,36 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; import EmojiPickerHeader from 'components/emoji_picker/components/emoji_picker_header'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; + describe('components/emoji_picker/components/EmojiPickerHeader', () => { test('should match snapshot, ', () => { const props = { handleEmojiPickerClose: jest.fn(), }; - const wrapper = shallow( + const {container} = renderWithContext( , ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); - test('handleEmojiPickerClose, should have called props.handleEmojiPickerClose', () => { + test('handleEmojiPickerClose, should have called props.handleEmojiPickerClose', async () => { const props = { handleEmojiPickerClose: jest.fn(), }; - const wrapper = shallow( + const {container} = renderWithContext( , ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); - wrapper.find('button').first().simulate('click'); + await userEvent.click(screen.getAllByRole('button')[0]); expect(props.handleEmojiPickerClose).toHaveBeenCalledTimes(1); }); }); diff --git a/webapp/channels/src/components/global_header/left_controls/product_menu/__snapshots__/product_menu.test.tsx.snap b/webapp/channels/src/components/global_header/left_controls/product_menu/__snapshots__/product_menu.test.tsx.snap index 1ef853dae2d..60611824377 100644 --- a/webapp/channels/src/components/global_header/left_controls/product_menu/__snapshots__/product_menu.test.tsx.snap +++ b/webapp/channels/src/components/global_header/left_controls/product_menu/__snapshots__/product_menu.test.tsx.snap @@ -2,518 +2,773 @@ exports[`components/global/product_switcher should have an active button state when the switcher menu is open 1`] = `
- - + +
`; exports[`components/global/product_switcher should match snapshot 1`] = `
- - + +
`; exports[`components/global/product_switcher should match snapshot for Entry license 1`] = `
- - + +
`; exports[`components/global/product_switcher should match snapshot with product switcher menu 1`] = `
- - + +
`; exports[`components/global/product_switcher should match snapshot without license 1`] = `
- - + +
`; exports[`components/global/product_switcher should render once when there are no top level products available 1`] = `
- - + +
`; exports[`components/global/product_switcher should render the correct amount of times when there are products available 1`] = `
- - + +
`; diff --git a/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding/__snapshots__/product_branding.test.tsx.snap b/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding/__snapshots__/product_branding.test.tsx.snap index 1364bd6e568..aa62439c24d 100644 --- a/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding/__snapshots__/product_branding.test.tsx.snap +++ b/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding/__snapshots__/product_branding.test.tsx.snap @@ -1,97 +1,126 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`components/ProductBranding should fallback to ProductChannelsIcon when string icon name is not found in glyphMap 1`] = ` - - -

+ - InvalidProduct -

- - InvalidProduct - -
+ +

+ InvalidProduct +

+ + InvalidProduct + + + `; exports[`components/ProductBranding should render a React element icon when switcherIcon is a React node 1`] = ` - - + - - -

- CustomProduct -

- - CustomProduct - -
+ + + +

+ CustomProduct +

+ + CustomProduct + + + `; exports[`components/ProductBranding should show correct icon glyph when we are on Boards 1`] = ` - - -

+ - Boards -

- - Boards - -
+ +

+ Boards +

+ + Boards + + + `; exports[`components/ProductBranding should show correct icon glyph when we are on Channels 1`] = ` - - -

+ - Channels -

- - Channels - -
+ +

+ Channels +

+ + Channels + + + `; exports[`components/ProductBranding should show correct icon glyph when we are on Playbooks 1`] = ` - - -

+ - Playbooks -

- - Playbooks - -
+ +

+ Playbooks +

+ + Playbooks + + + `; diff --git a/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding/product_branding.test.tsx b/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding/product_branding.test.tsx index 000ea61192e..3d4ed4bedda 100644 --- a/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding/product_branding.test.tsx +++ b/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding/product_branding.test.tsx @@ -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 {renderWithContext, screen} from 'tests/react_testing_utils'; import {TopLevelProducts} from 'utils/constants'; import * as productUtils from 'utils/products'; import {TestHelper} from 'utils/test_helper'; @@ -12,37 +12,50 @@ import type {ProductComponent} from 'types/store/plugins'; import ProductBranding from './product_branding'; +jest.mock('@mattermost/compass-icons/components', () => { + const actual = jest.requireActual('@mattermost/compass-icons/components'); + return { + ...actual, + ProductChannelsIcon: (props: any) => ( + + ), + }; +}); + describe('components/ProductBranding', () => { test('should show correct icon glyph when we are on Channels', () => { const currentProductSpy = jest.spyOn(productUtils, 'useCurrentProduct'); currentProductSpy.mockReturnValue(null); - const wrapper = shallow( + const {container} = renderWithContext( , ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); test('should show correct icon glyph when we are on Playbooks', () => { const currentProductSpy = jest.spyOn(productUtils, 'useCurrentProduct'); currentProductSpy.mockReturnValue(TestHelper.makeProduct(TopLevelProducts.PLAYBOOKS)); - const wrapper = shallow( + const {container} = renderWithContext( , ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); test('should show correct icon glyph when we are on Boards', () => { const currentProductSpy = jest.spyOn(productUtils, 'useCurrentProduct'); currentProductSpy.mockReturnValue(TestHelper.makeProduct(TopLevelProducts.BOARDS)); - const wrapper = shallow( + const {container} = renderWithContext( , ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); test('should render a React element icon when switcherIcon is a React node', () => { @@ -62,12 +75,12 @@ describe('components/ProductBranding', () => { }; currentProductSpy.mockReturnValue(productWithReactIcon); - const wrapper = shallow( + const {container} = renderWithContext( , ); - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('[data-testid="custom-icon"]').exists()).toBe(true); + expect(container).toMatchSnapshot(); + expect(screen.getByTestId('custom-icon')).toBeInTheDocument(); }); test('should fallback to ProductChannelsIcon when string icon name is not found in glyphMap', () => { @@ -78,11 +91,11 @@ describe('components/ProductBranding', () => { }; currentProductSpy.mockReturnValue(productWithInvalidIcon); - const wrapper = shallow( + const {container} = renderWithContext( , ); - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('ProductChannelsIcon').exists()).toBe(true); + expect(container).toMatchSnapshot(); + expect(screen.getByTestId('ProductChannelsIcon')).toBeInTheDocument(); }); }); diff --git a/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu.test.tsx b/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu.test.tsx index 38203b9ebc2..82488158253 100644 --- a/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu.test.tsx +++ b/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu.test.tsx @@ -1,28 +1,44 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; -import * as reactRedux from 'react-redux'; -import mockStore from 'tests/test_store'; +import {renderWithContext, screen} from 'tests/react_testing_utils'; import {TopLevelProducts} from 'utils/constants'; import * as productUtils from 'utils/products'; import {TestHelper} from 'utils/test_helper'; -import ProductBranding from './product_branding'; -import ProductBrandingFreeEdition from './product_branding_team_edition'; -import ProductMenu, {ProductMenuButton, ProductMenuContainer} from './product_menu'; -import ProductMenuItem from './product_menu_item'; -import ProductMenuList from './product_menu_list'; +import ProductMenu from './product_menu'; + +jest.mock('./product_branding', () => { + return function MockProductBranding() { + return
; + }; +}); + +jest.mock('./product_branding_team_edition', () => { + return function MockProductBrandingFreeEdition() { + return
; + }; +}); + +jest.mock('./product_menu_list', () => { + return function MockProductMenuList() { + return
; + }; +}); + +jest.mock('components/onboarding_tasks', () => ({ + OnboardingTaskCategory: 'onboardingTask', + OnboardingTasksName: {VISIT_SYSTEM_CONSOLE: 'visit_system_console'}, + TaskNameMapToSteps: {visit_system_console: {FINISHED: 999}}, + useHandleOnBoardingTaskData: () => jest.fn(), +})); const spyProduct = jest.spyOn(productUtils, 'useCurrentProductId'); spyProduct.mockReturnValue(null); describe('components/global/product_switcher', () => { - const useDispatchMock = jest.spyOn(reactRedux, 'useDispatch'); - const useSelectorMock = jest.spyOn(reactRedux, 'useSelector'); - beforeEach(() => { const products = [ TestHelper.makeProduct(TopLevelProducts.BOARDS), @@ -30,101 +46,90 @@ describe('components/global/product_switcher', () => { ]; const spyProducts = jest.spyOn(productUtils, 'useProducts'); spyProducts.mockReturnValue(products); - useDispatchMock.mockClear(); - useSelectorMock.mockClear(); }); - it('should match snapshot', () => { - const state = { - views: { - productMenu: { - switcherOpen: false, + const baseState = { + entities: { + general: { + license: { + IsLicensed: 'true', }, }, - }; - useSelectorMock.mockReturnValue(true); - useSelectorMock.mockReturnValueOnce(true); - useSelectorMock.mockReturnValueOnce({IsLicensed: 'true'}); - const store = mockStore(state); - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - const wrapper = shallow(, { - wrappingComponent: reactRedux.Provider, - wrappingComponentProps: {store}, - }); + }, + views: { + productMenu: { + switcherOpen: false, + }, + }, + }; - expect(wrapper).toMatchSnapshot(); + it('should match snapshot', () => { + const {container} = renderWithContext( + , + baseState, + ); + + expect(container).toMatchSnapshot(); }); it('should match snapshot without license', () => { const state = { - views: { - productMenu: { - switcherOpen: false, + ...baseState, + entities: { + ...baseState.entities, + general: { + ...baseState.entities.general, + license: { + IsLicensed: 'false', + }, }, }, }; - useSelectorMock.mockReturnValue(true); - useSelectorMock.mockReturnValueOnce(true); - useSelectorMock.mockReturnValueOnce({IsLicensed: 'false'}); - const store = mockStore(state); - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - const wrapper = shallow(, { - wrappingComponent: reactRedux.Provider, - wrappingComponentProps: {store}, - }); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext( + , + state, + ); + + expect(container).toMatchSnapshot(); }); it('should render once when there are no top level products available', () => { const state = { - users: { - currentUserId: 'test_id', - }, + ...baseState, views: { + ...baseState.views, productMenu: { switcherOpen: true, }, }, }; - const store = mockStore(state); - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - useSelectorMock.mockReturnValue(true); - useSelectorMock.mockReturnValueOnce(true); - useSelectorMock.mockReturnValueOnce({IsLicensed: 'true'}); - const wrapper = shallow(, { - wrappingComponent: reactRedux.Provider, - wrappingComponentProps: {store}, - }); + + const {container} = renderWithContext( + , + state, + ); const spyProducts = jest.spyOn(productUtils, 'useProducts'); - spyProducts.mockReturnValue([]); - expect(wrapper.find(ProductMenuItem).at(0)).toHaveLength(1); - expect(wrapper).toMatchSnapshot(); + + const menuItems = screen.getAllByRole('menuitem'); + expect(menuItems.length).toBeGreaterThanOrEqual(1); + expect(menuItems.at(0)).toBeDefined(); + expect(container).toMatchSnapshot(); }); it('should render the correct amount of times when there are products available', () => { const state = { + ...baseState, views: { + ...baseState.views, productMenu: { switcherOpen: true, }, }, }; - const store = mockStore(state); - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - useSelectorMock.mockReturnValue(true); - useSelectorMock.mockReturnValueOnce(true); - useSelectorMock.mockReturnValueOnce({IsLicensed: 'true'}); - const wrapper = shallow(, { - wrappingComponent: reactRedux.Provider, - wrappingComponentProps: {store}, - }); + const products = [ TestHelper.makeProduct(TopLevelProducts.BOARDS), TestHelper.makeProduct(TopLevelProducts.PLAYBOOKS), @@ -133,172 +138,172 @@ describe('components/global/product_switcher', () => { const spyProducts = jest.spyOn(productUtils, 'useProducts'); spyProducts.mockReturnValue(products); - expect(wrapper.find(ProductMenuItem)).toHaveLength(3); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext( + , + state, + ); + + // Channels + 2 products + expect(screen.getAllByRole('menuitem')).toHaveLength(3); + expect(container).toMatchSnapshot(); }); it('should have an active button state when the switcher menu is open', () => { const state = { + ...baseState, views: { + ...baseState.views, productMenu: { switcherOpen: true, }, }, }; - const store = mockStore(state); - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - useSelectorMock.mockReturnValue(true); - useSelectorMock.mockReturnValueOnce(true); - useSelectorMock.mockReturnValueOnce({IsLicensed: 'true'}); - const wrapper = shallow(, { - wrappingComponent: reactRedux.Provider, - wrappingComponentProps: {store}, - }); - const setState = jest.fn(); - const useStateSpy = jest.spyOn(React, 'useState'); - useStateSpy.mockImplementation(() => [false, setState]); + const {container} = renderWithContext( + , + state, + ); - wrapper.find(ProductMenuContainer).simulate('click'); - expect(wrapper.find(ProductMenuButton).props()['aria-expanded']).toEqual(true); - expect(wrapper).toMatchSnapshot(); + const button = screen.getByRole('button', {name: 'Product switch menu'}); + expect(button).toHaveAttribute('aria-expanded', 'true'); + expect(container).toMatchSnapshot(); }); it('should match snapshot with product switcher menu', () => { const state = { + ...baseState, views: { + ...baseState.views, productMenu: { switcherOpen: true, }, }, }; - const store = mockStore(state); - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - useSelectorMock.mockReturnValue(true); - useSelectorMock.mockReturnValueOnce(true); - useSelectorMock.mockReturnValueOnce({IsLicensed: 'true'}); - const wrapper = shallow(, { - wrappingComponent: reactRedux.Provider, - wrappingComponentProps: {store}, - }); - expect(wrapper.find(ProductMenuList)).toHaveLength(1); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext( + , + state, + ); + + expect(screen.getByTestId('product-menu-list')).toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); it('should render ProductBrandingFreeEdition for Entry license', () => { const state = { - views: { - productMenu: { - switcherOpen: false, + ...baseState, + entities: { + ...baseState.entities, + general: { + ...baseState.entities.general, + license: { + IsLicensed: 'true', + SkuShortName: 'entry', + }, }, }, }; - const store = mockStore(state); - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - useSelectorMock.mockReturnValue(true); - useSelectorMock.mockReturnValueOnce(true); - useSelectorMock.mockReturnValueOnce({IsLicensed: 'true', SkuShortName: 'entry'}); - const wrapper = shallow(, { - wrappingComponent: reactRedux.Provider, - wrappingComponentProps: {store}, - }); - expect(wrapper.find(ProductBrandingFreeEdition)).toHaveLength(1); - expect(wrapper.find(ProductBranding)).toHaveLength(0); + renderWithContext( + , + state, + ); + + expect(screen.getByTestId('product-branding-free-edition')).toBeInTheDocument(); + expect(screen.queryByTestId('product-branding')).not.toBeInTheDocument(); }); it('should render ProductBrandingFreeEdition for unlicensed', () => { const state = { - views: { - productMenu: { - switcherOpen: false, + ...baseState, + entities: { + ...baseState.entities, + general: { + ...baseState.entities.general, + license: { + IsLicensed: 'false', + }, }, }, }; - const store = mockStore(state); - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - useSelectorMock.mockReturnValue(true); - useSelectorMock.mockReturnValueOnce(true); - useSelectorMock.mockReturnValueOnce({IsLicensed: 'false'}); - const wrapper = shallow(, { - wrappingComponent: reactRedux.Provider, - wrappingComponentProps: {store}, - }); - expect(wrapper.find(ProductBrandingFreeEdition)).toHaveLength(1); - expect(wrapper.find(ProductBranding)).toHaveLength(0); + renderWithContext( + , + state, + ); + + expect(screen.getByTestId('product-branding-free-edition')).toBeInTheDocument(); + expect(screen.queryByTestId('product-branding')).not.toBeInTheDocument(); }); it('should render ProductBranding for Professional license', () => { const state = { - views: { - productMenu: { - switcherOpen: false, + ...baseState, + entities: { + ...baseState.entities, + general: { + ...baseState.entities.general, + license: { + IsLicensed: 'true', + SkuShortName: 'professional', + }, }, }, }; - const store = mockStore(state); - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - useSelectorMock.mockReturnValue(true); - useSelectorMock.mockReturnValueOnce(true); - useSelectorMock.mockReturnValueOnce({IsLicensed: 'true', SkuShortName: 'professional'}); - const wrapper = shallow(, { - wrappingComponent: reactRedux.Provider, - wrappingComponentProps: {store}, - }); - expect(wrapper.find(ProductBranding)).toHaveLength(1); - expect(wrapper.find(ProductBrandingFreeEdition)).toHaveLength(0); + renderWithContext( + , + state, + ); + + expect(screen.getByTestId('product-branding')).toBeInTheDocument(); + expect(screen.queryByTestId('product-branding-free-edition')).not.toBeInTheDocument(); }); it('should render ProductBranding for Enterprise license', () => { const state = { - views: { - productMenu: { - switcherOpen: false, + ...baseState, + entities: { + ...baseState.entities, + general: { + ...baseState.entities.general, + license: { + IsLicensed: 'true', + SkuShortName: 'enterprise', + }, }, }, }; - const store = mockStore(state); - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - useSelectorMock.mockReturnValue(true); - useSelectorMock.mockReturnValueOnce(true); - useSelectorMock.mockReturnValueOnce({IsLicensed: 'true', SkuShortName: 'enterprise'}); - const wrapper = shallow(, { - wrappingComponent: reactRedux.Provider, - wrappingComponentProps: {store}, - }); - expect(wrapper.find(ProductBranding)).toHaveLength(1); - expect(wrapper.find(ProductBrandingFreeEdition)).toHaveLength(0); + renderWithContext( + , + state, + ); + + expect(screen.getByTestId('product-branding')).toBeInTheDocument(); + expect(screen.queryByTestId('product-branding-free-edition')).not.toBeInTheDocument(); }); it('should match snapshot for Entry license', () => { const state = { - views: { - productMenu: { - switcherOpen: false, + ...baseState, + entities: { + ...baseState.entities, + general: { + ...baseState.entities.general, + license: { + IsLicensed: 'true', + SkuShortName: 'entry', + }, }, }, }; - const store = mockStore(state); - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - useSelectorMock.mockReturnValue(true); - useSelectorMock.mockReturnValueOnce(true); - useSelectorMock.mockReturnValueOnce({IsLicensed: 'true', SkuShortName: 'entry'}); - const wrapper = shallow(, { - wrappingComponent: reactRedux.Provider, - wrappingComponentProps: {store}, - }); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext( + , + state, + ); + + expect(container).toMatchSnapshot(); }); }); diff --git a/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/__snapshots__/product_menu_list.test.tsx.snap b/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/__snapshots__/product_menu_list.test.tsx.snap index 1896f0b9e91..5a2dfd77c36 100644 --- a/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/__snapshots__/product_menu_list.test.tsx.snap +++ b/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/__snapshots__/product_menu_list.test.tsx.snap @@ -1,677 +1,624 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`components/global/product_switcher_menu should match snapshot with id 1`] = ` - - - +
`; exports[`components/global/product_switcher_menu should match snapshot with most of the thing enabled 1`] = ` - -
- - - + + + + +
-
+
`; exports[`components/global/product_switcher_menu should match userGroups snapshot with EnableCustomGroups config 1`] = ` - - } + `; exports[`components/global/product_switcher_menu should match userGroups snapshot with cloud free 1`] = ` - - } + `; exports[`components/global/product_switcher_menu should match userGroups snapshot with cloud free trial 1`] = ` - - } + `; exports[`components/global/product_switcher_menu should show integrations should show integrations modal 1`] = ` - +
+ + +
-
+
`; diff --git a/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/product_menu_list.test.tsx b/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/product_menu_list.test.tsx index b043e25e8f1..2ddcec4280c 100644 --- a/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/product_menu_list.test.tsx +++ b/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/product_menu_list.test.tsx @@ -1,32 +1,29 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; -import * as reactRedux from 'react-redux'; import type {UserProfile} from '@mattermost/types/users'; +import type {DeepPartial} from '@mattermost/types/utilities'; -import MenuGroup from 'components/widgets/menu/menu_group'; - +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; +import type {GlobalState} from 'types/store'; + import ProductMenuList from './product_menu_list'; import type {Props as ProductMenuListProps} from './product_menu_list'; -jest.mock('react-redux', () => ({ - ...jest.requireActual('react-redux'), - useSelector: jest.fn(), +jest.mock('components/widgets/menu/menu_items/menu_cloud_trial', () => () => null); +jest.mock('components/widgets/menu/menu_items/menu_item_cloud_limit', () => () => null); +jest.mock('components/permissions_gates/system_permission_gate', () => ({children}: {children: React.ReactNode}) => <>{children}); +jest.mock('components/permissions_gates/team_permission_gate', () => ({children}: {children: React.ReactNode}) => <>{children}); +jest.mock('components/onboarding_tasks', () => ({ + VisitSystemConsoleTour: () => null, })); +jest.mock('components/widgets/menu/menu_items/restricted_indicator', () => () =>
); describe('components/global/product_switcher_menu', () => { - let useSelectorMock: jest.SpyInstance; - - const getMenuWrapper = (props: ProductMenuListProps) => { - const wrapper = shallow(); - return wrapper.find(MenuGroup).shallow(); - }; - const user = TestHelper.getUserMock({ id: 'test-user-id', username: 'username', @@ -59,15 +56,40 @@ describe('components/global/product_switcher_menu', () => { }, }; - beforeEach(() => { - useSelectorMock = jest.spyOn(reactRedux, 'useSelector'); - useSelectorMock.mockReturnValue(true); - }); + const adminState: DeepPartial = { + entities: { + users: { + currentUserId: 'test-user-id', + profiles: { + 'test-user-id': { + id: 'test-user-id', + username: 'username', + roles: 'system_admin system_user', + } as UserProfile, + }, + }, + }, + }; + + const nonAdminState: DeepPartial = { + entities: { + users: { + currentUserId: 'test-user-id', + profiles: { + 'test-user-id': { + id: 'test-user-id', + username: 'username', + roles: 'system_user', + } as UserProfile, + }, + }, + }, + }; test('should match snapshot with id', () => { const props = {...defaultProps, id: 'product-switcher-menu-test'}; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext(, adminState); + expect(container).toMatchSnapshot(); }); test('should not render if the user is not logged in', () => { @@ -75,8 +97,8 @@ describe('components/global/product_switcher_menu', () => { ...defaultProps, currentUser: undefined as unknown as UserProfile, }; - const wrapper = shallow(); - expect(wrapper.type()).toEqual(null); + const {container} = renderWithContext(, adminState); + expect(container.firstChild).toBeNull(); }); test('should match snapshot with most of the thing enabled', () => { @@ -90,8 +112,8 @@ describe('components/global/product_switcher_menu', () => { canManageIntegrations: true, enablePluginMarketplace: true, }; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext(, adminState); + expect(container).toMatchSnapshot(); }); test('should match userGroups snapshot with cloud free', () => { @@ -101,8 +123,8 @@ describe('components/global/product_switcher_menu', () => { isStarterFree: true, isFreeTrial: false, }; - const wrapper = shallow(); - expect(wrapper.find('#userGroups')).toMatchSnapshot(); + const {container} = renderWithContext(, adminState); + expect(container.querySelector('#userGroups')).toMatchSnapshot(); }); test('should match userGroups snapshot with cloud free trial', () => { @@ -112,8 +134,8 @@ describe('components/global/product_switcher_menu', () => { isStarterFree: false, isFreeTrial: true, }; - const wrapper = shallow(); - expect(wrapper.find('#userGroups')).toMatchSnapshot(); + const {container} = renderWithContext(, adminState); + expect(container.querySelector('#userGroups')).toMatchSnapshot(); }); test('should match userGroups snapshot with EnableCustomGroups config', () => { @@ -123,8 +145,8 @@ describe('components/global/product_switcher_menu', () => { isStarterFree: false, isFreeTrial: false, }; - const wrapper = shallow(); - expect(wrapper.find('#userGroups')).toMatchSnapshot(); + const {container} = renderWithContext(, adminState); + expect(container.querySelector('#userGroups')).toMatchSnapshot(); }); test('user groups button is disabled for free', () => { @@ -134,21 +156,19 @@ describe('components/global/product_switcher_menu', () => { isStarterFree: true, isFreeTrial: false, }; - const wrapper = getMenuWrapper(props); - expect(wrapper.find('#userGroups').prop('disabled')).toBe(true); + const {container} = renderWithContext(, adminState); + expect(container.querySelector('#userGroups button')).toBeDisabled(); }); test('should hide RestrictedIndicator if user is not admin', () => { - useSelectorMock.mockReturnValueOnce(false); - const props = { ...defaultProps, isStarterFree: true, }; - const wrapper = shallow(); + const {container} = renderWithContext(, nonAdminState); - expect(wrapper.find('RestrictedIndicator').exists()).toBe(false); + expect(container.querySelector('[data-testid="RestrictedIndicator"]')).toBeNull(); }); describe('should show integrations', () => { @@ -157,8 +177,8 @@ describe('components/global/product_switcher_menu', () => { ...defaultProps, enableIncomingWebhooks: true, }; - const wrapper = shallow(); - expect(wrapper.find('#integrations').prop('show')).toBe(true); + const {container} = renderWithContext(, adminState); + expect(container.querySelector('#integrations')).not.toBeNull(); }); it('when outgoing webhooks enabled', () => { @@ -166,8 +186,8 @@ describe('components/global/product_switcher_menu', () => { ...defaultProps, enableOutgoingWebhooks: true, }; - const wrapper = shallow(); - expect(wrapper.find('#integrations').prop('show')).toBe(true); + const {container} = renderWithContext(, adminState); + expect(container.querySelector('#integrations')).not.toBeNull(); }); it('when slash commands enabled', () => { @@ -175,8 +195,8 @@ describe('components/global/product_switcher_menu', () => { ...defaultProps, enableCommands: true, }; - const wrapper = getMenuWrapper(props); - expect(wrapper.find('#integrations').prop('show')).toBe(true); + const {container} = renderWithContext(, adminState); + expect(container.querySelector('#integrations')).not.toBeNull(); }); it('when oauth providers enabled', () => { @@ -184,8 +204,8 @@ describe('components/global/product_switcher_menu', () => { ...defaultProps, enableOAuthServiceProvider: true, }; - const wrapper = getMenuWrapper(props); - expect(wrapper.find('#integrations').prop('show')).toBe(true); + const {container} = renderWithContext(, adminState); + expect(container.querySelector('#integrations')).not.toBeNull(); }); it('when can manage system bots', () => { @@ -193,8 +213,8 @@ describe('components/global/product_switcher_menu', () => { ...defaultProps, canManageSystemBots: true, }; - const wrapper = getMenuWrapper(props); - expect(wrapper.find('#integrations').prop('show')).toBe(true); + const {container} = renderWithContext(, adminState); + expect(container.querySelector('#integrations')).not.toBeNull(); }); it('unless cannot manage integrations', () => { @@ -203,18 +223,19 @@ describe('components/global/product_switcher_menu', () => { canManageIntegrations: false, enableCommands: true, }; - const wrapper = getMenuWrapper(props); - expect(wrapper.find('#integrations').prop('show')).toBe(false); + const {container} = renderWithContext(, adminState); + expect(container.querySelector('#integrations')).toBeNull(); }); - it('should show integrations modal', () => { + it('should show integrations modal', async () => { const props = { ...defaultProps, enableIncomingWebhooks: true, + teamName: 'test-team', }; - const wrapper = getMenuWrapper(props); - wrapper.find('#integrations').simulate('click'); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext(, adminState); + await userEvent.click(screen.getByText('Integrations')); + expect(container).toMatchSnapshot(); }); }); }); diff --git a/webapp/channels/src/components/global_header/right_controls/at_mentions_button/__snapshots__/at_mentions_button.test.tsx.snap b/webapp/channels/src/components/global_header/right_controls/at_mentions_button/__snapshots__/at_mentions_button.test.tsx.snap index 974963953e2..a4d45e044e1 100644 --- a/webapp/channels/src/components/global_header/right_controls/at_mentions_button/__snapshots__/at_mentions_button.test.tsx.snap +++ b/webapp/channels/src/components/global_header/right_controls/at_mentions_button/__snapshots__/at_mentions_button.test.tsx.snap @@ -1,39 +1,16 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`components/global/AtMentionsButton should match snapshot 1`] = ` - - - - - } -> - + +
`; diff --git a/webapp/channels/src/components/global_header/right_controls/at_mentions_button/at_mentions_button.test.tsx b/webapp/channels/src/components/global_header/right_controls/at_mentions_button/at_mentions_button.test.tsx index c8a52a16dbc..c4d26b87707 100644 --- a/webapp/channels/src/components/global_header/right_controls/at_mentions_button/at_mentions_button.test.tsx +++ b/webapp/channels/src/components/global_header/right_controls/at_mentions_button/at_mentions_button.test.tsx @@ -1,44 +1,45 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; -import IconButton from 'components/global_header/header_icon_button'; +import {showMentions} from 'actions/views/rhs'; + +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import type {GlobalState} from 'types/store'; import AtMentionsButton from './at_mentions_button'; -const mockDispatch = jest.fn(); -let mockState: GlobalState; - -jest.mock('react-redux', () => ({ - ...jest.requireActual('react-redux') as typeof import('react-redux'), - useSelector: (selector: (state: typeof mockState) => unknown) => selector(mockState), - useDispatch: () => mockDispatch, +jest.mock('actions/views/rhs', () => ({ + closeRightHandSide: jest.fn(() => ({type: 'MOCK_CLOSE_RHS'})), + showMentions: jest.fn(() => ({type: 'MOCK_SHOW_MENTIONS'})), })); describe('components/global/AtMentionsButton', () => { - beforeEach(() => { - mockState = {views: {rhs: {isSidebarOpen: true}}} as GlobalState; - }); + const initialState = { + views: { + rhs: { + isSidebarOpen: true, + }, + }, + } as unknown as GlobalState; test('should match snapshot', () => { - const wrapper = shallow( + const {container} = renderWithContext( , + initialState, ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); - test('should show active mentions', () => { - const wrapper = shallow( + test('should show active mentions', async () => { + renderWithContext( , + initialState, ); - wrapper.find(IconButton).simulate('click', { - preventDefault: jest.fn(), - }); - expect(mockDispatch).toHaveBeenCalledTimes(1); + await userEvent.click(screen.getByRole('button', {name: 'Recent mentions'})); + expect(showMentions).toHaveBeenCalledTimes(1); }); }); diff --git a/webapp/channels/src/components/global_header/right_controls/plan_upgrade_button/plan_upgrade_button.test.tsx b/webapp/channels/src/components/global_header/right_controls/plan_upgrade_button/plan_upgrade_button.test.tsx index c41040ed8c6..321b778c06f 100644 --- a/webapp/channels/src/components/global_header/right_controls/plan_upgrade_button/plan_upgrade_button.test.tsx +++ b/webapp/channels/src/components/global_header/right_controls/plan_upgrade_button/plan_upgrade_button.test.tsx @@ -1,23 +1,26 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {mount} from 'enzyme'; import React from 'react'; -import * as reactRedux from 'react-redux'; import * as cloudActions from 'mattermost-redux/actions/cloud'; -import mockStore from 'tests/test_store'; +import {renderWithContext, screen} from 'tests/react_testing_utils'; import {CloudProducts} from 'utils/constants'; import PlanUpgradeButton from './index'; -describe('components/global/PlanUpgradeButton', () => { - const useDispatchMock = jest.spyOn(reactRedux, 'useDispatch'); +jest.mock('components/common/hooks/useOpenPricingModal', () => ({ + __esModule: true, + default: () => ({openPricingModal: jest.fn(), isAirGapped: false}), +})); +describe('components/global/PlanUpgradeButton', () => { beforeEach(() => { - useDispatchMock.mockClear(); + jest.spyOn(cloudActions, 'getCloudSubscription').mockReturnValue({type: 'MOCK_GET_CLOUD_SUBSCRIPTION'} as any); + jest.spyOn(cloudActions, 'getCloudProducts').mockReturnValue({type: 'MOCK_GET_CLOUD_PRODUCTS'} as any); }); + const initialState = { entities: { general: { @@ -49,28 +52,19 @@ describe('components/global/PlanUpgradeButton', () => { }, }, }; - it('should show Upgrade button in global header for admin users, cloud free subscription', () => { - const state = { - ...initialState, - }; + it('should show Upgrade button in global header for admin users, cloud free subscription', () => { const cloudSubscriptionSpy = jest.spyOn(cloudActions, 'getCloudSubscription'); const cloudProductsSpy = jest.spyOn(cloudActions, 'getCloudProducts'); - const store = mockStore(state); - - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - - const wrapper = mount( - - - , + renderWithContext( + , + initialState, ); expect(cloudSubscriptionSpy).toHaveBeenCalledTimes(1); expect(cloudProductsSpy).toHaveBeenCalledTimes(1); - expect(wrapper.find('#UpgradeButton').exists()).toEqual(true); + expect(screen.getByRole('button', {name: 'View plans'})).toBeInTheDocument(); }); it('should show Upgrade button in global header for admin users, cloud and enterprise trial subscription', () => { @@ -89,18 +83,12 @@ describe('components/global/PlanUpgradeButton', () => { }, }; - const store = mockStore(state); - - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - - const wrapper = mount( - - - , + renderWithContext( + , + state, ); - expect(wrapper.find('#UpgradeButton').exists()).toEqual(true); + expect(screen.getByRole('button', {name: 'View plans'})).toBeInTheDocument(); }); it('should not show for cloud enterprise non-trial', () => { @@ -119,18 +107,12 @@ describe('components/global/PlanUpgradeButton', () => { }, }; - const store = mockStore(state); - - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - - const wrapper = mount( - - - , + renderWithContext( + , + state, ); - expect(wrapper.find('#UpgradeButton').exists()).toEqual(false); + expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument(); }); it('should not show for cloud professional product', () => { @@ -149,18 +131,12 @@ describe('components/global/PlanUpgradeButton', () => { }, }; - const store = mockStore(state); - - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - - const wrapper = mount( - - - , + renderWithContext( + , + state, ); - expect(wrapper.find('#UpgradeButton').exists()).toEqual(false); + expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument(); }); it('should not show Upgrade button in global header for non admin cloud users', () => { @@ -172,18 +148,12 @@ describe('components/global/PlanUpgradeButton', () => { }, }; - const store = mockStore(state); - - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - - const wrapper = mount( - - - , + renderWithContext( + , + state, ); - expect(wrapper.find('#UpgradeButton').exists()).toEqual(false); + expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument(); }); it('should not show Upgrade button in global header for non admin self hosted users', () => { @@ -195,22 +165,16 @@ describe('components/global/PlanUpgradeButton', () => { }, }; state.entities.general.license = { - IsLicensed: 'false', // starter + IsLicensed: 'false', Cloud: 'false', }; - const store = mockStore(state); - - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - - const wrapper = mount( - - - , + renderWithContext( + , + state, ); - expect(wrapper.find('#UpgradeButton').exists()).toEqual(false); + expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument(); }); it('should not show Upgrade button in global header for non enterprise edition self hosted users', () => { @@ -231,18 +195,12 @@ describe('components/global/PlanUpgradeButton', () => { BuildEnterpriseReady: 'false', }; - const store = mockStore(state); - - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - - const wrapper = mount( - - - , + renderWithContext( + , + state, ); - expect(wrapper.find('#UpgradeButton').exists()).toEqual(false); + expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument(); }); it('should NOT show Upgrade button in global header for self hosted non trial and licensed', () => { @@ -256,19 +214,13 @@ describe('components/global/PlanUpgradeButton', () => { const cloudSubscriptionSpy = jest.spyOn(cloudActions, 'getCloudSubscription'); const cloudProductsSpy = jest.spyOn(cloudActions, 'getCloudProducts'); - const store = mockStore(state); - - const dummyDispatch = jest.fn(); - useDispatchMock.mockReturnValue(dummyDispatch); - - const wrapper = mount( - - - , + renderWithContext( + , + state, ); - expect(cloudSubscriptionSpy).toHaveBeenCalledTimes(0); // no calls to cloud endpoints for non cloud + expect(cloudSubscriptionSpy).toHaveBeenCalledTimes(0); expect(cloudProductsSpy).toHaveBeenCalledTimes(0); - expect(wrapper.find('#UpgradeButton').exists()).toEqual(false); + expect(screen.queryByRole('button', {name: 'View plans'})).not.toBeInTheDocument(); }); }); diff --git a/webapp/channels/src/components/global_header/right_controls/saved_posts_button/__snapshots__/saved_posts_button.test.tsx.snap b/webapp/channels/src/components/global_header/right_controls/saved_posts_button/__snapshots__/saved_posts_button.test.tsx.snap index e621f44a94f..dc36dec0986 100644 --- a/webapp/channels/src/components/global_header/right_controls/saved_posts_button/__snapshots__/saved_posts_button.test.tsx.snap +++ b/webapp/channels/src/components/global_header/right_controls/saved_posts_button/__snapshots__/saved_posts_button.test.tsx.snap @@ -1,21 +1,16 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`components/global/AtMentionsButton should match snapshot 1`] = ` - - } -> - + + `; diff --git a/webapp/channels/src/components/global_header/right_controls/saved_posts_button/saved_posts_button.test.tsx b/webapp/channels/src/components/global_header/right_controls/saved_posts_button/saved_posts_button.test.tsx index 714d895bac5..5d240952814 100644 --- a/webapp/channels/src/components/global_header/right_controls/saved_posts_button/saved_posts_button.test.tsx +++ b/webapp/channels/src/components/global_header/right_controls/saved_posts_button/saved_posts_button.test.tsx @@ -1,44 +1,45 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; -import IconButton from 'components/global_header/header_icon_button'; +import {showFlaggedPosts} from 'actions/views/rhs'; + +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import type {GlobalState} from 'types/store'; import SavedPostsButton from './saved_posts_button'; -const mockDispatch = jest.fn(); -let mockState: GlobalState; - -jest.mock('react-redux', () => ({ - ...jest.requireActual('react-redux') as typeof import('react-redux'), - useSelector: (selector: (state: typeof mockState) => unknown) => selector(mockState), - useDispatch: () => mockDispatch, +jest.mock('actions/views/rhs', () => ({ + closeRightHandSide: jest.fn(() => ({type: 'MOCK_CLOSE_RHS'})), + showFlaggedPosts: jest.fn(() => ({type: 'MOCK_SHOW_FLAGGED_POSTS'})), })); describe('components/global/AtMentionsButton', () => { - beforeEach(() => { - mockState = {views: {rhs: {isSidebarOpen: true}}} as GlobalState; - }); + const initialState = { + views: { + rhs: { + isSidebarOpen: true, + }, + }, + } as unknown as GlobalState; test('should match snapshot', () => { - const wrapper = shallow( + const {container} = renderWithContext( , + initialState, ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); - test('should show active mentions', () => { - const wrapper = shallow( + test('should show active mentions', async () => { + renderWithContext( , + initialState, ); - wrapper.find(IconButton).simulate('click', { - preventDefault: jest.fn(), - }); - expect(mockDispatch).toHaveBeenCalledTimes(1); + await userEvent.click(screen.getByRole('button', {name: 'Saved messages'})); + expect(showFlaggedPosts).toHaveBeenCalledTimes(1); }); }); diff --git a/webapp/channels/src/components/interactive_dialog/__snapshots__/dialog_introduction_text.test.tsx.snap b/webapp/channels/src/components/interactive_dialog/__snapshots__/dialog_introduction_text.test.tsx.snap index 9a81fadde2b..715bbe65466 100644 --- a/webapp/channels/src/components/interactive_dialog/__snapshots__/dialog_introduction_text.test.tsx.snap +++ b/webapp/channels/src/components/interactive_dialog/__snapshots__/dialog_introduction_text.test.tsx.snap @@ -1,45 +1,45 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`components/DialogIntroductionText should not fail on empty value 1`] = ` - +
- +
`; exports[`components/DialogIntroductionText should render message with supported values 1`] = ` - +
bold italic link <br/> link target blank

", - } - } id="testsupported" - /> - + > +

+ + bold + + + + italic + + + + link + + <br/> + + link target blank + +

+
+
`; diff --git a/webapp/channels/src/components/interactive_dialog/dialog_element/dialog_element.test.tsx b/webapp/channels/src/components/interactive_dialog/dialog_element/dialog_element.test.tsx index aae931f1a14..f6342f0b267 100644 --- a/webapp/channels/src/components/interactive_dialog/dialog_element/dialog_element.test.tsx +++ b/webapp/channels/src/components/interactive_dialog/dialog_element/dialog_element.test.tsx @@ -1,11 +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 RadioSetting from 'components/widgets/settings/radio_setting'; -import TextSetting from 'components/widgets/settings/text_setting'; +import {render, screen} from 'tests/react_testing_utils'; import DialogElement from './dialog_element'; @@ -23,29 +21,29 @@ describe('components/interactive_dialog/DialogElement', () => { }; it('type textarea', () => { - const wrapper = shallow( + render( , ); - expect(wrapper.find(TextSetting).dive().find('textarea').exists()).toBe(true); + expect(document.querySelector('textarea')).toBeInTheDocument(); }); it('subtype blank', () => { - const wrapper = shallow( + render( , ); - expect(wrapper.find(TextSetting).props().type).toEqual('text'); + expect(screen.getByTestId('testinginput')).toHaveAttribute('type', 'text'); }); describe('subtype number', () => { test('value is 0', () => { - const wrapper = shallow( + render( { value={0} />, ); - expect(wrapper.find(TextSetting).props().value).toEqual(0); + expect(screen.getByTestId('testingnumber')).toHaveValue(0); }); test('value is 123', () => { - const wrapper = shallow( + render( { value={123} />, ); - expect(wrapper.find(TextSetting).props().value).toEqual(123); + expect(screen.getByTestId('testingnumber')).toHaveValue(123); }); }); it('subtype email', () => { - const wrapper = shallow( + render( , ); - expect(wrapper.find(TextSetting).props().type).toEqual('email'); + expect(screen.getByTestId('testingemail')).toHaveAttribute('type', 'email'); }); it('subtype password', () => { - const wrapper = shallow( + render( , ); - expect(wrapper.find(TextSetting).props().type).toEqual('password'); + expect(screen.getByTestId('testingpassword')).toHaveAttribute('type', 'password'); }); describe('radioSetting', () => { @@ -96,7 +94,7 @@ describe('components/interactive_dialog/DialogElement', () => { ]; test('RadioSetting is rendered when type is radio', () => { - const wrapper = shallow( + render( { />, ); - expect(wrapper.find(RadioSetting).exists()).toBe(true); + expect(screen.getAllByRole('radio')).toHaveLength(2); }); test('RadioSetting is rendered when options are null', () => { - const wrapper = shallow( + render( { />, ); - expect(wrapper.find(RadioSetting).exists()).toBe(true); + expect(screen.getByTestId('testing')).toBeInTheDocument(); }); test('RadioSetting is rendered when options are null and value is null', () => { - const wrapper = shallow( + render( { />, ); - expect(wrapper.find(RadioSetting).exists()).toBe(true); + expect(screen.getByTestId('testing')).toBeInTheDocument(); }); test('RadioSetting is rendered when options are null and value is not null', () => { - const wrapper = shallow( + render( { />, ); - expect(wrapper.find(RadioSetting).exists()).toBe(true); + expect(screen.getByTestId('testing')).toBeInTheDocument(); }); test('RadioSetting is rendered when value is not one of the options', () => { - const wrapper = shallow( + render( { />, ); - expect(wrapper.find(RadioSetting).exists()).toBe(true); + expect(screen.getByTestId('testing')).toBeInTheDocument(); }); test('No default value is selected from the radio button list', () => { - const wrapper = shallow( + render( , ); - const instance = wrapper.instance() as DialogElement; - expect(instance.props.value).toBeUndefined(); + const radios = screen.getAllByRole('radio'); + radios.forEach((radio) => { + expect(radio).not.toBeChecked(); + }); }); test('The default value can be specified from the list', () => { - const wrapper = shallow( + render( { value={radioOptions[1].value} />, ); - expect(wrapper.find({options: radioOptions, value: radioOptions[1].value}).exists()).toBe(true); + expect(screen.getByRole('radio', {name: radioOptions[1].text})).toBeChecked(); }); }); }); diff --git a/webapp/channels/src/components/interactive_dialog/dialog_introduction_text.test.tsx b/webapp/channels/src/components/interactive_dialog/dialog_introduction_text.test.tsx index abc736d22af..9e3b36e1c6e 100644 --- a/webapp/channels/src/components/interactive_dialog/dialog_introduction_text.test.tsx +++ b/webapp/channels/src/components/interactive_dialog/dialog_introduction_text.test.tsx @@ -1,9 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {mount} from 'enzyme'; import React from 'react'; +import {render} from 'tests/react_testing_utils'; import EmojiMap from 'utils/emoji_map'; import DialogIntroductionText from './dialog_introduction_text'; @@ -17,8 +17,8 @@ describe('components/DialogIntroductionText', () => { value: '**bold** *italic* [link](https://mattermost.com/)
[link target blank](!https://mattermost.com/)', emojiMap, }; - const wrapper = mount(); - expect(wrapper).toMatchSnapshot(); + const {container} = render(); + expect(container).toMatchSnapshot(); }); test('should not fail on empty value', () => { @@ -27,7 +27,7 @@ describe('components/DialogIntroductionText', () => { value: '', emojiMap, }; - const wrapper = mount(); - expect(wrapper).toMatchSnapshot(); + const {container} = render(); + expect(container).toMatchSnapshot(); }); }); diff --git a/webapp/channels/src/components/learn_more_trial_modal/__snapshots__/learn_more_trial_modal.test.tsx.snap b/webapp/channels/src/components/learn_more_trial_modal/__snapshots__/learn_more_trial_modal.test.tsx.snap index eda3bff2c9f..e2cfeeeb07d 100644 --- a/webapp/channels/src/components/learn_more_trial_modal/__snapshots__/learn_more_trial_modal.test.tsx.snap +++ b/webapp/channels/src/components/learn_more_trial_modal/__snapshots__/learn_more_trial_modal.test.tsx.snap @@ -1,34 +1,1235 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`components/learn_more_trial_modal/learn_more_trial_modal should match snapshot 1`] = ` - - +
+ +
+ 5 of 8 members +
+
+
+
+ 5 results found for your search. +
+
+ +
+
+
- +
`; exports[`components/multiselect/multiselect should match snapshot 1`] = ` - +
- - + + +
+
+
+
+ + label + +
+
+ +
+
+
+ +
+
+
+
+
- + 5 of 8 members
-
+
+
+ 5 results found for your search. +
+
+ +
+
+
- +
`; exports[`components/multiselect/multiselect should match snapshot for page 2 1`] = ` - +
- - + + +
+
+
+
+ + label + +
+
+ +
+
+
+ +
+
+
+
+
- + 3 of 8 members
-
+
+
+ 3 results found for your search. +
+
+ 5 +
+ +
+
+
- +
`; diff --git a/webapp/channels/src/components/multiselect/multiselect.test.tsx b/webapp/channels/src/components/multiselect/multiselect.test.tsx index ee0b3cb4ba1..a22059b53f1 100644 --- a/webapp/channels/src/components/multiselect/multiselect.test.tsx +++ b/webapp/channels/src/components/multiselect/multiselect.test.tsx @@ -1,73 +1,70 @@ // 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 {IntlShape} from 'react-intl'; +import {createIntl} from 'react-intl'; -import {mountWithIntl} from 'tests/helpers/intl-test-helper'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import MultiSelect from './multiselect'; import type {Value} from './multiselect'; -import MultiSelectList from './multiselect_list'; import type {Props as MultiSelectProps} from './multiselect_list'; -const element = () =>
; - describe('components/multiselect/multiselect', () => { + const intl = createIntl({locale: 'en'}); const totalCount = 8; const optionsNumber = 8; - const users = []; + const users: Value[] = []; for (let i = 0; i < optionsNumber; i++) { users.push({id: `${i}`, label: `${i}`, value: `${i}`}); } const baseProps = { - ariaLabelRenderer: element as any, + ariaLabelRenderer: (option: Value) => option?.label ?? '', handleAdd: jest.fn(), handleDelete: jest.fn(), handleInput: jest.fn(), handleSubmit: jest.fn(), - intl: {} as IntlShape, - optionRenderer: element, + intl, + optionRenderer: (option: Value) =>
{option.label}
, options: users, perPage: 5, saving: false, totalCount, users, - valueRenderer: element as any, + valueRenderer: (props: {data: Value}) => {props.data.label}, values: [{id: 'id', label: 'label', value: 'value'}], valueWithImage: false, + focusOnLoad: false, }; test('should match snapshot', () => { - const wrapper = shallow( + const {container} = renderWithContext( , ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); - test('should match snapshot for page 2', () => { - const wrapper = shallow( + test('should match snapshot for page 2', async () => { + const {container} = renderWithContext( , ); - wrapper.find('.filter-control__next').simulate('click'); - wrapper.update(); - expect(wrapper.state('page')).toEqual(1); - expect(wrapper).toMatchSnapshot(); + await userEvent.click(screen.getByText('Next')); + expect(container).toMatchSnapshot(); }); - test('MultiSelectList should match state on next page', () => { + test('MultiSelectList should match state on next page', async () => { const renderOption: MultiSelectProps['optionRenderer'] = (option, isSelected, onAdd, onMouseMove) => { return (

onAdd(option)} onMouseMove={() => onMouseMove(option)} > @@ -80,7 +77,7 @@ describe('components/multiselect/multiselect', () => { return props.data.value; }; - const wrapper = mountWithIntl( + renderWithContext( { />, ); - expect(wrapper.find(MultiSelectList).state('selected')).toEqual(-1); - wrapper.find('.filter-control__next').simulate('click'); - expect(wrapper.find(MultiSelectList).state('selected')).toEqual(0); + // Initially no option should be selected (selected = -1) + expect(document.querySelector('.option--selected')).not.toBeInTheDocument(); + + // Click next page + await userEvent.click(screen.getByText('Next')); + + // After clicking next, the first option on the new page should be selected (selected = 0) + expect(document.querySelector('.option--selected')).toBeInTheDocument(); }); test('MultiSelectList should match snapshot when custom no option message is defined', () => { @@ -100,19 +102,19 @@ describe('components/multiselect/multiselect', () => {

); - const wrapper = shallow( + const {container} = renderWithContext( , ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); - test('Back button should be customizable', () => { + test('Back button should be customizable', async () => { const handleBackButtonClick = jest.fn(); - const wrapper = mountWithIntl( + renderWithContext( { />, ); - const backButton = wrapper.find('div.multi-select__footer button.tertiary-button'); + const backButton = screen.getByRole('button', {name: 'Cancel'}); - backButton.simulate('click'); + await userEvent.click(backButton); - expect(backButton).toHaveLength(1); + expect(backButton).toBeInTheDocument(); expect(handleBackButtonClick).toHaveBeenCalled(); }); diff --git a/webapp/channels/src/components/multiselect/multiselect_list.test.tsx b/webapp/channels/src/components/multiselect/multiselect_list.test.tsx index a9ebf8e3231..3c9b003c8e7 100644 --- a/webapp/channels/src/components/multiselect/multiselect_list.test.tsx +++ b/webapp/channels/src/components/multiselect/multiselect_list.test.tsx @@ -1,18 +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 {fireEvent, renderWithContext} from 'tests/react_testing_utils'; + import type {Value} from './multiselect'; import MultiSelectList from './multiselect_list'; import type {Props as MultiSelectProps} from './multiselect_list'; -const element = () =>
; - describe('components/multiselect/multiselect', () => { const optionsNumber = 8; - const users = []; + const users: Value[] = []; for (let i = 0; i < optionsNumber; i++) { users.push({id: `${i}`, label: `${i}`, value: `${i}`}); } @@ -28,11 +27,11 @@ describe('components/multiselect/multiselect', () => { } as any; const baseProps = { - ariaLabelRenderer: element as any, + ariaLabelRenderer: (() =>
) as any, loading: false, onAdd: jest.fn(), onSelect: jest.fn(), - optionRenderer: element, + optionRenderer: (() =>
) as any, selectedItemRef, options: users, }; @@ -42,7 +41,6 @@ describe('components/multiselect/multiselect', () => { return (

onAdd(option)} onMouseMove={() => onMouseMove(option)} > @@ -51,23 +49,22 @@ describe('components/multiselect/multiselect', () => { ); }; - const wrapper = shallow( + renderWithContext( , ); - (wrapper.instance() as any).listRef = { - current: { - getBoundingClientRect: jest.fn(() => ({ - bottom: 50, - top: 50, - })), - }, - } as any; + const listEl = document.getElementById('multiSelectList')!; + jest.spyOn(listEl, 'getBoundingClientRect').mockReturnValue({ + bottom: 50, + top: 50, + } as DOMRect); + + // fireEvent on document used because userEvent.keyboard requires element focus + fireEvent.keyDown(document, {key: 'ArrowDown'}); - wrapper.setState({selected: 1}); expect(selectedItemRef.current.scrollIntoView).toHaveBeenCalledWith(false); }); @@ -76,7 +73,6 @@ describe('components/multiselect/multiselect', () => { return (

onAdd(option)} onMouseMove={() => onMouseMove(option)} > @@ -85,23 +81,22 @@ describe('components/multiselect/multiselect', () => { ); }; - const wrapper = shallow( + renderWithContext( , ); - (wrapper.instance() as any).listRef = { - current: { - getBoundingClientRect: jest.fn(() => ({ - bottom: 200, - top: 60, - })), - }, - } as any; + const listEl = document.getElementById('multiSelectList')!; + jest.spyOn(listEl, 'getBoundingClientRect').mockReturnValue({ + bottom: 200, + top: 60, + } as DOMRect); + + // fireEvent on document used because userEvent.keyboard requires element focus + fireEvent.keyDown(document, {key: 'ArrowDown'}); - wrapper.setState({selected: 1}); expect(selectedItemRef.current.scrollIntoView).toHaveBeenCalledWith(true); }); }); diff --git a/webapp/channels/src/components/onboarding_tasklist/__snapshots__/onboarding_tasklist_completed.test.tsx.snap b/webapp/channels/src/components/onboarding_tasklist/__snapshots__/onboarding_tasklist_completed.test.tsx.snap index 4c3b701f237..9da2ca7b2f5 100644 --- a/webapp/channels/src/components/onboarding_tasklist/__snapshots__/onboarding_tasklist_completed.test.tsx.snap +++ b/webapp/channels/src/components/onboarding_tasklist/__snapshots__/onboarding_tasklist_completed.test.tsx.snap @@ -1,89 +1,81 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`components/onboarding_tasklist/onboarding_tasklist_completed.tsx should match snapshot 1`] = ` - - +

- - completed tasks image -

- -

- - + completed tasks image +

+ Well done. You’ve completed all of the tasks! +

+ + We hope Mattermost is more familiar now. + + + Interested in our higher-security features? + +
+ Start your free Enterprise trial now! +
+ + Start trial + + +
+ + Now that you’re all set up, + + download our apps. + - - - -
- +
+
+ + By clicking “Start trial”, I agree to the + + Mattermost Software Evaluation Agreement + + , + + privacy policy + + and receiving product emails. - - -
- - - -
-
- - - -
- - - +
+
+
`; diff --git a/webapp/channels/src/components/onboarding_tasklist/onboarding_tasklist_completed.test.tsx b/webapp/channels/src/components/onboarding_tasklist/onboarding_tasklist_completed.test.tsx index bee4f81e9c9..26326621809 100644 --- a/webapp/channels/src/components/onboarding_tasklist/onboarding_tasklist_completed.test.tsx +++ b/webapp/channels/src/components/onboarding_tasklist/onboarding_tasklist_completed.test.tsx @@ -1,21 +1,24 @@ // 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, userEvent} from 'tests/react_testing_utils'; + import Completed from './onboarding_tasklist_completed'; -let mockState: any; -const mockDispatch = jest.fn(); -const dismissMockFn = jest.fn(); - -jest.mock('react-redux', () => ({ - ...jest.requireActual('react-redux') as typeof import('react-redux'), - useSelector: (selector: (state: typeof mockState) => unknown) => selector(mockState), - useDispatch: () => mockDispatch, +jest.mock('mattermost-redux/actions/admin', () => ({ + ...jest.requireActual('mattermost-redux/actions/admin'), + getPrevTrialLicense: () => ({type: 'MOCK_GET_PREV_TRIAL_LICENSE'}), })); +jest.mock('components/common/hooks/useOpenStartTrialFormModal', () => ({ + __esModule: true, + default: () => jest.fn(), +})); + +const dismissMockFn = jest.fn(); + describe('components/onboarding_tasklist/onboarding_tasklist_completed.tsx', () => { const props = { dismissAction: dismissMockFn, @@ -23,51 +26,45 @@ describe('components/onboarding_tasklist/onboarding_tasklist_completed.tsx', () isFirstAdmin: true, }; - beforeEach(() => { - mockState = { - entities: { - admin: { - prevTrialLicense: { - IsLicensed: 'false', - }, - }, - general: { - license: { - IsLicensed: 'false', - }, - }, - cloud: { - subscription: { - product_id: 'prod_professional', - is_free_trial: 'false', - trial_end_at: 1, - }, + const initialState = { + entities: { + admin: { + prevTrialLicense: { + IsLicensed: 'false', }, }, - }; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); + general: { + license: { + IsLicensed: 'false', + }, + }, + cloud: { + subscription: { + product_id: 'prod_professional', + is_free_trial: 'false', + trial_end_at: 1, + }, + }, + }, + }; test('should match snapshot', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext(, initialState); + expect(container).toMatchSnapshot(); }); test('finds the completed subtitle', () => { - const wrapper = shallow(); - expect(wrapper.find('.completed-subtitle')).toHaveLength(1); + const {container} = renderWithContext(, initialState); + expect(container.querySelectorAll('.completed-subtitle')).toHaveLength(1); }); - test('displays the no thanks option to close the onboarding list', () => { - const wrapper = shallow(); - const noThanksLink = wrapper.find('.no-thanks-link'); + test('displays the no thanks option to close the onboarding list', async () => { + const {container} = renderWithContext(, initialState); + const noThanksLink = container.querySelectorAll('.no-thanks-link'); expect(noThanksLink).toHaveLength(1); // calls the dissmiss function on click - noThanksLink.simulate('click'); + await userEvent.click(noThanksLink[0]); expect(dismissMockFn).toHaveBeenCalledTimes(1); }); }); diff --git a/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.test.tsx b/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.test.tsx index bc816eeceb1..98f20c81cc9 100644 --- a/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.test.tsx +++ b/webapp/channels/src/components/onboarding_tasks/onboarding_tasks_manager.test.tsx @@ -1,11 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {mount} from 'enzyme'; import React from 'react'; -import {Provider} from 'react-redux'; -import configureStore from 'store'; +import {renderWithContext, screen} from 'tests/react_testing_utils'; import {useTasksList} from './onboarding_tasks_manager'; @@ -49,49 +47,44 @@ describe('onboarding tasks manager', () => { }; it('Places all the elements (6 ignoring plugins) when user is first admin or admin', () => { - const store = configureStore(initialState); - - const wrapper = mount( - - - , + renderWithContext( + , + initialState, ); - expect(wrapper.find('li')).toHaveLength(6); + expect(screen.getAllByRole('listitem')).toHaveLength(6); // find the visit system console and start_trial - expect(wrapper.findWhere((node) => node.key() === 'visit_system_console')).toHaveLength(1); - expect(wrapper.findWhere((node) => node.key() === 'start_trial')).toHaveLength(1); + expect(screen.getByText('visit_system_console')).toBeInTheDocument(); + expect(screen.getByText('start_trial')).toBeInTheDocument(); }); it('Removes start_trial and visit_system_console when user is end user', () => { const endUserState = {...initialState, entities: {...initialState.entities, users: {...initialState.entities.users, currentUserId: user2}}}; - const store = configureStore(endUserState); - const wrapper = mount( - - - , + renderWithContext( + , + endUserState, ); - expect(wrapper.find('li')).toHaveLength(4); + + expect(screen.getAllByRole('listitem')).toHaveLength(4); // verify visit_system_console and start_trial were removed - expect(wrapper.findWhere((node) => node.key() === 'visit_system_console')).toHaveLength(0); - expect(wrapper.findWhere((node) => node.key() === 'start_trial')).toHaveLength(0); + expect(screen.queryByText('visit_system_console')).not.toBeInTheDocument(); + expect(screen.queryByText('start_trial')).not.toBeInTheDocument(); }); it('Removes invite people task item when user is GUEST user', () => { const endUserState = {...initialState, entities: {...initialState.entities, users: {...initialState.entities.users, currentUserId: user3}}}; - const store = configureStore(endUserState); - const wrapper = mount( - - - , + renderWithContext( + , + endUserState, ); - expect(wrapper.find('li')).toHaveLength(3); + + expect(screen.getAllByRole('listitem')).toHaveLength(3); // verify visit_system_console and start_trial were removed - expect(wrapper.findWhere((node) => node.key() === 'invite_people')).toHaveLength(0); + expect(screen.queryByText('invite_people')).not.toBeInTheDocument(); }); }); diff --git a/webapp/channels/src/components/post_view/post_attachment_opengraph/__snapshots__/post_attachment_opengraph.test.tsx.snap b/webapp/channels/src/components/post_view/post_attachment_opengraph/__snapshots__/post_attachment_opengraph.test.tsx.snap index 1aa65f060c3..9d892fc073f 100644 --- a/webapp/channels/src/components/post_view/post_attachment_opengraph/__snapshots__/post_attachment_opengraph.test.tsx.snap +++ b/webapp/channels/src/components/post_view/post_attachment_opengraph/__snapshots__/post_attachment_opengraph.test.tsx.snap @@ -1,313 +1,151 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`PostAttachmentOpenGraph should match snapshot 1`] = ` - - + - - - +
+ + Mattermost.com + + + Mattermost | Open Source Collaboration for Developers + + + Mattermost is a secure, open source platform for communication, collaboration, and workflow orchestration across tools and teams. + +
+
+
+
+ +
+ Mattermost | Open Source Collaboration for Developers +
+
+
+
+
+
`; exports[`PostAttachmentOpenGraphBody should match snapshot 1`] = ` - +
test-sitename test-title test-description
- +
`; exports[`PostAttachmentOpenGraphBody should match snapshot 2`] = ` - +
test_title test_description
- +
`; exports[`PostAttachmentOpenGraphImage should match snapshot 1`] = ` - - +
- - [Function] - - } - slot2={ - - } - > - + -
- test_image -
- - -
-
- - + + + +
+ test_image +
+
- - + + `; -exports[`PostAttachmentOpenGraphImage should not render when used in Permalink 1`] = `Object {}`; +exports[`PostAttachmentOpenGraphImage should not render when used in Permalink 1`] = `
`; diff --git a/webapp/channels/src/components/post_view/post_attachment_opengraph/post_attachment_opengraph.test.tsx b/webapp/channels/src/components/post_view/post_attachment_opengraph/post_attachment_opengraph.test.tsx index 621bedc8588..ed19cc5ce98 100644 --- a/webapp/channels/src/components/post_view/post_attachment_opengraph/post_attachment_opengraph.test.tsx +++ b/webapp/channels/src/components/post_view/post_attachment_opengraph/post_attachment_opengraph.test.tsx @@ -1,17 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {mount} from 'enzyme'; -import cloneDeep from 'lodash/cloneDeep'; -import set from 'lodash/set'; import React from 'react'; -import {Provider} from 'react-redux'; import type {OpenGraphMetadata, Post} from '@mattermost/types/posts'; import {getPreferenceKey} from 'mattermost-redux/utils/preference_utils'; -import mockStore from 'tests/test_store'; +import {render, renderWithContext} from 'tests/react_testing_utils'; import {Preferences} from 'utils/constants'; import {getBestImage, getIsLargeImage, PostAttachmentOpenGraphImage, PostAttachmentOpenGraphBody} from './post_attachment_opengraph'; @@ -91,18 +87,9 @@ describe('PostAttachmentOpenGraph', () => { const baseProps = { post, - postId: '', - link: 'http://mattermost.com', + postId: 'post_id_1', + link: openGraphData.url, currentUserId: '1234', - openGraphData: { - description: 'description', - images: [{ - secure_url: '', - url: imageUrl, - }], - site_name: 'Mattermost', - title: 'Mattermost Private Cloud Messaging', - }, toggleEmbedVisibility, actions: { editPost: jest.fn(), @@ -110,60 +97,72 @@ describe('PostAttachmentOpenGraph', () => { }; test('should match snapshot', () => { - const store = mockStore(initialState); - - const wrapper = mount( - - - , + const {container} = renderWithContext( + , + initialState, ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); test('should render nothing without any data', () => { - const state = cloneDeep(initialState); - set(state, 'entities.posts.openGraph', {}); - - const store = mockStore(state); - - const wrapper = mount( - - - , + const {container} = renderWithContext( + , + { + ...initialState, + entities: { + ...initialState.entities, + posts: { + ...initialState.entities.posts, + openGraph: {}, + }, + }, + }, ); - expect(wrapper).toEqual({}); + expect(container).toBeEmptyDOMElement(); }); test('should render nothing when link previews are disabled on the server', () => { - const state = cloneDeep(initialState); - set(state, 'entities.config.EnableLinkPreviews', 'false'); - - const store = mockStore(state); - - const wrapper = mount( - - - , + const {container} = renderWithContext( + , + { + ...initialState, + entities: { + ...initialState.entities, + general: { + ...initialState.entities.general, + config: { + ...initialState.entities.general.config, + EnableLinkPreviews: 'false', + }, + }, + }, + }, ); - expect(wrapper).toEqual({}); + expect(container).toBeEmptyDOMElement(); }); test('should render nothing when link previews are disabled by the user', () => { - const state = cloneDeep(initialState); - set(state, `entities.preferences.EnableLinkPreviews["${preferenceKeys.LINK_PREVIEW_DISPLAY}"]`, 'false'); - - const store = mockStore(state); - - const wrapper = mount( - - - , + const {container} = renderWithContext( + , + { + ...initialState, + entities: { + ...initialState.entities, + preferences: { + ...initialState.entities.preferences, + myPreferences: { + ...initialState.entities.preferences.myPreferences, + [preferenceKeys.LINK_PREVIEW_DISPLAY]: {value: 'false'}, + }, + }, + }, + }, ); - expect(wrapper).toEqual({}); + expect(container).toBeEmptyDOMElement(); }); }); @@ -176,62 +175,72 @@ describe('PostAttachmentOpenGraphBody', () => { }; test('should match snapshot', () => { - const wrapper = mount(); + const {container} = render(); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); test('should not render without title', () => { - const props = cloneDeep(baseProps); - set(props, 'title', ''); + const props = { + ...baseProps, + title: '', + }; - const wrapper = mount(); + const {container} = render(); - expect(wrapper).toEqual({}); + expect(container).toBeEmptyDOMElement(); }); test('should add extra class for permalink view', () => { - const props = cloneDeep(baseProps); - set(props, 'isInPermalink', true); + const props = { + ...baseProps, + isInPermalink: true, + }; - const wrapper = mount(); + const {container} = render(); - expect(wrapper.find('.isInPermalink').exists()).toBe(true); - expect(wrapper.find('.sitename').exists()).toBe(false); + expect(container.querySelector('.isInPermalink')).toBeInTheDocument(); + expect(container.querySelector('.sitename')).not.toBeInTheDocument(); }); test('should render without sitename', () => { - const props = cloneDeep(baseProps); - set(props, 'sitename', ''); + const props = { + ...baseProps, + sitename: '', + }; - const wrapper = mount(); + const {container} = render(); - expect(wrapper.find('.sitename').exists()).toBe(false); - expect(wrapper.find('.title').exists()).toBe(true); - expect(wrapper.find('.description').exists()).toBe(true); + expect(container.querySelector('.sitename')).not.toBeInTheDocument(); + expect(container.querySelector('.title')).toBeInTheDocument(); + expect(container.querySelector('.description')).toBeInTheDocument(); }); test('should render without description', () => { - const props = cloneDeep(baseProps); - set(props, 'description', ''); + const props = { + ...baseProps, + description: '', + }; - const wrapper = mount(); + const {container} = render(); - expect(wrapper.find('.sitename').exists()).toBe(true); - expect(wrapper.find('.title').exists()).toBe(true); - expect(wrapper.find('.description').exists()).toBe(false); + expect(container.querySelector('.sitename')).toBeInTheDocument(); + expect(container.querySelector('.title')).toBeInTheDocument(); + expect(container.querySelector('.description')).not.toBeInTheDocument(); }); test('should render with title only', () => { - const props = cloneDeep(baseProps); - set(props, 'sitename', ''); - set(props, 'description', ''); + const props = { + ...baseProps, + sitename: '', + description: '', + }; - const wrapper = mount(); + const {container} = render(); - expect(wrapper.find('.sitename').exists()).toBe(false); - expect(wrapper.find('.title').exists()).toBe(true); - expect(wrapper.find('.description').exists()).toBe(false); + expect(container.querySelector('.sitename')).not.toBeInTheDocument(); + expect(container.querySelector('.title')).toBeInTheDocument(); + expect(container.querySelector('.description')).not.toBeInTheDocument(); }); }); @@ -245,62 +254,57 @@ describe('PostAttachmentOpenGraphImage', () => { }; test('should match snapshot', () => { - const store = mockStore(initialState); - - const wrapper = mount( - - - , + const {container} = renderWithContext( + , + initialState, ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); test('should not render when used in Permalink', () => { - const props = cloneDeep(baseProps); - set(props, 'isInPermalink', true); + const props = { + ...baseProps, + isInPermalink: true, + }; - const store = mockStore(initialState); - - const wrapper = mount( - - - , + const {container} = renderWithContext( + , + initialState, ); - expect(wrapper).toMatchSnapshot({}); + expect(container).toMatchSnapshot(); }); test('should render a large image with toggle', () => { - const store = mockStore(initialState); - - const wrapper = mount( - - - , + const {container} = renderWithContext( + , + initialState, ); - expect(wrapper.find('.PostAttachmentOpenGraph__image').exists()).toBe(true); - expect(wrapper.find('.PostAttachmentOpenGraph__image.large').exists()).toBe(true); - expect(wrapper.find('.PostAttachmentOpenGraph__image .preview-toggle').exists()).toBe(true); + expect(container.querySelector('.PostAttachmentOpenGraph__image')).toBeInTheDocument(); + expect(container.querySelector('.PostAttachmentOpenGraph__image.large')).toBeInTheDocument(); + expect(container.querySelector('.PostAttachmentOpenGraph__image .preview-toggle')).toBeInTheDocument(); }); test('should render a small image without toggle', () => { - const props = cloneDeep(baseProps); - set(props, 'imageMetadata.height', 90); - set(props, 'imageMetadata.width', 120); + const props = { + ...baseProps, + imageMetadata: { + ...baseProps.imageMetadata!, + height: 90, + width: 120, + }, + }; - const store = mockStore(initialState); - - const wrapper = mount( - - - , + const {container} = renderWithContext( + , + initialState, ); - expect(wrapper.find('.PostAttachmentOpenGraph__image').exists()).toBe(true); - expect(wrapper.find('.PostAttachmentOpenGraph__image.large').exists()).toBe(false); - expect(wrapper.find('.PostAttachmentOpenGraph__image .preview-toggle').exists()).toBe(false); + expect(container.querySelector('.PostAttachmentOpenGraph__image')).toBeInTheDocument(); + expect(container.querySelector('.PostAttachmentOpenGraph__image.large')).not.toBeInTheDocument(); + expect(container.querySelector('.PostAttachmentOpenGraph__image .preview-toggle')).not.toBeInTheDocument(); }); }); @@ -313,24 +317,26 @@ describe('PostAttachmentOpenGraphBody', () => { }; test('should match snapshot', () => { - const wrapper = mount( + const {container} = render( , ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); describe('permalink preview', () => { - const props = { - ...baseProps, - isInPermalink: true, - }; + test('should add extra class for permalink view', () => { + const props = { + ...baseProps, + isInPermalink: true, + }; - const wrapper = mount( - , - ); + const {container} = render( + , + ); - expect(wrapper.find('.isInPermalink').exists()).toBe(true); + expect(container.querySelector('.isInPermalink')).toBeInTheDocument(); + }); }); }); diff --git a/webapp/channels/src/components/post_view/post_body_additional_content/__snapshots__/post_body_additional_content.test.tsx.snap b/webapp/channels/src/components/post_view/post_body_additional_content/__snapshots__/post_body_additional_content.test.tsx.snap index c9b4a8aed9b..c0863cf0e20 100644 --- a/webapp/channels/src/components/post_view/post_body_additional_content/__snapshots__/post_body_additional_content.test.tsx.snap +++ b/webapp/channels/src/components/post_view/post_body_additional_content/__snapshots__/post_body_additional_content.test.tsx.snap @@ -2,372 +2,202 @@ exports[`PostBodyAdditionalContent with a YouTube video should not render content when isEmbedVisible is false 1`] = `
-
`; exports[`PostBodyAdditionalContent with a YouTube video should render correctly 1`] = `
-
`; exports[`PostBodyAdditionalContent with a YouTube video should render the toggle after a message containing more than just a link 1`] = `
- - some children - -
`; exports[`PostBodyAdditionalContent with a message attachment should render correctly 1`] = `
- - some children - - +
+ + some children + +
+
`; exports[`PostBodyAdditionalContent with a normal link Should render nothing if the plugin matches but isEmbedVisible is false 1`] = `
- - some children - +
+ + some children + +
`; exports[`PostBodyAdditionalContent with a normal link Should render nothing if the registered plugins don't match 1`] = `
- - some children - +
+ + some children + +
`; exports[`PostBodyAdditionalContent with a normal link Should render the plugin component if it matches and is not toggeable 1`] = `
- - some children - - +
+ + some children + +
+
`; exports[`PostBodyAdditionalContent with a normal link Should render the plugin component if it matches and is toggeable 1`] = `
-
`; exports[`PostBodyAdditionalContent with a permalinklink Render permalink preview 1`] = `
- - some children - - +
+ + some children + +
+
`; exports[`PostBodyAdditionalContent with a permalinklink Render permalink preview with no data 1`] = `
- - some children - +
+ + some children + +
`; exports[`PostBodyAdditionalContent with an image preview should render correctly 1`] = `
-
`; exports[`PostBodyAdditionalContent with an image preview should render the toggle after a message containing more than just a link 1`] = `
- - some children - -
`; exports[`PostBodyAdditionalContent with an opengraph preview should render correctly 1`] = `
- - some children - - +
+ + some children + +
+
`; exports[`PostBodyAdditionalContent with an opengraph preview should render the toggle after a message containing more than just a link 1`] = `
- - some children - - +
+ + some children + +
+
`; diff --git a/webapp/channels/src/components/post_view/post_body_additional_content/post_body_additional_content.test.tsx b/webapp/channels/src/components/post_view/post_body_additional_content/post_body_additional_content.test.tsx index 9243a751bee..953a7daeb4e 100644 --- a/webapp/channels/src/components/post_view/post_body_additional_content/post_body_additional_content.test.tsx +++ b/webapp/channels/src/components/post_view/post_body_additional_content/post_body_additional_content.test.tsx @@ -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 { @@ -13,10 +12,7 @@ import type { import {getEmbedFromMetadata} from 'mattermost-redux/utils/post_utils'; -import MessageAttachmentList from 'components/post_view/message_attachments/message_attachment_list'; -import PostAttachmentOpenGraph from 'components/post_view/post_attachment_opengraph'; -import PostImageComponent from 'components/post_view/post_image'; -import YoutubeVideo from 'components/youtube_video'; +import {render, screen, userEvent} from 'tests/react_testing_utils'; import PostBodyAdditionalContent from './post_body_additional_content'; import type {Props} from './post_body_additional_content'; @@ -29,6 +25,52 @@ jest.mock('mattermost-redux/utils/post_utils', () => { }; }); +jest.mock('components/post_view/post_image', () => ({ + __esModule: true, + default: jest.fn((props: any) => ( +
+ )), +})); + +jest.mock('components/post_view/message_attachments/message_attachment_list', () => ({ + __esModule: true, + default: jest.fn(() => ( +
+ )), +})); + +jest.mock('components/post_view/post_attachment_opengraph', () => ({ + __esModule: true, + default: jest.fn(() => ( +
+ )), +})); + +jest.mock('components/youtube_video', () => { + const MockYoutubeVideo: any = jest.fn(() => ( +
+ )); + MockYoutubeVideo.isYoutubeLink = (link: string) => { + return (/^https?:\/\/((www\.)?youtube\.com|youtu\.be)\/.+/).test(link); + }; + return {__esModule: true, default: MockYoutubeVideo}; +}); + +jest.mock('components/post_view/post_message_preview', () => ({ + __esModule: true, + default: jest.fn(() => ( +
+ )), +})); + +jest.mock('client/web_websocket_client', () => ({ + __esModule: true, + default: {}, +})); + describe('PostBodyAdditionalContent', () => { const baseProps: Props = { children: {'some children'}, @@ -73,11 +115,10 @@ describe('PostBodyAdditionalContent', () => { }; test('should render correctly', () => { - const wrapper = shallow(); + const {container} = render(); - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find(PostImageComponent).exists()).toBe(true); - expect(wrapper.find(PostImageComponent).prop('imageMetadata')).toBe(imageMetadata); + expect(container).toMatchSnapshot(); + expect(screen.getByTestId('post-image')).toBeInTheDocument(); }); test('should render the toggle after a message containing more than just a link', () => { @@ -89,9 +130,9 @@ describe('PostBodyAdditionalContent', () => { }, }; - const wrapper = shallow(); + const {container} = render(); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); test('should not render content when isEmbedVisible is false', () => { @@ -100,9 +141,9 @@ describe('PostBodyAdditionalContent', () => { isEmbedVisible: false, }; - const wrapper = shallow(); + render(); - expect(wrapper.find(PostImageComponent).exists()).toBe(false); + expect(screen.queryByTestId('post-image')).not.toBeInTheDocument(); }); }); @@ -125,11 +166,10 @@ describe('PostBodyAdditionalContent', () => { }; test('should render correctly', () => { - const wrapper = shallow(); + const {container} = render(); - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find(MessageAttachmentList).exists()).toBe(true); - expect(wrapper.find(MessageAttachmentList).prop('attachments')).toBe(attachments); + expect(container).toMatchSnapshot(); + expect(screen.getByTestId('message-attachment-list')).toBeInTheDocument(); }); test('should render content when isEmbedVisible is false', () => { @@ -138,9 +178,9 @@ describe('PostBodyAdditionalContent', () => { isEmbedVisible: false, }; - const wrapper = shallow(); + render(); - expect(wrapper.find(MessageAttachmentList).exists()).toBe(true); + expect(screen.getByTestId('message-attachment-list')).toBeInTheDocument(); }); }); @@ -162,10 +202,10 @@ describe('PostBodyAdditionalContent', () => { }; test('should render correctly', () => { - const wrapper = shallow(); + const {container} = render(); - expect(wrapper.find(PostAttachmentOpenGraph).exists()).toBe(true); - expect(wrapper).toMatchSnapshot(); + expect(screen.getByTestId('post-attachment-opengraph')).toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); test('should render the toggle after a message containing more than just a link', () => { @@ -177,9 +217,9 @@ describe('PostBodyAdditionalContent', () => { }, }; - const wrapper = shallow(); + const {container} = render(); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); test('should render content when isEmbedVisible is false', () => { @@ -188,9 +228,9 @@ describe('PostBodyAdditionalContent', () => { isEmbedVisible: false, }; - const wrapper = shallow(); + render(); - expect(wrapper.find(PostAttachmentOpenGraph).exists()).toBe(true); + expect(screen.getByTestId('post-attachment-opengraph')).toBeInTheDocument(); }); }); @@ -212,10 +252,10 @@ describe('PostBodyAdditionalContent', () => { }; test('should render correctly', () => { - const wrapper = shallow(); + const {container} = render(); - expect(wrapper.find(YoutubeVideo).exists()).toBe(true); - expect(wrapper).toMatchSnapshot(); + expect(screen.getByTestId('youtube-video')).toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); test('should render the toggle after a message containing more than just a link', () => { @@ -227,9 +267,9 @@ describe('PostBodyAdditionalContent', () => { }, }; - const wrapper = shallow(); + const {container} = render(); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); test('should not render content when isEmbedVisible is false', () => { @@ -238,17 +278,17 @@ describe('PostBodyAdditionalContent', () => { isEmbedVisible: false, }; - const wrapper = shallow(); + const {container} = render(); - expect(wrapper.find(YoutubeVideo).exists()).toBe(false); - expect(wrapper).toMatchSnapshot(); + expect(screen.queryByTestId('youtube-video')).not.toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); }); describe('with a normal link', () => { const mp3Url = 'https://example.com/song.mp3'; - const EmbedMP3 = () => <>; + const EmbedMP3 = () =>
; const linkBaseProps = { ...baseProps, @@ -278,9 +318,9 @@ describe('PostBodyAdditionalContent', () => { ], }; - const wrapper = shallow(); - expect(wrapper.find(EmbedMP3).exists()).toBe(false); - expect(wrapper).toMatchSnapshot(); + const {container} = render(); + expect(screen.queryByTestId('embed-mp3')).not.toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); test('Should render the plugin component if it matches and is toggeable', () => { @@ -297,10 +337,10 @@ describe('PostBodyAdditionalContent', () => { ], }; - const wrapper = shallow(); - expect(wrapper.find(EmbedMP3).exists()).toBe(true); - expect(wrapper.find('button.post__embed-visibility').exists()).toBe(true); - expect(wrapper).toMatchSnapshot(); + const {container} = render(); + expect(screen.getByTestId('embed-mp3')).toBeInTheDocument(); + expect(container.querySelector('button.post__embed-visibility')).toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); test('Should render the plugin component if it matches and is not toggeable', () => { @@ -317,10 +357,10 @@ describe('PostBodyAdditionalContent', () => { ], }; - const wrapper = shallow(); - expect(wrapper.find(EmbedMP3).exists()).toBe(true); - expect(wrapper.find('button.post__embed-visibility').exists()).toBe(false); - expect(wrapper).toMatchSnapshot(); + const {container} = render(); + expect(screen.getByTestId('embed-mp3')).toBeInTheDocument(); + expect(container.querySelector('button.post__embed-visibility')).not.toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); test('Should render nothing if the plugin matches but isEmbedVisible is false', () => { @@ -338,16 +378,38 @@ describe('PostBodyAdditionalContent', () => { isEmbedVisible: false, }; - const wrapper = shallow(); - expect(wrapper.find(EmbedMP3).exists()).toBe(false); - expect(wrapper).toMatchSnapshot(); + const {container} = render(); + expect(screen.queryByTestId('embed-mp3')).not.toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); }); - test('should call toggleEmbedVisibility with post id', () => { - const wrapper = shallow(); + test('should call toggleEmbedVisibility with post id', async () => { + const imageUrl = 'https://example.com/image.png'; + const props = { + ...baseProps, + post: { + ...baseProps.post, + message: imageUrl, + metadata: { + embeds: [{ + type: 'image', + url: imageUrl, + }], + images: { + [imageUrl]: {} as PostImage, + }, + emojis: [], + files: [], + reactions: [], + } as PostMetadata, + }, + }; - wrapper.instance().toggleEmbedVisibility(); + render(); + + const toggleButton = screen.getByRole('button', {name: 'Toggle Embed Visibility'}); + await userEvent.click(toggleButton); expect(baseProps.actions.toggleEmbedVisibility).toHaveBeenCalledTimes(1); expect(baseProps.actions.toggleEmbedVisibility).toHaveBeenCalledWith('post_id_1'); @@ -367,8 +429,7 @@ describe('PostBodyAdditionalContent', () => { }, }; - const wrapper = shallow(); - wrapper.instance().getEmbed(); + render(); expect(getEmbedFromMetadata).toHaveBeenCalledWith(metadata); }); @@ -402,8 +463,8 @@ describe('PostBodyAdditionalContent', () => { }; test('Render permalink preview', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const {container} = render(); + expect(container).toMatchSnapshot(); }); test('Render permalink preview with no data', () => { @@ -421,8 +482,8 @@ describe('PostBodyAdditionalContent', () => { }, }; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const {container} = render(); + expect(container).toMatchSnapshot(); }); }); }); diff --git a/webapp/channels/src/components/post_view/reaction/__snapshots__/reaction.test.tsx.snap b/webapp/channels/src/components/post_view/reaction/__snapshots__/reaction.test.tsx.snap index 8708abb312b..88ef56e99a5 100644 --- a/webapp/channels/src/components/post_view/reaction/__snapshots__/reaction.test.tsx.snap +++ b/webapp/channels/src/components/post_view/reaction/__snapshots__/reaction.test.tsx.snap @@ -1,275 +1,195 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`components/post_view/Reaction should apply read-only class if user does not have permission to add reaction 1`] = ` - - - + +
+
`; exports[`components/post_view/Reaction should apply read-only class if user does not have permission to remove reaction 1`] = ` - - - + +
+
`; exports[`components/post_view/Reaction should match snapshot 1`] = ` - - - + +
+
`; exports[`components/post_view/Reaction should match snapshot when a current user reacted to a post 1`] = ` - - - + +
+
`; -exports[`components/post_view/Reaction should return null/empty if no emojiImageUrl 1`] = `""`; +exports[`components/post_view/Reaction should return null/empty if no emojiImageUrl 1`] = `
`; diff --git a/webapp/channels/src/components/post_view/reaction/reaction.test.tsx b/webapp/channels/src/components/post_view/reaction/reaction.test.tsx index 236b1a46959..e7bcd9d9411 100644 --- a/webapp/channels/src/components/post_view/reaction/reaction.test.tsx +++ b/webapp/channels/src/components/post_view/reaction/reaction.test.tsx @@ -1,18 +1,28 @@ // 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 {Reaction as ReactionType} from '@mattermost/types/reactions'; -import {Reaction as ReactionClass} from 'components/post_view/reaction/reaction'; - -import {getIntl} from 'utils/i18n'; +import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; +import ReactionComponent from './reaction'; + +jest.mock('./reaction_tooltip', () => { + return { + __esModule: true, + default: ({children, onShow}: any) => ( +
{children}
+ ), + }; +}); + describe('components/post_view/Reaction', () => { - const intl = getIntl(); const post = TestHelper.getPostMock({ id: 'post_id', }); @@ -43,12 +53,11 @@ describe('components/post_view/Reaction', () => { reactions, emojiImageUrl: 'emoji_image_url', actions, - intl, }; test('should match snapshot', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext(); + expect(container).toMatchSnapshot(); }); test('should match snapshot when a current user reacted to a post', () => { @@ -67,20 +76,20 @@ describe('components/post_view/Reaction', () => { currentUserReacted: true, reactions: newReactions, }; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext(); + expect(container).toMatchSnapshot(); }); test('should return null/empty if no emojiImageUrl', () => { const props = {...baseProps, emojiImageUrl: ''}; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext(); + expect(container).toMatchSnapshot(); }); test('should apply read-only class if user does not have permission to add reaction', () => { const props = {...baseProps, canAddReactions: false}; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext(); + expect(container).toMatchSnapshot(); }); test('should apply read-only class if user does not have permission to remove reaction', () => { @@ -89,13 +98,15 @@ describe('components/post_view/Reaction', () => { canRemoveReactions: false, currentUserReacted: true, }; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); + const {container} = renderWithContext(); + expect(container).toMatchSnapshot(); }); - test('should have called actions.getMissingProfilesByIds when loadMissingProfiles is called', () => { - const wrapper = shallow(); - wrapper.instance().loadMissingProfiles(); + test('should have called actions.getMissingProfilesByIds when loadMissingProfiles is called', async () => { + renderWithContext(); + + const tooltipTrigger = screen.getByTestId('reaction-tooltip'); + await userEvent.hover(tooltipTrigger); expect(actions.getMissingProfilesByIds).toHaveBeenCalledTimes(1); expect(actions.getMissingProfilesByIds).toHaveBeenCalledWith([reactions[0].user_id, reactions[1].user_id]); diff --git a/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap b/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap index 8da7b283925..6ba7955fefe 100644 --- a/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap +++ b/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap @@ -1,83 +1,19 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`components/ReactionList should render nothing when no reactions 1`] = `""`; +exports[`components/ReactionList should render nothing when no reactions 1`] = `
`; exports[`components/ReactionList should render when there are reactions 1`] = ` -
- - +
+
+
+
+
`; diff --git a/webapp/channels/src/components/post_view/reaction_list/reactions_list.test.tsx b/webapp/channels/src/components/post_view/reaction_list/reactions_list.test.tsx index d9c02b1a57b..76bd4fa3f4f 100644 --- a/webapp/channels/src/components/post_view/reaction_list/reactions_list.test.tsx +++ b/webapp/channels/src/components/post_view/reaction_list/reactions_list.test.tsx @@ -1,15 +1,31 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {shallow} from 'enzyme'; import React from 'react'; -import Reaction from 'components/post_view/reaction'; - +import {render, screen} from 'tests/react_testing_utils'; import {TestHelper} from 'utils/test_helper'; import ReactionList from './reaction_list'; +jest.mock('components/post_view/reaction', () => { + return { + __esModule: true, + default: (props: any) => ( +
+ ), + }; +}); + +jest.mock('./add_reaction_button', () => { + return { + __esModule: true, + default: () => ( +
+ ), + }; +}); + describe('components/ReactionList', () => { const reaction = { user_id: '1rj9fokoeffrigu7sk5uc8aiih', @@ -44,19 +60,19 @@ describe('components/ReactionList', () => { reactions: {}, }; - const wrapper = shallow( + const {container} = render( , ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); test('should render when there are reactions', () => { - const wrapper = shallow( + const {container} = render( , ); - expect(wrapper).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); /* @@ -91,14 +107,16 @@ describe('components/ReactionList', () => { }, }; - const wrapper = shallow( + const {rerender} = render( , ); - const firstRender = wrapper.find(Reaction).map((node) => node.key); + const firstRender = screen.getAllByTestId(/^reaction-/).map((el) => el.getAttribute('data-testid')); - wrapper.setProps(propsB); + rerender( + , + ); - const secondRender = wrapper.find(Reaction).map((node) => node.key); + const secondRender = screen.getAllByTestId(/^reaction-/).map((el) => el.getAttribute('data-testid')); expect(firstRender.length).toBe(2); expect(firstRender).toEqual(secondRender); diff --git a/webapp/channels/src/components/suggestion/suggestion_box/suggestion_box.test.tsx b/webapp/channels/src/components/suggestion/suggestion_box/suggestion_box.test.tsx index b317a522335..4e1531bf624 100644 --- a/webapp/channels/src/components/suggestion/suggestion_box/suggestion_box.test.tsx +++ b/webapp/channels/src/components/suggestion/suggestion_box/suggestion_box.test.tsx @@ -127,8 +127,10 @@ describe('SuggestionBox', () => { expect(providerSpy).toHaveBeenCalledTimes(2); }); - expect(screen.queryByRole('listbox')).toBeVisible(); - expect(screen.getByText('Suggestion: testwordstestwords')).toBeVisible(); + await waitFor(() => { + expect(screen.queryByRole('listbox')).toBeVisible(); + expect(screen.getByText('Suggestion: testwordstestwords')).toBeVisible(); + }); // Clearing the textbox hides all suggestions await userEvent.clear(screen.getByPlaceholderText('test input')); diff --git a/webapp/channels/src/components/user_settings/advanced/join_leave_section/__snapshots__/join_leave_section.test.tsx.snap b/webapp/channels/src/components/user_settings/advanced/join_leave_section/__snapshots__/join_leave_section.test.tsx.snap index 59a8fe42fa1..003a19c2c05 100644 --- a/webapp/channels/src/components/user_settings/advanced/join_leave_section/__snapshots__/join_leave_section.test.tsx.snap +++ b/webapp/channels/src/components/user_settings/advanced/join_leave_section/__snapshots__/join_leave_section.test.tsx.snap @@ -1,88 +1,136 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`components/user_settings/advanced/JoinLeaveSection should match snapshot 1`] = ` - - } - updateSection={[Function]} -/> +
+
+
+

+ Enable Join/Leave Messages +

+