Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MNT LinkField Jest tests #217

Merged
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
65 changes: 57 additions & 8 deletions client/src/components/LinkField/tests/LinkField-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* global jest, test, expect, document */
import React from 'react';
import { render, act, screen } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import { Component as LinkField } from '../LinkField';
Expand Down Expand Up @@ -57,6 +57,57 @@ function makeProps(obj = {}) {
};
}

test('LinkField returns list of links if they exist', async () => {
const { container } = render(<LinkField {...makeProps({
isMulti: true,
value: [1, 2],
types: {
sitetree: { key: 'sitetree', title: 'Page', icon: 'font-icon-page', allowed: true },
email: { key: 'email', title: 'Email', icon: 'font-icon-email', allowed: true },
},
})}
/>);

await doResolve({ json: () => ({
1: {
Title: 'Page title',
typeKey: 'sitetree',
},
2: {
Title: 'Email title',
typeKey: 'email',
},
}) });
await screen.findByText('Page title');
expect(container.querySelectorAll('.link-picker__button')).toHaveLength(2);
expect(container.querySelectorAll('.link-picker__button.font-icon-page')[0]).toHaveTextContent('Page title');
expect(container.querySelectorAll('.link-picker__button.font-icon-email')[0]).toHaveTextContent('Email title');
});

test('LinkField will render disabled state if disabled is true', async () => {
const { container } = render(<LinkField {...makeProps({
ownerID: 1,
disabled: true
})}
/>);
doResolve();
await screen.findByText('Cannot create link');
expect(container.querySelectorAll('.link-picker')).toHaveLength(1);
expect(container.querySelectorAll('.link-picker')[0]).toHaveTextContent('Cannot create link');
});

test('LinkField will render readonly state if readonly is true', async () => {
const { container } = render(<LinkField {...makeProps({
ownerID: 1,
readonly: true
})}
/>);
doResolve();
await screen.findByText('Cannot create link');
expect(container.querySelectorAll('.link-picker')).toHaveLength(1);
expect(container.querySelectorAll('.link-picker')[0]).toHaveTextContent('Cannot create link');
});

test('LinkField tab order', async () => {
const user = userEvent.setup();
const { container } = render(<LinkField {...makeProps({
Expand Down Expand Up @@ -135,11 +186,9 @@ test('LinkField will render link-picker if ownerID is not 0 and has finished loa
})}
/>);
doResolve();
// Short wait - we can't use screen.find* because we're waiting for something to be removed, not added to the DOM
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
});
expect(container.querySelectorAll('.link-field__save-record-first')).toHaveLength(0);
expect(container.querySelectorAll('.link-field__loading')).toHaveLength(0);
expect(container.querySelectorAll('.link-picker')).toHaveLength(1);
await waitFor(() => {
expect(container.querySelectorAll('.link-field__save-record-first')).toHaveLength(0);
expect(container.querySelectorAll('.link-field__loading')).toHaveLength(0);
expect(container.querySelectorAll('.link-picker')).toHaveLength(1);
}, { timeout: 100 });
});
18 changes: 11 additions & 7 deletions client/src/components/LinkPicker/tests/LinkPicker-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* global jest, test */
import React from 'react';
import { render, fireEvent, act } from '@testing-library/react';
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import { LinkFieldContext } from 'components/LinkField/LinkField';
import LinkPicker from '../LinkPicker';

Expand All @@ -22,6 +24,7 @@ test('LinkPickerMenu render() should display toggle if can create', () => {
<LinkPicker {...makeProps({ canCreate: true })} />
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__menu-toggle')).toHaveLength(1);
expect(container.querySelector('.link-picker__menu-toggle')).toHaveTextContent('Add Link');
expect(container.querySelectorAll('.link-picker__cannot-create')).toHaveLength(0);
});

Expand All @@ -31,6 +34,7 @@ test('LinkPickerMenu render() should display cannot create message if cannot cre
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__menu-toggle')).toHaveLength(0);
expect(container.querySelectorAll('.link-picker__cannot-create')).toHaveLength(1);
expect(container.querySelector('.link-picker__cannot-create')).toHaveTextContent('Cannot create link');
});

test('LinkPickerMenu render() should display cannot create message if types is empty', () => {
Expand Down Expand Up @@ -60,18 +64,18 @@ test('LinkPickerMenu should open dropdown on click when not loading', async () =
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPicker {...makeProps()} />
</LinkFieldContext.Provider>);
await act(async () => {
await fireEvent.click(container.querySelector('button.link-picker__menu-toggle'));
userEvent.click(container.querySelector('button.link-picker__menu-toggle'));
await waitFor(() => {
expect(container.querySelectorAll('.dropdown-menu.show')).toHaveLength(1);
});
expect(container.querySelectorAll('.dropdown-menu.show')).toHaveLength(1);
});

test('LinkPickerMenu should not open dropdown on click while loading', async () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: true }}>
<LinkPicker {...makeProps()} />
</LinkFieldContext.Provider>);
await act(async () => {
await fireEvent.click(container.querySelector('button.link-picker__menu-toggle'));
userEvent.click(container.querySelector('button.link-picker__menu-toggle'));
await waitFor(() => {
expect(container.querySelectorAll('.dropdown-menu.show')).toHaveLength(0);
});
expect(container.querySelectorAll('.dropdown-menu.show')).toHaveLength(0);
});
73 changes: 73 additions & 0 deletions client/src/components/LinkPicker/tests/LinkPickerMenu-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* global jest, test */

