diff --git a/client/src/components/LinkField/tests/LinkField-test.js b/client/src/components/LinkField/tests/LinkField-test.js index 96c3c4a7..b4140581 100644 --- a/client/src/components/LinkField/tests/LinkField-test.js +++ b/client/src/components/LinkField/tests/LinkField-test.js @@ -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'; @@ -57,6 +57,57 @@ function makeProps(obj = {}) { }; } +test('LinkField returns list of links if they exist', async () => { + const { container } = render(); + + 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(); + 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(); + 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(); 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 }); }); diff --git a/client/src/components/LinkPicker/tests/LinkPicker-test.js b/client/src/components/LinkPicker/tests/LinkPicker-test.js index f9a0e59c..390c6a27 100644 --- a/client/src/components/LinkPicker/tests/LinkPicker-test.js +++ b/client/src/components/LinkPicker/tests/LinkPicker-test.js @@ -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'; @@ -22,6 +24,7 @@ test('LinkPickerMenu render() should display toggle if can create', () => { ); 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); }); @@ -31,6 +34,7 @@ test('LinkPickerMenu render() should display cannot create message if cannot cre ); 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', () => { @@ -60,18 +64,18 @@ test('LinkPickerMenu should open dropdown on click when not loading', async () = const { container } = render( ); - 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( ); - 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); }); diff --git a/client/src/components/LinkPicker/tests/LinkPickerMenu-test.js b/client/src/components/LinkPicker/tests/LinkPickerMenu-test.js new file mode 100644 index 00000000..958af61b --- /dev/null +++ b/client/src/components/LinkPicker/tests/LinkPickerMenu-test.js @@ -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( + + ); + 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( + + ); + 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( + + ); + 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( + + ); + container.querySelector('.dropdown-item').focus(); + userEvent.keyboard('{enter}'); + await waitFor(() => expect(onKeyDownEdit).toHaveBeenCalledTimes(1)); +}); diff --git a/client/src/components/LinkPicker/tests/LinkPickerTitle-test.js b/client/src/components/LinkPicker/tests/LinkPickerTitle-test.js index 03e74c7b..f7083e9f 100644 --- a/client/src/components/LinkPicker/tests/LinkPickerTitle-test.js +++ b/client/src/components/LinkPicker/tests/LinkPickerTitle-test.js @@ -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'; @@ -30,11 +32,26 @@ function makeProps(obj = {}) { }; } +test('LinkPickerTitle render() should display link type title and link type icon', () => { + const { container } = render( + + ); + 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( ); 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); }); @@ -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( - - ); - 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( @@ -75,11 +85,13 @@ test('LinkPickerTitle delete button should fire the onDelete callback when not l })} /> ); - 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( ); - 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 () => { @@ -97,8 +111,10 @@ test('LinkPickerTitle main button should fire the onClick callback when not load const { container } = render( ); - 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 () => { @@ -106,8 +122,10 @@ test('LinkPickerTitle main button should not fire the onClick callback while loa const { container } = render( ); - 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', () => { @@ -137,3 +155,42 @@ test('LinkPickerTitle render() should not have disabled class if set to disabled ); expect(container.querySelectorAll('.link-picker__link--disabled')).toHaveLength(0); }); + +test('dnd handler is displayed on LinkPickerTitle on MultiLinkField', () => { + const { container } = render( + + ); + expect(container.querySelectorAll('.link-picker__drag-handle')).toHaveLength(1); +}); + +test('dnd handler is not displayed if link field is disabled', () => { + const { container } = render( + + ); + expect(container.querySelectorAll('.link-picker__drag-handle')).toHaveLength(0); +}); + +test('dnd handler is not displayed if link field is readonly', () => { + const { container } = render( + + ); + expect(container.querySelectorAll('.link-picker__drag-handle')).toHaveLength(0); +}); + +test('dnd handler is not displayed if link field is not MultiLinkField', () => { + const { container } = render( + + ); + expect(container.querySelectorAll('.link-picker__drag-handle')).toHaveLength(0); +}); + +test('keydown on dnd handler', async () => { + const { container } = render( + + ); + 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'); +}); diff --git a/client/src/tests/sample-test.js b/client/src/tests/sample-test.js deleted file mode 100644 index 47bc3714..00000000 --- a/client/src/tests/sample-test.js +++ /dev/null @@ -1,9 +0,0 @@ -/* global jest, describe, it, expect */ -/* eslint-disable */ - -describe('sample tests', () => { - it('sample test', () => { - const css = 'sample css'; - expect(css).toBe('sample css'); - }); -});