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
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@
"coverageDirectory": "<rootDir>/reports",
"collectCoverageFrom": [
"src/**/*.js",
"!src/utils/*.js",
"!third-party/*.js",
"!src/**/messages.js",
"!**/node_modules/**",
"!**/__tests__/**"
],
Expand Down
132 changes: 120 additions & 12 deletions src/components/ActionControls/__tests__/ActionControls-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,162 @@ import { shallow } from 'enzyme';

import ActionControls from '../ActionControls';

const fn = jest.fn();
const onCreate = jest.fn();
const onCommentClick = jest.fn();
const onCancel = jest.fn();
const onDelete = jest.fn();

const event = {
stopPropagation: jest.fn(),
preventDefault: jest.fn()
};

describe('components/ActionControls', () => {
const render = (props = {}) =>
shallow(
<ActionControls
isPending={false}
canDelete={false}
onCreate={fn}
onCommentClick={fn}
onDelete={fn}
onCancel={fn}
hasComments={false}
onCreate={onCreate}
onCommentClick={onCommentClick}
onDelete={onDelete}
onCancel={onCancel}
{...props}
/>
);

test('should correctly render controls for plain highlight annotations without delete permissions', () => {
test('should render no controls if the type does not match a valid type', () => {
const wrapper = render({ type: 'invalid' });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('HighlightControls').exists()).toBeFalsy();
expect(wrapper.find('DrawingControls').exists()).toBeFalsy();
expect(wrapper.find('ApprovalCommentForm').exists()).toBeFalsy();
});

test('should render no controls if user cannot delete or comment on a plain highlight annotation', () => {
const wrapper = render({ type: 'highlight' });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('HighlightControls').exists()).toBeFalsy();
expect(wrapper.find('DrawingControls').exists()).toBeFalsy();
expect(wrapper.find('ApprovalCommentForm').exists()).toBeFalsy();
});

test('should correctly render controls for plain highlight annotations without delete permissions', () => {
const wrapper = render({ type: 'highlight', canComment: true });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('HighlightControls').exists()).toBeTruthy();
});

test('should correctly render controls for plain highlight annotations without comment permissions', () => {
const wrapper = render({ type: 'highlight', canDelete: true });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('HighlightControls').exists()).toBeTruthy();
});

test('should correctly render controls for plain highlight annotations', () => {
const wrapper = render({ canDelete: true, type: 'highlight' });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('HighlightControls').exists()).toBeTruthy();
});

test('should correctly render controls for highlight comment annotations without delete permissions', () => {
const wrapper = render({ type: 'highlight-comment' });
test('should correctly render controls for pending highlight comment annotations', () => {
const wrapper = render({ isPending: true, type: 'highlight-comment' });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('ApprovalCommentForm').exists()).toBeTruthy();
});

test('should correctly render controls for highlight comment annotations without any existing comments', () => {
const wrapper = render({ type: 'highlight-comment', hasComments: true });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('ApprovalCommentForm').exists()).toBeTruthy();
});

test('should correctly render controls for highlight comment annotations', () => {
const wrapper = render({ canDelete: true, type: 'highlight-comment' });
const wrapper = render({ type: 'highlight-comment' });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('HighlightControls').exists()).toBeTruthy();
});

test('should correctly render controls for pending highlight comment annotations', () => {
const wrapper = render({ isPending: true, type: 'highlight-comment' });
test('should render no controls if user cannot delete or comment on a plain highlight annotation', () => {
const wrapper = render({ type: 'draw' });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('HighlightControls').exists()).toBeFalsy();
expect(wrapper.find('DrawingControls').exists()).toBeFalsy();
expect(wrapper.find('ApprovalCommentForm').exists()).toBeFalsy();
});

test('should correctly render controls for drawing annotations', () => {
const wrapper = render({ type: 'draw' });
const wrapper = render({ type: 'draw', canDelete: true });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('DrawingControls').exists()).toBeTruthy();
});