import React from 'react';
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import { LinkFieldContext } from 'components/LinkField/LinkField';
import LinkPickerMenu from '../LinkPickerMenu';

function makeProps(obj = {}) {
return {
types: [
{ key: 'sitetree', title: 'Page', icon: 'font-icon-page', allowed: true },
{ key: 'external', title: 'External URL', icon: 'font-icon-link', allowed: true },
{ key: 'email', title: 'Email', icon: 'font-icon-email', allowed: true },
{ key: 'phone', title: 'Phone', icon: 'font-icon-phone', allowed: true },
],
onSelect: jest.fn(),
onKeyDownEdit: jest.fn(),
...obj
};
}

test('LinkPickerMenu render() should display link list', () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerMenu {...makeProps()} />
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.dropdown-item')).toHaveLength(4);
expect(container.querySelectorAll('.dropdown-item')[0]).toHaveTextContent('Page');
expect(container.querySelectorAll('.dropdown-item')[1]).toHaveTextContent('External URL');
expect(container.querySelectorAll('.dropdown-item')[2]).toHaveTextContent('Email');
expect(container.querySelectorAll('.dropdown-item')[3]).toHaveTextContent('Phone');
});

test('LinkPickerMenu render() should display link list with allowed SiteTreeLink and EmailLink', () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerMenu {...makeProps(
{ types: [
{ key: 'sitetree', title: 'Page', icon: 'font-icon-page', allowed: true },
{ key: 'email', title: 'Email', icon: 'font-icon-email', allowed: true },
{ key: 'phone', title: 'Phone', icon: 'font-icon-phone', allowed: false },
{ key: 'external', title: 'External URL', icon: 'font-icon-link', allowed: false },
] })}
/>
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.dropdown-item')).toHaveLength(2);
expect(container.querySelectorAll('.dropdown-item')[0]).toHaveTextContent('Page');
expect(container.querySelectorAll('.dropdown-item')[0].firstChild).toHaveClass('font-icon-page');
expect(container.querySelectorAll('.dropdown-item')[1]).toHaveTextContent('Email');
expect(container.querySelectorAll('.dropdown-item')[1].firstChild).toHaveClass('font-icon-email');
});

test('LinkPickerMenu onSelect() should call onSelect with selected type', async () => {
const onSelect = jest.fn();
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerMenu {...makeProps({ onSelect })} />
</LinkFieldContext.Provider>);
userEvent.click(container.querySelectorAll('.dropdown-item')[1]);
await waitFor(() => {
expect(onSelect).toHaveBeenCalledTimes(1);
expect(onSelect).toHaveBeenCalledWith('external');
});
});

test('LinkPickerMenu onKeyDownEdit() should call onKeyDownEdit with selected type', async () => {
const onKeyDownEdit = jest.fn();
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerMenu {...makeProps({ onKeyDownEdit })} />
</LinkFieldContext.Provider>);
container.querySelector('.dropdown-item').focus();
userEvent.keyboard('{enter}');
await waitFor(() => expect(onKeyDownEdit).toHaveBeenCalledTimes(1));
});
91 changes: 74 additions & 17 deletions client/src/components/LinkPicker/tests/LinkPickerTitle-test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/* global jest, test */

import React, { createRef } from 'react';
import { render, fireEvent } from '@testing-library/react';
import { render, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import { LinkFieldContext } from 'components/LinkField/LinkField';
import LinkPickerTitle from '../LinkPickerTitle';

Expand Down Expand Up @@ -30,11 +32,26 @@ function makeProps(obj = {}) {
};
}

test('LinkPickerTitle render() should display link type title and link type icon', () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerTitle {...makeProps({ canDelete: false })} />
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__title')).toHaveLength(1);
expect(container.querySelector('.link-picker__title')).toHaveTextContent('My title');
expect(container.querySelectorAll('.font-icon-phone')).toHaveLength(1);
expect(container.querySelector('.link-picker__type')).toHaveTextContent('Phone');
expect(container.querySelector('.link-picker__url')).toHaveTextContent('My description');
expect(container.querySelector('.link-picker__title > .badge')).toHaveTextContent('Draft');
expect(container.querySelectorAll('.link-picker__title > .status-draft')).toHaveLength(1);
});

