box-ui-elements-mlh
Version:
721 lines (664 loc) • 23.2 kB
JavaScript
import React from 'react';
import TEMPLATE_CUSTOM_PROPERTIES from '../constants';
import Instance from '../Instance';
import { isValidValue } from '../../metadata-instance-fields/validateMetadataField';
jest.mock('../../metadata-instance-fields/validateMetadataField');
const data = {
stringfield: 'some string',
floatfield: 1,
enumfield: 'yes',
datefield: '2018-06-20T00:00:00.000Z',
multiselectfield: ['yes', 'no'],
};
const fields = [
{
id: 'field0',
type: 'string',
key: 'nodescfield',
displayName: 'No Description Field',
},
{
id: 'field1',
type: 'string',
key: 'stringfield',
displayName: 'String Field',
description: 'example of a string field',
},
{
id: 'field2',
type: 'string',
key: 'emptystring',
displayName: 'Empty String Field',
description: 'example of an empty string field',
},
{
id: 'field3',
type: 'float',
key: 'floatfield',
displayName: 'Float Field',
description: 'example of a float field',
},
{
id: 'field4',
type: 'float',
key: 'emptyfloat',
displayName: 'Empty Float Field',
description: 'example of an empty float field',
},
{
id: 'field5',
type: 'enum',
key: 'enumfield',
displayName: 'Enum Field',
description: 'example of a enum field',
options: [{ key: 'yes' }, { key: 'no' }],
},
{
id: 'field6',
type: 'enum',
key: 'emptyenumfield',
displayName: 'Empty Enum Field',
description: 'example of an empty enum field',
options: [{ key: 'yes' }, { key: 'no' }],
},
{
id: 'field7',
type: 'date',
key: 'datefield',
displayName: 'Date Field',
description: 'example of a date field',
},
{
id: 'field8',
type: 'date',
key: 'emptydatefield',
displayName: 'Empty Date Field',
description: 'example of an empty date field',
},
{
id: 'field9',
type: 'multiSelect',
key: 'multiselectfield',
displayName: 'Multi Select Field',
description: 'example of a multi select field field',
options: [
{ key: 'yes' },
{ key: 'no' },
{ key: 'maybe' },
{ key: 'idk?' },
{ key: 'oh well' },
{ key: 'whatever' },
],
},
];
const allFieldsHidden = [
{
id: 'field0',
type: 'string',
key: 'nodescfield',
displayName: 'No Description Field that is hidden',
hidden: true,
},
{
id: 'field1',
type: 'string',
key: 'nodescfield',
displayName: 'No Description Field that is also hidden',
isHidden: true,
},
];
const noFieldsInTemplate = [];
const singleTemplateField = [
{
id: 'field0',
type: 'enum',
key: 'emptyenumfield',
displayName: 'Empty Enum Field',
description: 'example of an empty enum field',
},
];
const customTemplateField = [
{
id: 'field0',
type: 'enum',
key: 'emptyenumfield',
displayName: 'Empty Enum Field',
description: 'example of an empty enum field',
templateKey: TEMPLATE_CUSTOM_PROPERTIES,
},
];
const userDefinedTemplateField = [
{
id: 'field0',
type: 'enum',
key: 'emptyenumfield',
displayName: 'Empty Enum Field',
description: 'example of an empty enum field',
templateKey: 'not properties',
},
];
const intl = { formatMessage: () => {} };
describe('features/metadata-instance-editor/fields/Instance', () => {
test('should correctly render templated metadata instance', () => {
const wrapper = shallow(
<Instance
data={data}
dataValue="value"
intl={intl}
template={{
fields,
}}
/>,
);
expect(wrapper).toMatchSnapshot();
});
test('should correctly render templated metadata instance with CascadePolicy', () => {
const wrapper = shallow(
<Instance
cascadePolicy={{
canEdit: true,
id: 'hello',
}}
data={data}
dataValue="value"
intl={intl}
isCascadingPolicyApplicable
shouldShowCascadingOptions
template={{
fields,
}}
/>,
);
expect(wrapper).toMatchSnapshot();
});
test('should correctly render custom metadata instance', () => {
const wrapper = shallow(
<Instance
data={data}
dataValue="value"
intl={intl}
template={{
fields,
templateKey: 'properties',
}}
/>,
);
expect(wrapper).toMatchSnapshot();
});
test('should not render footer when not editing', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onRemove={jest.fn()}
template={{
fields,
}}
/>,
);
expect(wrapper).toMatchSnapshot();
});
test('should correctly render the footer', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields,
}}
/>,
);
wrapper.setState({ isEditing: true });
expect(wrapper).toMatchSnapshot();
});
test('should correctly render the footer even when all fields are hidden', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields: allFieldsHidden,
}}
/>,
);
wrapper.setState({ isEditing: true });
expect(wrapper).toMatchSnapshot();
});
test('should correctly render the footer even when there are no fields in a template', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields: noFieldsInTemplate,
}}
/>,
);
wrapper.setState({ isEditing: true });
expect(wrapper).toMatchSnapshot();
});
test('should correctly render the footer with save button when dirty', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
isDirty
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields,
}}
/>,
);
wrapper.setState({ isEditing: true });
expect(wrapper).toMatchSnapshot();
});
test('should correctly render the busy indicator', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields,
}}
/>,
);
wrapper.setState({ isBusy: true });
expect(wrapper).toMatchSnapshot();
});
test('isOpen should be true if the prop is not undefined', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
isOpen={singleTemplateField.length === 1}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
singleTemplateField,
}}
/>,
);
expect(wrapper.instance().props.isOpen).toBe(true);
});
test('collapsible isOpen prop should be true', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
isOpen={singleTemplateField.length === 1}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
singleTemplateField,
}}
/>,
);
expect(wrapper).toMatchSnapshot();
});
test('message id should be fileMetadataRemoveCustomTemplateConfirm', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
isOpen={customTemplateField.length === 1}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
customTemplateField,
}}
/>,
);
const expectedMessage = 'boxui.metadataInstanceEditor.fileMetadataRemoveCustomTemplateConfirm';
const formattedMessage = wrapper.instance().renderDeleteMessage(true, customTemplateField[0]);
expect(formattedMessage.props.id).toEqual(expectedMessage);
expect(formattedMessage).toMatchSnapshot();
});
test('message id should be folderMetadataRemoveCustomTemplateConfirm', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
isOpen={customTemplateField.length === 1}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
customTemplateField,
}}
/>,
);
const expectedMessage = 'boxui.metadataInstanceEditor.folderMetadataRemoveCustomTemplateConfirm';
const formattedMessage = wrapper.instance().renderDeleteMessage(false, customTemplateField[0]);
expect(formattedMessage.props.id).toEqual(expectedMessage);
expect(formattedMessage).toMatchSnapshot();
});
test('message id should be fileMetadataRemoveTemplateConfirm', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
isOpen={userDefinedTemplateField.length === 1}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
userDefinedTemplateField,
}}
/>,
);
const expectedMessage = 'boxui.metadataInstanceEditor.fileMetadataRemoveTemplateConfirm';
const formattedMessage = wrapper.instance().renderDeleteMessage(true, userDefinedTemplateField[0]);
expect(formattedMessage.props.id).toEqual(expectedMessage);
expect(formattedMessage).toMatchSnapshot();
});
test('message id should be folderMetadataRemoveTemplateConfirm', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
isOpen={userDefinedTemplateField.length === 1}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
userDefinedTemplateField,
}}
/>,
);
const expectedMessage = 'boxui.metadataInstanceEditor.folderMetadataRemoveTemplateConfirm';
const formattedMessage = wrapper.instance().renderDeleteMessage(false, userDefinedTemplateField[0]);
expect(formattedMessage.props.id).toEqual(expectedMessage);
expect(formattedMessage).toMatchSnapshot();
});
describe('createJSONPatch()', () => {
test('should correctly create a JSON patch', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields,
}}
/>,
);
const instance = wrapper.instance();
const currentData = {
stringfield: 'some new string',
floatfield: 1,
newfield: 'new field',
multiselectfield: ['yes', 'no'],
};
expect(instance.createJSONPatch(currentData, data)).toEqual([
{ op: 'test', path: '/stringfield', value: 'some string' },
{
op: 'replace',
path: '/stringfield',
value: 'some new string',
},
{ op: 'test', path: '/enumfield', value: 'yes' },
{ op: 'remove', path: '/enumfield' },
{
op: 'test',
path: '/datefield',
value: '2018-06-20T00:00:00.000Z',
},
{ op: 'remove', path: '/datefield' },
{ op: 'add', path: '/newfield', value: 'new field' },
]);
});
test('should correctly create a JSON patch with multi select diffs', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields,
}}
/>,
);
const instance = wrapper.instance();
const currentData = {
stringfield: 'some new string',
floatfield: 1,
newfield: 'new field',
multiselectfield: ['no', 'yes'],
};
expect(instance.createJSONPatch(currentData, data)).toEqual([
{ op: 'test', path: '/stringfield', value: 'some string' },
{
op: 'replace',
path: '/stringfield',
value: 'some new string',
},
{ op: 'test', path: '/enumfield', value: 'yes' },
{ op: 'remove', path: '/enumfield' },
{
op: 'test',
path: '/datefield',
value: '2018-06-20T00:00:00.000Z',
},
{ op: 'remove', path: '/datefield' },
{ op: 'test', path: '/multiselectfield', value: ['yes', 'no'] },
{
op: 'replace',
path: '/multiselectfield',
value: ['no', 'yes'],
},
{ op: 'add', path: '/newfield', value: 'new field' },
]);
});
});
describe('onFieldChange()', () => {
test('should not do anything when not editing', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields,
}}
/>,
);
const instance = wrapper.instance();
instance.setState = jest.fn();
instance.isEditing = jest.fn().mockReturnValueOnce(false);
instance.onFieldChange('multiselectfield', ['yes'], 'multiSelect');
wrapper.update();
expect(isValidValue).not.toBeCalled();
expect(instance.setState).not.toBeCalled();
});
test('should not do anything when current and prior value is the same', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields,
}}
/>,
);
const instance = wrapper.instance();
instance.setState = jest.fn();
instance.isEditing = jest.fn().mockReturnValueOnce(true);
instance.onFieldChange('multiselectfield', ['yes', 'no'], 'multiSelect');
wrapper.update();
expect(isValidValue).not.toBeCalled();
expect(instance.setState).not.toBeCalled();
});
test('should validate and set state when editing and prior value isnt the same', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields,
}}
/>,
);
const instance = wrapper.instance();
instance.setState = jest.fn();
instance.isEditing = jest.fn().mockReturnValueOnce(true);
instance.onFieldChange('multiselectfield', ['no', 'yes'], 'multiSelect');
wrapper.update();
expect(isValidValue).toBeCalled();
expect(instance.setState).toBeCalled();
});
test('should add an error to the state if field was invalid', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields,
}}
/>,
);
const instance = wrapper.instance();
instance.setState = jest.fn();
instance.isEditing = jest.fn().mockReturnValueOnce(true);
isValidValue.mockReturnValueOnce(false);
instance.onFieldChange('multiselectfield', ['no', 'yes'], 'multiSelect');
wrapper.update();
expect(isValidValue).toBeCalledWith('multiSelect', ['no', 'yes']);
expect(instance.setState).toBeCalledWith(
{
data: { ...data, multiselectfield: ['no', 'yes'] },
errors: {
multiselectfield: expect.any(Object),
},
},
expect.any(Function),
);
});
test('should remove prior errors related to the changed field', () => {
const wrapper = shallow(
<Instance
canEdit
data={data}
dataValue="value"
intl={intl}
onModification={jest.fn()}
onRemove={jest.fn()}
onSave={jest.fn()}
template={{
fields,
}}
/>,
);
wrapper.setState({
errors: {
multiselectfield: 'error',
},
});
wrapper.update();
const instance = wrapper.instance();
instance.setState = jest.fn();
instance.isEditing = jest.fn().mockReturnValueOnce(true);
isValidValue.mockReturnValueOnce(true);
instance.onFieldChange('multiselectfield', ['no', 'yes'], 'multiSelect');
wrapper.update();
expect(isValidValue).toBeCalledWith('multiSelect', ['no', 'yes']);
expect(instance.setState).toBeCalledWith(
{
data: { ...data, multiselectfield: ['no', 'yes'] },
errors: {},
},
expect.any(Function),
);
});
});
describe('Lifecycle methods', () => {
describe('componentDidUpdate()', () => {
test('should mark is editing and remove loader if errored', () => {
const wrapper = shallow(<Instance template={{ fields }} canEdit />);
wrapper.setProps({ hasError: true });
expect(wrapper.state('isBusy')).toEqual(false);
expect(wrapper.state('isEditing')).toEqual(true);
});
test('form has switched from dirty to a clean state', () => {
const wrapper = shallow(<Instance isDirty template={{ fields }} canEdit />);
wrapper.setState({ isEditing: true });
wrapper.setProps({ isDirty: false });
expect(wrapper.state('isBusy')).toEqual(false);
});
test('form has switched from dirty to a clean state', () => {
const wrapper = shallow(<Instance isDirty template={{ fields }} canEdit />);
wrapper.setState({ isEditing: false });
wrapper.setProps({ isDirty: false });
expect(wrapper.state('isBusy')).toEqual(false);
expect(wrapper.state('isCascadingOverwritten')).toEqual(false);
});
});
});
});