test('should correctly render controls for point annotations', () => {
const wrapper = render({ type: 'point' });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('ApprovalCommentForm').exists()).toBeTruthy();
});

test('should update state and call onDelete() prop on delete button click', () => {
const wrapper = render({ canDelete: true, type: 'highlight' });
const instance = wrapper.instance();

instance.onDelete(event);
wrapper.update();

expect(event.stopPropagation).toBeCalled();
expect(event.preventDefault).toBeCalled();
expect(onDelete).toBeCalled();
expect(wrapper.state().isInputOpen).toEqual(false);
});

test('should update state and call onCreate() prop on create button click', () => {
const wrapper = render({ canDelete: true, type: 'highlight' });
const instance = wrapper.instance();

instance.onCreate(event);
wrapper.update();

expect(onCreate).toBeCalled();
expect(wrapper.state().isInputOpen).toEqual(true);
});

test('should update state and call onCancel() prop on cancel button click', () => {
const wrapper = render({ canDelete: true, type: 'highlight' });
const instance = wrapper.instance();

instance.onCancel(event);
wrapper.update();

expect(event.stopPropagation).toBeCalled();
expect(event.preventDefault).toBeCalled();
expect(onCancel).toBeCalled();
expect(wrapper.state().isInputOpen).toEqual(false);
});

test('should update state to open the input onFocus()', () => {
const wrapper = render({ canDelete: true, type: 'highlight' });
const instance = wrapper.instance();

instance.onFocus(event);
wrapper.update();

expect(wrapper.state().isInputOpen).toEqual(true);
});

test('should update state to close the input componentWillUnmount()', () => {
const wrapper = render({ canDelete: true, type: 'highlight' });
const instance = wrapper.instance();

instance.componentWillUnmount(event);
wrapper.update();

expect(wrapper.state().isInputOpen).toEqual(false);
});

