Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
420 changes: 219 additions & 201 deletions webpack/assets/javascripts/react_app/components/Editor/Editor.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,107 +1,172 @@
import React from 'react';
import { act } from '@testing-library/react';
import { mount } from 'enzyme';
import { testComponentSnapshotsWithFixtures } from '../../../common/testHelpers';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import { rtlHelpers } from '../../../common/rtlTestHelpers';

import Editor from '../Editor';
import { editorOptions } from '../Editor.fixtures';

const didMountStubs = () => ({
changeState: jest.fn(),
importFile: jest.fn(),
revertChanges: jest.fn(),
previewTemplate: jest.fn(),
initializeEditor: jest.fn(),
});
const { renderWithI18n } = rtlHelpers;

const renderEditor = (props = {}) => {
const initializeEditor = jest.fn();

const fixtures = {
'renders editor': editorOptions,
return {
initializeEditor,
...renderWithI18n(
<Editor {...editorOptions} initializeEditor={initializeEditor} {...props} />
),
};
};

describe('Editor', () => {
jest.useFakeTimers();
describe('rendering', () =>
testComponentSnapshotsWithFixtures(Editor, fixtures));

describe('triggering', () => {
it('should trigger input view', async () => {
const props = { ...editorOptions, ...didMountStubs() };
const component = mount(<Editor {...props} />);
await act(async () => jest.advanceTimersByTime(1000));
expect(
component
.find('li[role="presentation"]')
.at(0)
.hasClass('active')
).toBe(true);
it('renders editor tabs', async () => {
renderEditor();

expect(await screen.findByRole('tab', { name: 'Editor' })).toBeInTheDocument();
expect(screen.getByRole('tab', { name: 'Changes' })).toBeInTheDocument();
expect(screen.getByRole('tab', { name: 'Preview' })).toBeInTheDocument();
});

it('calls initializeEditor on mount', async () => {
const { initializeEditor } = renderEditor();

await screen.findByRole('tab', { name: 'Editor' });

expect(initializeEditor).toHaveBeenCalledTimes(1);
expect(initializeEditor).toHaveBeenCalledWith(
expect.objectContaining({
selectedView: editorOptions.selectedView,
template: editorOptions.data.template,
})
);
});

it('selects the input tab by default', async () => {
renderEditor({ selectedView: 'input' });

expect(await screen.findByRole('tab', { name: 'Editor' })).toHaveAttribute(
'aria-selected',
'true'
);
});

it('selects the diff tab when diff view is active', async () => {
renderEditor({ selectedView: 'diff' });

expect(await screen.findByRole('tab', { name: 'Changes' })).toHaveAttribute(
'aria-selected',
'true'
);
});

it('selects the preview tab when preview view is active', async () => {
renderEditor({ selectedView: 'preview', isRendering: true });

expect(await screen.findByRole('tab', { name: 'Preview' })).toHaveAttribute(
'aria-selected',
'true'
);
});

it('shows the diff table when diff view is active', async () => {
renderEditor({
selectedView: 'diff',
data: { ...editorOptions.data, template: 'old template' },
value: 'new value',
});
it('should trigger input view with no template', async () => {
const props = {
...editorOptions,
...didMountStubs(),
data: { ...editorOptions.data, template: null },
};
const component = mount(<Editor {...props} />);
await act(async () => jest.advanceTimersByTime(1000));
expect(component.props().template).toBe('<? />');

expect(await screen.findByRole('tab', { name: 'Changes' })).toHaveAttribute(
'aria-selected',
'true'
);
expect(screen.getByText('old template')).toBeInTheDocument();
expect(screen.getAllByText('new value').length).toBeGreaterThan(0);
});

it('shows outdated preview warning', async () => {
renderEditor({
selectedView: 'preview',
previewResult: 'rendered preview',
renderedEditorValue: 'old rendered value',
value: 'current value',
});
it('should trigger diff view', async () => {
const props = {
...editorOptions,
...didMountStubs(),
selectedView: 'diff',
};
const component = mount(<Editor {...props} />);
await act(async () => jest.advanceTimersByTime(1000));
expect(
component
.find('li[role="presentation"]')
.at(1)
.hasClass('active')
).toBe(true);

expect(await screen.findByText('Preview is outdated.')).toBeInTheDocument();
expect(
screen.getByRole('button', { name: 'Preview' })
).toBeInTheDocument();
});

it('refreshes preview when outdated preview link is clicked', async () => {
const previewTemplate = jest.fn();

renderEditor({
previewTemplate,
selectedView: 'preview',
previewResult: 'rendered preview',
renderedEditorValue: 'old rendered value',
value: 'current value',
selectedHost: { id: '1', name: 'host1' },
data: {
...editorOptions.data,
safemodeRenderPath: '/safemode/path',
},
});
it('should trigger preview view', async () => {
const props = {
...editorOptions,
...didMountStubs(),
selectedView: 'preview',
isRendering: true,
};
const wrapper = mount(<Editor {...props} />);
wrapper.find('button.close').simulate('click');
await act(async () => jest.advanceTimersByTime(1000));
const component = mount(<Editor {...props} />);
await act(async () => jest.advanceTimersByTime(1000));

expect(
component
.find('li[role="presentation"]')
.at(2)
.hasClass('active')
).toBe(true);

userEvent.click(
await screen.findByRole('button', { name: 'Preview' })
);

expect(previewTemplate).toHaveBeenCalledWith({
host: { id: '1', name: 'host1' },
renderPath: '/safemode/path',
templateKindId: '',
});
});
it('should trigger hidden value editor', async () => {
const props = {
...editorOptions,
...didMountStubs(),
selectedView: 'preview',
isRendering: true,
isMasked: true,
};
const wrapper = mount(<Editor {...props} />);
await act(async () => jest.advanceTimersByTime(1000));
expect(wrapper.find('.mask-editor').exists()).toBe(true);

it('dismisses the preview error toast', async () => {
const dismissErrorToast = jest.fn();

renderEditor({ dismissErrorToast, showError: true, errorText: 'Preview failed' });

userEvent.click(await screen.findByRole('button', { name: /Close/i }));

expect(dismissErrorToast).toHaveBeenCalledTimes(1);
});

it('renders the hidden value textarea when editable', async () => {
renderEditor({ readOnly: false, value: 'editor value' });

await screen.findByRole('tab', { name: 'Editor' });

expect(screen.getByDisplayValue('editor value')).toBeInTheDocument();
});
it('textarea disappears if readOnly', async () => {
const props = {
...editorOptions,
...didMountStubs(),
selectedView: 'input',
};
const wrapper = mount(<Editor {...props} />);
await act(async () => jest.advanceTimersByTime(1000));
expect(wrapper.find('textarea.hidden').exists()).toBe(true);
wrapper.setProps({ readOnly: true });
expect(wrapper.find('textarea.hidden').exists()).toBe(false);

it('hides the value textarea when read only', async () => {
const initializeEditor = jest.fn();
const { rerender } = renderWithI18n(
<Editor
{...editorOptions}
initializeEditor={initializeEditor}
readOnly={false}
value="editor value"
/>
);

await screen.findByRole('tab', { name: 'Editor' });
expect(screen.getByDisplayValue('editor value')).toBeInTheDocument();

rerender(
<Editor
{...editorOptions}
initializeEditor={initializeEditor}
readOnly
value="editor value"
/>
);

expect(screen.queryByDisplayValue('editor value')).not.toBeInTheDocument();
});
});
Loading
Loading