test('LinkPickerTitle render() should display clear button if can delete', () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerTitle {...makeProps({ canDelete: true })} />
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__delete')).toHaveLength(1);
expect(container.querySelector('.link-picker__delete')).toHaveTextContent('Archive');
expect(container.querySelector('.link-picker__delete').getAttribute('aria-label')).toBe('Archive');
expect(container.querySelectorAll('.font-icon-phone')).toHaveLength(1);
});

Expand All @@ -59,13 +76,6 @@ test('LinkPickerTitle render() should not display clear button if disabled', ()
expect(container.querySelectorAll('.link-picker__delete')).toHaveLength(0);
});

test('LinkPickerTitle render() should display link type icon', () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerTitle {...makeProps({ canDelete: false })} />
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.font-icon-phone')).toHaveLength(1);
});

test('LinkPickerTitle delete button should fire the onDelete callback when not loading', async () => {
const mockOnDelete = jest.fn();
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
Expand All @@ -75,11 +85,13 @@ test('LinkPickerTitle delete button should fire the onDelete callback when not l
})}
/>
</LinkFieldContext.Provider>);
fireEvent.click(container.querySelector('.link-picker__delete'));
expect(mockOnDelete).toHaveBeenCalledTimes(1);
userEvent.click(container.querySelector('.link-picker__delete'));
await waitFor(() => {
expect(mockOnDelete).toHaveBeenCalledTimes(1);
});
});

test('LinkPickerTitle delete button should not fire the onDelete callback while loading', () => {
test('LinkPickerTitle delete button should not fire the onDelete callback while loading', async () => {
const mockOnDelete = jest.fn();
const { container } = render(<LinkFieldContext.Provider value={{ loading: true }}>
<LinkPickerTitle {...makeProps({
Expand All @@ -88,26 +100,32 @@ test('LinkPickerTitle delete button should not fire the onDelete callback while
})}
/>
</LinkFieldContext.Provider>);
fireEvent.click(container.querySelector('.link-picker__delete'));
expect(mockOnDelete).toHaveBeenCalledTimes(0);
userEvent.click(container.querySelector('.link-picker__delete'));
await waitFor(() => {
expect(mockOnDelete).toHaveBeenCalledTimes(0);
});
});

test('LinkPickerTitle main button should fire the onClick callback when not loading', async () => {
const mockOnClick = jest.fn();
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerTitle {...makeProps({ onClick: mockOnClick })} />
</LinkFieldContext.Provider>);
fireEvent.click(container.querySelector('.link-picker__button'));
expect(mockOnClick).toHaveBeenCalledTimes(1);
userEvent.click(container.querySelector('.link-picker__button'));
await waitFor(() => {
expect(mockOnClick).toHaveBeenCalledTimes(1);
});
});

test('LinkPickerTitle main button should not fire the onClick callback while loading', async () => {
const mockOnClick = jest.fn();
const { container } = render(<LinkFieldContext.Provider value={{ loading: true }}>
<LinkPickerTitle {...makeProps({ onClick: mockOnClick })} />
</LinkFieldContext.Provider>);
fireEvent.click(container.querySelector('.link-picker__button'));
expect(mockOnClick).toHaveBeenCalledTimes(0);
userEvent.click(container.querySelector('.link-picker__button'));
await waitFor(() => {
expect(mockOnClick).toHaveBeenCalledTimes(0);
});
});

test('LinkPickerTitle render() should have readonly class if set to readonly', () => {
Expand Down Expand Up @@ -137,3 +155,42 @@ test('LinkPickerTitle render() should not have disabled class if set to disabled
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__link--disabled')).toHaveLength(0);
});

test('dnd handler is displayed on LinkPickerTitle on MultiLinkField', () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerTitle {...makeProps({ disabled: false, readonly: false, isMulti: true })} />
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__drag-handle')).toHaveLength(1);
});

test('dnd handler is not displayed if link field is disabled', () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerTitle {...makeProps({ disabled: true, readonly: false, isMulti: true })} />
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__drag-handle')).toHaveLength(0);
});

test('dnd handler is not displayed if link field is readonly', () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerTitle {...makeProps({ disabled: false, readonly: true, isMulti: true })} />
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__drag-handle')).toHaveLength(0);
});

test('dnd handler is not displayed if link field is not MultiLinkField', () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerTitle {...makeProps({ disabled: false, readonly: false, isMulti: false })} />
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__drag-handle')).toHaveLength(0);
});

test('keydown on dnd handler', async () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerTitle {...makeProps({ isMulti: true })}/>
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__drag-handle')).toHaveLength(1);
container.querySelector('.link-picker__drag-handle').focus();
fireEvent.keyDown(document.activeElement || document.body, { key: 'Enter', code: 'Enter', charCode: 13 });
expect(container.querySelector('.link-picker__drag-handle').getAttribute('aria-pressed')).toBe('true');
expect(container.querySelector('.link-picker__drag-handle').getAttribute('aria-label')).toBe('Sort Links');
});
9 changes: 0 additions & 9 deletions client/src/tests/sample-test.js

This file was deleted.

Loading