test('should focus the input on componentDidMount()', () => {
const wrapper = render({ canDelete: true, type: 'highlight' });
expect(wrapper.state().isInputOpen).toEqual(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@
exports[`components/ActionControls should correctly render controls for drawing annotations 1`] = `
<div
className="ba-action-controls"
/>
>
<DrawingControls
canDelete={true}
isPending={false}
onCreate={[Function]}
onDelete={[Function]}
/>
</div>
`;

exports[`components/ActionControls should correctly render controls for highlight comment annotations 1`] = `
<div
className="ba-action-controls"
>
<HighlightControls
canAnnotateAndDelete={true}
canAnnotateAndDelete={false}
canComment={true}
isPending={false}
onCommentClick={[MockFunction]}
Expand All @@ -21,17 +28,20 @@ exports[`components/ActionControls should correctly render controls for highligh
</div>
`;

exports[`components/ActionControls should correctly render controls for highlight comment annotations without delete permissions 1`] = `
exports[`components/ActionControls should correctly render controls for highlight comment annotations without any existing comments 1`] = `
<div
className="ba-action-controls"
>
<HighlightControls
canAnnotateAndDelete={false}
canComment={true}
isPending={false}
onCommentClick={[MockFunction]}
onCreate={[Function]}
onDelete={[Function]}
<ApprovalCommentForm
className="ba-annotation-input-container"
createComment={[Function]}
getAvatarUrl={[Function]}
isEditing={true}
isOpen={true}
onCancel={[Function]}
onFocus={[Function]}
onSubmit={[Function]}
user={Object {}}
/>
</div>
`;
Expand Down Expand Up @@ -69,10 +79,34 @@ exports[`components/ActionControls should correctly render controls for plain hi
</div>
`;

exports[`components/ActionControls should correctly render controls for plain highlight annotations without comment permissions 1`] = `
<div
className="ba-action-controls"
>
<HighlightControls
canAnnotateAndDelete={true}
canComment={false}
isPending={false}
onCommentClick={[MockFunction]}
onCreate={[Function]}
onDelete={[Function]}
/>
</div>
`;

exports[`components/ActionControls should correctly render controls for plain highlight annotations without delete permissions 1`] = `
<div
className="ba-action-controls"
/>
>
<HighlightControls
canAnnotateAndDelete={false}
canComment={true}
isPending={false}
onCommentClick={[MockFunction]}
onCreate={[Function]}
onDelete={[Function]}
/>
</div>
`;

exports[`components/ActionControls should correctly render controls for point annotations 1`] = `
Expand All @@ -92,3 +126,21 @@ exports[`components/ActionControls should correctly render controls for point an
/>
</div>
`;

exports[`components/ActionControls should render no controls if the type does not match a valid type 1`] = `
<div
className="ba-action-controls"
/>
`;

exports[`components/ActionControls should render no controls if user cannot delete or comment on a plain highlight annotation 1`] = `
<div
className="ba-action-controls"
/>
`;

exports[`components/ActionControls should render no controls if user cannot delete or comment on a plain highlight annotation 2`] = `
<div
className="ba-action-controls"
/>
`;
50 changes: 22 additions & 28 deletions src/components/AnnotationPopover/AnnotatorLabel.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,36 +20,30 @@ type Props = {
intl: any
};

class AnnotatorLabel extends React.PureComponent<Props> {
/**
* Return a string describes the action completed by the annotator
* @return {string} Localized annotator label message
*/
getAnnotatorLabelMessage(): string {
const { type, createdBy, intl } = this.props;
const anonymousUserName = intl.formatMessage(messages.anonymousUserName);
const name = get(createdBy, 'name', anonymousUserName);

let labelMessage = messages.whoAnnotated;
if (isHighlightAnnotation(type)) {
labelMessage = messages.whoHighlighted;
} else if (isDrawingAnnotation(type)) {
labelMessage = messages.whoDrew;
}

return intl.formatMessage(labelMessage, { name });
const AnnotatorLabel = ({ id, isPending, type, createdBy, intl }: Props) => {
if (isPending) {
return null;
}

render() {
const { id, isPending } = this.props;
return (
!isPending && (
<span className={CLASS_ANNOTATOR_LABEL}>
<CommentText id={id} tagged_message={this.getAnnotatorLabelMessage()} translationEnabled={false} />
</span>
)
);
const anonymousUserName = intl.formatMessage(messages.anonymousUserName);
const name = get(createdBy, 'name', anonymousUserName);

let labelMessage = messages.whoAnnotated;
if (isHighlightAnnotation(type)) {
labelMessage = messages.whoHighlighted;
} else if (isDrawingAnnotation(type)) {
labelMessage = messages.whoDrew;
}
}

return (
<span className={CLASS_ANNOTATOR_LABEL}>
<CommentText
id={id}
tagged_message={intl.formatMessage(labelMessage, { name })}
translationEnabled={false}
/>
</span>
);
};

export default injectIntl(AnnotatorLabel);
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import AnnotationPopover from '../AnnotationPopover';
import { TYPES } from '../../../constants';

const fnMock = jest.fn();
const position = jest.fn();

const TIME_STRING_SEPT_27_2017 = '2017-09-27T10:40:41-07:00';

Expand Down Expand Up @@ -41,17 +42,22 @@ describe('components/AnnotationPopover', () => {
onCreate={fnMock}
onCancel={fnMock}
onDelete={fnMock}
position={fnMock}
position={position}
{...props}
/>
);

test('should position the component on componentDidUpdate()', () => {
const wrapper = render({ comments });
wrapper.setProps({ canAnnotate: true });
expect(position).toBeCalled();
});

test('should correctly render a view-only popover with comments', () => {
const wrapper = render({
comments
});
const wrapper = render({ comments });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('.ba-inline-popover').length).toEqual(0);
expect(position).toBeCalled();
});

test('should render a view-only annotation with a annotator label and no comments', () => {
Expand Down
Loading