|
1 | 1 | import React from 'react'; |
2 | | -import { act } from '@testing-library/react'; |
3 | | -import { mount } from 'enzyme'; |
4 | | -import { testComponentSnapshotsWithFixtures } from '../../../common/testHelpers'; |
| 2 | +import { screen } from '@testing-library/react'; |
| 3 | +import userEvent from '@testing-library/user-event'; |
| 4 | +import '@testing-library/jest-dom'; |
| 5 | +import { rtlHelpers } from '../../../common/rtlTestHelpers'; |
| 6 | + |
5 | 7 | import Editor from '../Editor'; |
6 | 8 | import { editorOptions } from '../Editor.fixtures'; |
7 | 9 |
|
8 | | -const didMountStubs = () => ({ |
9 | | - changeState: jest.fn(), |
10 | | - importFile: jest.fn(), |
11 | | - revertChanges: jest.fn(), |
12 | | - previewTemplate: jest.fn(), |
13 | | - initializeEditor: jest.fn(), |
14 | | -}); |
| 10 | +const { renderWithI18n } = rtlHelpers; |
| 11 | + |
| 12 | +const renderEditor = (props = {}) => { |
| 13 | + const initializeEditor = jest.fn(); |
15 | 14 |
|
16 | | -const fixtures = { |
17 | | - 'renders editor': editorOptions, |
| 15 | + return { |
| 16 | + initializeEditor, |
| 17 | + ...renderWithI18n( |
| 18 | + <Editor {...editorOptions} initializeEditor={initializeEditor} {...props} /> |
| 19 | + ), |
| 20 | + }; |
18 | 21 | }; |
19 | 22 |
|
20 | 23 | describe('Editor', () => { |
21 | | - jest.useFakeTimers(); |
22 | | - describe('rendering', () => |
23 | | - testComponentSnapshotsWithFixtures(Editor, fixtures)); |
24 | | - |
25 | | - describe('triggering', () => { |
26 | | - it('should trigger input view', async () => { |
27 | | - const props = { ...editorOptions, ...didMountStubs() }; |
28 | | - const component = mount(<Editor {...props} />); |
29 | | - await act(async () => jest.advanceTimersByTime(1000)); |
30 | | - expect( |
31 | | - component |
32 | | - .find('li[role="presentation"]') |
33 | | - .at(0) |
34 | | - .hasClass('active') |
35 | | - ).toBe(true); |
| 24 | + it('renders editor tabs', async () => { |
| 25 | + renderEditor(); |
| 26 | + |
| 27 | + expect(await screen.findByRole('tab', { name: 'Editor' })).toBeInTheDocument(); |
| 28 | + expect(screen.getByRole('tab', { name: 'Changes' })).toBeInTheDocument(); |
| 29 | + expect(screen.getByRole('tab', { name: 'Preview' })).toBeInTheDocument(); |
| 30 | + }); |
| 31 | + |
| 32 | + it('calls initializeEditor on mount', async () => { |
| 33 | + const { initializeEditor } = renderEditor(); |
| 34 | + |
| 35 | + await screen.findByRole('tab', { name: 'Editor' }); |
| 36 | + |
| 37 | + expect(initializeEditor).toHaveBeenCalledTimes(1); |
| 38 | + expect(initializeEditor).toHaveBeenCalledWith( |
| 39 | + expect.objectContaining({ |
| 40 | + selectedView: editorOptions.selectedView, |
| 41 | + template: editorOptions.data.template, |
| 42 | + }) |
| 43 | + ); |
| 44 | + }); |
| 45 | + |
| 46 | + it('selects the input tab by default', async () => { |
| 47 | + renderEditor({ selectedView: 'input' }); |
| 48 | + |
| 49 | + expect(await screen.findByRole('tab', { name: 'Editor' })).toHaveAttribute( |
| 50 | + 'aria-selected', |
| 51 | + 'true' |
| 52 | + ); |
| 53 | + }); |
| 54 | + |
| 55 | + it('selects the diff tab when diff view is active', async () => { |
| 56 | + renderEditor({ selectedView: 'diff' }); |
| 57 | + |
| 58 | + expect(await screen.findByRole('tab', { name: 'Changes' })).toHaveAttribute( |
| 59 | + 'aria-selected', |
| 60 | + 'true' |
| 61 | + ); |
| 62 | + }); |
| 63 | + |
| 64 | + it('selects the preview tab when preview view is active', async () => { |
| 65 | + renderEditor({ selectedView: 'preview', isRendering: true }); |
| 66 | + |
| 67 | + expect(await screen.findByRole('tab', { name: 'Preview' })).toHaveAttribute( |
| 68 | + 'aria-selected', |
| 69 | + 'true' |
| 70 | + ); |
| 71 | + }); |
| 72 | + |
| 73 | + it('shows the diff table when diff view is active', async () => { |
| 74 | + renderEditor({ |
| 75 | + selectedView: 'diff', |
| 76 | + data: { ...editorOptions.data, template: 'old template' }, |
| 77 | + value: 'new value', |
36 | 78 | }); |
37 | | - it('should trigger input view with no template', async () => { |
38 | | - const props = { |
39 | | - ...editorOptions, |
40 | | - ...didMountStubs(), |
41 | | - data: { ...editorOptions.data, template: null }, |
42 | | - }; |
43 | | - const component = mount(<Editor {...props} />); |
44 | | - await act(async () => jest.advanceTimersByTime(1000)); |
45 | | - expect(component.props().template).toBe('<? />'); |
| 79 | + |
| 80 | + expect(await screen.findByRole('tab', { name: 'Changes' })).toHaveAttribute( |
| 81 | + 'aria-selected', |
| 82 | + 'true' |
| 83 | + ); |
| 84 | + expect(screen.getByText('old template')).toBeInTheDocument(); |
| 85 | + expect(screen.getAllByText('new value').length).toBeGreaterThan(0); |
| 86 | + }); |
| 87 | + |
| 88 | + it('shows outdated preview warning', async () => { |
| 89 | + renderEditor({ |
| 90 | + selectedView: 'preview', |
| 91 | + previewResult: 'rendered preview', |
| 92 | + renderedEditorValue: 'old rendered value', |
| 93 | + value: 'current value', |
46 | 94 | }); |
47 | | - it('should trigger diff view', async () => { |
48 | | - const props = { |
49 | | - ...editorOptions, |
50 | | - ...didMountStubs(), |
51 | | - selectedView: 'diff', |
52 | | - }; |
53 | | - const component = mount(<Editor {...props} />); |
54 | | - await act(async () => jest.advanceTimersByTime(1000)); |
55 | | - expect( |
56 | | - component |
57 | | - .find('li[role="presentation"]') |
58 | | - .at(1) |
59 | | - .hasClass('active') |
60 | | - ).toBe(true); |
| 95 | + |
| 96 | + expect(await screen.findByText('Preview is outdated.')).toBeInTheDocument(); |
| 97 | + expect( |
| 98 | + screen.getByRole('button', { name: 'Preview' }) |
| 99 | + ).toBeInTheDocument(); |
| 100 | + }); |
| 101 | + |
| 102 | + it('refreshes preview when outdated preview link is clicked', async () => { |
| 103 | + const previewTemplate = jest.fn(); |
| 104 | + |
| 105 | + renderEditor({ |
| 106 | + previewTemplate, |
| 107 | + selectedView: 'preview', |
| 108 | + previewResult: 'rendered preview', |
| 109 | + renderedEditorValue: 'old rendered value', |
| 110 | + value: 'current value', |
| 111 | + selectedHost: { id: '1', name: 'host1' }, |
| 112 | + data: { |
| 113 | + ...editorOptions.data, |
| 114 | + safemodeRenderPath: '/safemode/path', |
| 115 | + }, |
61 | 116 | }); |
62 | | - it('should trigger preview view', async () => { |
63 | | - const props = { |
64 | | - ...editorOptions, |
65 | | - ...didMountStubs(), |
66 | | - selectedView: 'preview', |
67 | | - isRendering: true, |
68 | | - }; |
69 | | - const wrapper = mount(<Editor {...props} />); |
70 | | - wrapper.find('button.close').simulate('click'); |
71 | | - await act(async () => jest.advanceTimersByTime(1000)); |
72 | | - const component = mount(<Editor {...props} />); |
73 | | - await act(async () => jest.advanceTimersByTime(1000)); |
74 | | - |
75 | | - expect( |
76 | | - component |
77 | | - .find('li[role="presentation"]') |
78 | | - .at(2) |
79 | | - .hasClass('active') |
80 | | - ).toBe(true); |
| 117 | + |
| 118 | + userEvent.click( |
| 119 | + await screen.findByRole('button', { name: 'Preview' }) |
| 120 | + ); |
| 121 | + |
| 122 | + expect(previewTemplate).toHaveBeenCalledWith({ |
| 123 | + host: { id: '1', name: 'host1' }, |
| 124 | + renderPath: '/safemode/path', |
| 125 | + templateKindId: '', |
81 | 126 | }); |
82 | 127 | }); |
83 | | - it('should trigger hidden value editor', async () => { |
84 | | - const props = { |
85 | | - ...editorOptions, |
86 | | - ...didMountStubs(), |
87 | | - selectedView: 'preview', |
88 | | - isRendering: true, |
89 | | - isMasked: true, |
90 | | - }; |
91 | | - const wrapper = mount(<Editor {...props} />); |
92 | | - await act(async () => jest.advanceTimersByTime(1000)); |
93 | | - expect(wrapper.find('.mask-editor').exists()).toBe(true); |
| 128 | + |
| 129 | + it('dismisses the preview error toast', async () => { |
| 130 | + const dismissErrorToast = jest.fn(); |
| 131 | + |
| 132 | + renderEditor({ dismissErrorToast, showError: true, errorText: 'Preview failed' }); |
| 133 | + |
| 134 | + userEvent.click(await screen.findByRole('button', { name: /Close/i })); |
| 135 | + |
| 136 | + expect(dismissErrorToast).toHaveBeenCalledTimes(1); |
| 137 | + }); |
| 138 | + |
| 139 | + it('renders the hidden value textarea when editable', async () => { |
| 140 | + renderEditor({ readOnly: false, value: 'editor value' }); |
| 141 | + |
| 142 | + await screen.findByRole('tab', { name: 'Editor' }); |
| 143 | + |
| 144 | + expect(screen.getByDisplayValue('editor value')).toBeInTheDocument(); |
94 | 145 | }); |
95 | | - it('textarea disappears if readOnly', async () => { |
96 | | - const props = { |
97 | | - ...editorOptions, |
98 | | - ...didMountStubs(), |
99 | | - selectedView: 'input', |
100 | | - }; |
101 | | - const wrapper = mount(<Editor {...props} />); |
102 | | - await act(async () => jest.advanceTimersByTime(1000)); |
103 | | - expect(wrapper.find('textarea.hidden').exists()).toBe(true); |
104 | | - wrapper.setProps({ readOnly: true }); |
105 | | - expect(wrapper.find('textarea.hidden').exists()).toBe(false); |
| 146 | + |
| 147 | + it('hides the value textarea when read only', async () => { |
| 148 | + const initializeEditor = jest.fn(); |
| 149 | + const { rerender } = renderWithI18n( |
| 150 | + <Editor |
| 151 | + {...editorOptions} |
| 152 | + initializeEditor={initializeEditor} |
| 153 | + readOnly={false} |
| 154 | + value="editor value" |
| 155 | + /> |
| 156 | + ); |
| 157 | + |
| 158 | + await screen.findByRole('tab', { name: 'Editor' }); |
| 159 | + expect(screen.getByDisplayValue('editor value')).toBeInTheDocument(); |
| 160 | + |
| 161 | + rerender( |
| 162 | + <Editor |
| 163 | + {...editorOptions} |
| 164 | + initializeEditor={initializeEditor} |
| 165 | + readOnly |
| 166 | + value="editor value" |
| 167 | + /> |
| 168 | + ); |
| 169 | + |
| 170 | + expect(screen.queryByDisplayValue('editor value')).not.toBeInTheDocument(); |
106 | 171 | }); |
107 | 172 | }); |
0 commit comments