Remove enzyme mount from shared tests

refs LF-1439
flag=none

Test Plan:
- TermsOfServiceModal.test.jsx - passed
- AnnouncementRow.test.jsx - passed
- CourseItemRow.test.jsx - passed
- ToggleIcon.test.jsx - passed
- TruncateWithTooltip.test.jsx - passed
- UnreadBadge.test.jsx - passed

Change-Id: I0e1c93008ec49a1efc4d9462df9a571a44c2efbd
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/346597
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Eric Saupe <eric.saupe@instructure.com>
QA-Review: Eric Saupe <eric.saupe@instructure.com>
Product-Review: Árpád Kozma <arpad.kozma@instructure.com>
This commit is contained in:
Arpad Kozma 2024-05-02 13:48:17 +02:00 committed by Árpád Kozma
parent c677a896aa
commit f98b461976
11 changed files with 709 additions and 664 deletions

View File

@ -1,205 +0,0 @@
/*
* Copyright (C) 2017 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react'
import {mount} from 'enzyme'
import merge from 'lodash/merge'
import AnnouncementRow from '@canvas/announcements/react/components/AnnouncementRow'
QUnit.module('AnnouncementRow component')
const makeProps = (props = {}) =>
merge(
{
announcement: {
id: '1',
position: 1,
published: true,
title: 'Hello World',
message: 'Foo bar bar baz boop beep bop Foo',
posted_at: 'January 10, 2019 at 10:00 AM',
author: {
id: '5',
display_name: 'John Smith',
html_url: '',
avatar_image_url: null,
},
read_state: 'unread',
unread_count: 0,
discussion_subentry_count: 5,
locked: false,
html_url: '',
user_count: 10,
permissions: {
reply: true,
},
},
canManage: false,
masterCourseData: {},
},
props
)
test('renders the AnnouncementRow component', () => {
const tree = mount(<AnnouncementRow {...makeProps()} />)
ok(tree.exists())
})
test('renders a checkbox if canManage: true', () => {
const tree = mount(<AnnouncementRow {...makeProps({canManage: true})} />)
const node = tree.find('Checkbox')
ok(node.exists())
})
test('renders no checkbox if canManage: false', () => {
const tree = mount(<AnnouncementRow {...makeProps({canManage: false})} />)
const node = tree.find('Checkbox')
notOk(node.exists())
})
test('renders UnreadBadge if announcement has replies > 0', () => {
const announcement = {discussion_subentry_count: 5}
const tree = mount(<AnnouncementRow {...makeProps({announcement})} />)
const node = tree.find('UnreadBadge')
ok(node.exists())
})
test('renders UnreadBadge if announcement has replies == 0', () => {
const announcement = {discussion_subentry_count: 0}
const tree = mount(<AnnouncementRow {...makeProps({announcement})} />)
const node = tree.find('UnreadBadge')
notOk(node.exists())
})
test('renders "Delayed" date label if announcement is delayed', () => {
const delayedDate = new Date().toString()
const announcement = {delayed_post_at: delayedDate}
const tree = mount(<AnnouncementRow {...makeProps({announcement})} />)
const node = tree.find('.ic-item-row__meta-content-timestamp')
ok(node.exists())
})
test('renders "Posted on" date label if announcement is not delayed', () => {
const test_date = '1/24/2018'
const announcement = {delayed_post_at: null, posted_at: test_date}
const tree = mount(<AnnouncementRow {...makeProps({announcement})} />)
const node = tree.find('.ic-item-row__meta-content-timestamp Text')
ok(node.first().text().includes('Jan 24, 2018'))
})
test('renders the SectionsTooltip component if canHaveSections: true', () => {
const announcement = {user_count: 200}
const tree = mount(<AnnouncementRow {...makeProps({announcement, canHaveSections: true})} />)
equal(tree.find('SectionsTooltip Text').at(0).text(), 'All Sections')
})
test('does not render the SectionsTooltip component if canHaveSections: false', () => {
const announcement = {user_count: 200, canHaveSections: false}
const tree = mount(<AnnouncementRow {...makeProps({announcement})} />)
notOk(tree.find('SectionsTooltip').exists())
})
test('renders the SectionsTooltip component with sections', () => {
const announcement = {
sections: [
{id: 6, course_id: 1, name: 'section 4', user_count: 2},
{id: 5, course_id: 1, name: 'section 2', user_count: 1},
],
}
const tree = mount(<AnnouncementRow {...makeProps({announcement, canHaveSections: true})} />)
equal(tree.find('SectionsTooltip Text').at(0).text(), '2 Sectionssection 4section 2')
})
test('does not render master course lock icon if masterCourseData is not provided', assert => {
const done = assert.async()
const masterCourseData = null
const rowRef = row => {
notOk(row.masterCourseLock)
done()
}
mount(<AnnouncementRow {...makeProps({masterCourseData, rowRef})} />)
})
test('renders master course lock icon if masterCourseData is provided', assert => {
const done = assert.async()
const masterCourseData = {isMasterCourse: true, masterCourse: {id: '1'}}
const rowRef = row => {
ok(row.masterCourseLock)
done()
}
mount(<AnnouncementRow {...makeProps({masterCourseData, rowRef})} />)
})
test('renders reply button icon if user has reply permission', () => {
const tree = mount(
<AnnouncementRow {...makeProps({announcement: {permissions: {reply: true}}})} />
)
const node = tree.find('IconReplyLine')
ok(node.exists())
})
test('does not render reply button icon if user does not have reply permission', () => {
const tree = mount(
<AnnouncementRow {...makeProps({announcement: {permissions: {reply: false}}})} />
)
const node = tree.find('IconReply')
notOk(node.exists())
})
test('removes non-text content from announcement message', () => {
const messageHtml = `
<p>Hello World!</p>
<img src="/images/stuff/things.png" />
<p>foo bar</p>
`
const tree = mount(<AnnouncementRow {...makeProps({announcement: {message: messageHtml}})} />)
const node = tree.find('.ic-announcement-row__content').instance()
equal(node.childNodes.length, 1)
equal(node.childNodes[0].nodeType, 3) // nodeType === 3 is text node type
ok(node.textContent.includes('Hello World!'))
ok(node.textContent.includes('foo bar'))
})
test('does not render manage menu if canManage is false', () => {
const tree = mount(<AnnouncementRow {...makeProps({canManage: false})} />)
const menu = tree.find('.ic-item-row__manage-menu')
notOk(menu.exists())
})
test('renders manage menu if canManage is true', () => {
const tree = mount(<AnnouncementRow {...makeProps({canManage: true})} />)
const menu = tree.find('.ic-item-row__manage-menu')
ok(menu.exists())
})
test('does not render Allow Comments menu item if announcements are globally locked', () => {
const tree = mount(
<AnnouncementRow
{...makeProps({canManage: true, canDelete: true, announcementsLocked: true})}
/>
)
// If we click the menu, it does not actually pop up the new menu in this tree,
// it pops it up in another tree in the dom which afaict can't be tested here.
// This is a way to get around that.
const courseItemRow = tree.find('CourseItemRow')
ok(courseItemRow.exists())
const menuItems = courseItemRow.props().manageMenuOptions()
strictEqual(menuItems.length, 1)
strictEqual(menuItems[0].key, 'delete')
})

View File

@ -1,246 +0,0 @@
/*
* Copyright (C) 2017 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react'
import {mount} from 'enzyme'
import CourseItemRow from '@canvas/announcements/react/components/CourseItemRow'
import AnnouncementModel from '@canvas/discussions/backbone/models/Announcement'
import {IconAssignmentLine} from '@instructure/ui-icons'
QUnit.module('CourseItemRow component')
const props = {
title: <p>Hello World</p>,
body: <p>Hello World</p>,
actionsContent: null,
metaContent: null,
author: {
id: '5',
display_name: 'John Smith',
html_url: '',
avatar_image_url: null,
},
className: '',
id: '5',
itemUrl: '',
selectable: false,
defaultSelected: false,
isRead: false,
showAvatar: true,
onSelectedChanged: () => {},
}
test('renders the CourseItemRow component', () => {
const tree = mount(<CourseItemRow {...props} />)
ok(tree.exists())
})
test("renders the CourseItemRow component when author doesn't exist", () => {
const tree = mount(<CourseItemRow {...props} author={null} />)
ok(tree.exists())
})
test('renders children inside content column', () => {
const tree = mount(
<CourseItemRow
{...props}
title={<span className="find-me" />}
body={<span className="find-me2" />}
sectionToolTip={<span className="find-me3" />}
replyButton={<span className="find-me4" />}
/>
)
ok(tree.find('.ic-item-row__content-col .find-me').exists())
ok(tree.find('.ic-item-row__content-col .find-me2').exists())
ok(tree.find('.ic-item-row__content-col .find-me3').exists())
ok(tree.find('.ic-item-row__content-col .find-me4').exists())
})
test('renders clickable children inside content link', () => {
const tree = mount(
<CourseItemRow
{...props}
itemUrl="/foo"
title={<span className="find-me" />}
body={<span className="find-me2" />}
sectionToolTip={<span className="find-me3" />}
replyButton={<span className="find-me4" />}
/>
)
ok(tree.find('.ic-item-row__content-col .ic-item-row__content-link .find-me').exists())
ok(tree.find('.ic-item-row__content-col .ic-item-row__content-container .find-me2').exists())
ok(!tree.find('.ic-item-row__content-col .ic-item-row__content-link .find-me3').exists())
ok(tree.find('.ic-item-row__content-col .ic-item-row__content-link .find-me4').exists())
})
test('renders actions inside actions wrapper', () => {
const tree = mount(<CourseItemRow {...props} actionsContent={<span className="find-me" />} />)
const node = tree.find('.ic-item-row__meta-actions .find-me')
ok(node.exists())
})
test('renders metaContent inside meta content wrapper', () => {
const tree = mount(<CourseItemRow {...props} metaContent={<span className="find-me" />} />)
const node = tree.find('.ic-item-row__meta-content .find-me')
ok(node.exists())
})
test('renders a checkbox if selectable: true', () => {
const tree = mount(<CourseItemRow {...props} selectable />)
const node = tree.find('Checkbox')
ok(node.exists())
})
test('renders a drag handle if draggable: true', () => {
const tree = mount(
<CourseItemRow {...props} draggable connectDragSource={component => component} />
)
const node = tree.find('IconDragHandleLine')
ok(node.exists())
})
test('renders inputted icon', () => {
const tree = mount(<CourseItemRow {...props} icon={<IconAssignmentLine />} />)
const node = tree.find(IconAssignmentLine)
ok(node.exists())
})
test('renders no checkbox if selectable: false', () => {
const tree = mount(<CourseItemRow {...props} selectable={false} />)
const node = tree.find('Checkbox')
notOk(node.exists())
})
test('renders an accessible avatar if showAvatar: true', () => {
const tree = mount(<CourseItemRow {...props} showAvatar />)
const node = tree.find('Avatar').find("img[alt='John Smith']")
ok(node.exists())
})
test('renders no avatar if showAvatar: false', () => {
const tree = mount(<CourseItemRow {...props} showAvatar={false} />)
const node = tree.find('Avatar')
notOk(node.exists())
})
test('renders unread indicator if isRead: false', () => {
const tree = mount(<CourseItemRow {...props} isRead={false} />)
const rowNode = tree.find('Badge')
ok(rowNode.exists())
const srNode = tree.find('.ic-item-row__content-col Heading ScreenReaderContent').first()
ok(srNode.exists())
ok(srNode.text().includes('unread,'))
})
test('renders no unread indicator if isRead: true', () => {
const tree = mount(<CourseItemRow {...props} isRead />)
const rowNode = tree.find('Badge')
notOk(rowNode.exists())
const srNode = tree.find('.ic-item-row__content-col Heading ScreenReaderContent')
notOk(srNode.exists())
})
test('passes down className prop to component', () => {
const tree = mount(<CourseItemRow {...props} className="find-me" />)
const rowNode = tree.find('.ic-item-row')
ok(rowNode.hasClass('find-me'))
})
test('renders master course lock icon if isMasterCourse', () => {
const masterCourse = {
courseData: {isMasterCourse: true, masterCourse: {id: '1'}},
getLockOptions: () => ({
model: new AnnouncementModel(props.announcement),
unlockedText: '',
lockedText: '',
course_id: '3',
content_id: '5',
content_type: 'announcement',
}),
}
const tree = mount(<CourseItemRow {...props} masterCourse={masterCourse} />)
ok(tree.instance().masterCourseLock)
})
test('renders peer review icon if peer review', () => {
const tree = mount(<CourseItemRow {...props} peerReview />)
const peerReviewComponent = tree.find('.ic-item-row__peer_review')
ok(peerReviewComponent.exists())
})
test('renders master course lock icon if isChildCourse', () => {
const masterCourse = {
courseData: {isChildCourse: true, masterCourse: {id: '1'}},
getLockOptions: () => ({
model: new AnnouncementModel(props.announcement),
unlockedText: '',
lockedText: '',
course_id: '3',
content_id: '5',
content_type: 'announcement',
}),
}
const tree = mount(<CourseItemRow {...props} masterCourse={masterCourse} />)
ok(tree.instance().masterCourseLock)
})
test('renders no master course lock icon if no master course data provided', () => {
const masterCourse = {
courseData: {},
getLockOptions: () => ({}),
}
const tree = mount(<CourseItemRow {...props} masterCourse={masterCourse} />)
notOk(tree.instance().masterCourseLock)
})
test('renders no master course lock icon if isMasterCourse and isChildCourse are false', () => {
const masterCourse = {
courseData: {isMasterCourse: false, isChildCourse: false},
getLockOptions: () => ({}),
}
const tree = mount(<CourseItemRow {...props} masterCourse={masterCourse} />)
notOk(tree.instance().masterCourseLock)
})
test('calls onSelectChanged when checkbox is toggled', () => {
const onSelectedChanged = sinon.spy()
const tree = mount(<CourseItemRow {...props} onSelectedChanged={onSelectedChanged} selectable />)
const instance = tree.instance()
instance.onSelectChanged({target: {checked: true}})
ok(onSelectedChanged.calledWithMatch({id: '5', selected: true}))
})
test('renders no manage menu when showManageMenu is false', () => {
const tree = mount(<CourseItemRow {...props} showManageMenu={false} />)
const menu = tree.find('.ic-item-row__manage-menu')
notOk(menu.exists())
})
test('renders manage menu when showManageMenu is true and manageMenuOptions is not empty', () => {
const tree = mount(<CourseItemRow {...props} showManageMenu manageMenuOptions={['one', 'two']} />)
const menu = tree.find('.ic-item-row__manage-menu')
ok(menu.exists())
})
test('does not render a clickable div if the body is empty', () => {
const tree = mount(<CourseItemRow {...props} body={null} />)
const contentLinks = tree.find('.ic-item-row__content-link')
strictEqual(contentLinks.length, 1)
})

View File

@ -1,84 +0,0 @@
/*
* Copyright (C) 2018 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react'
import {mount} from 'enzyme'
import merge from 'lodash/merge'
import ToggleIcon from 'ui/features/discussion_topics_index/react/components/ToggleIcon'
QUnit.module('ToggleIcon component')
const makeProps = (props = {}) =>
merge(
{
toggled: true,
OnIcon: <span className="onIcon" />,
OffIcon: <span className="offIcon" />,
onToggleOn: () => {},
onToggleOff: () => {},
disabled: false,
},
props
)
test('renders the ToggleIcon component', () => {
const tree = mount(<ToggleIcon {...makeProps()} />)
ok(tree.exists())
})
test('renders the on icon when toggled', () => {
const tree = mount(<ToggleIcon {...makeProps()} />)
ok(tree.find('.onIcon').exists())
ok(!tree.find('.offIcon').exists())
})
test('renders the off icon when untoggled', () => {
const tree = mount(<ToggleIcon {...makeProps({toggled: false})} />)
ok(!tree.find('.onIcon').exists())
ok(tree.find('.offIcon').exists())
})
test('calls onToggleOff when clicked while toggled', () => {
const onToggleOn = sinon.spy()
const onToggleOff = sinon.spy()
const tree = mount(<ToggleIcon {...makeProps({onToggleOn, onToggleOff})} />)
tree.find('.onIcon').simulate('click')
strictEqual(onToggleOff.callCount, 1)
strictEqual(onToggleOn.callCount, 0)
})
test('calls onToggleOn when clicked while untoggled', () => {
const onToggleOn = sinon.spy()
const onToggleOff = sinon.spy()
const tree = mount(<ToggleIcon {...makeProps({onToggleOn, onToggleOff, toggled: false})} />)
tree.find('.offIcon').simulate('click')
strictEqual(onToggleOff.callCount, 0)
strictEqual(onToggleOn.callCount, 1)
})
test('cannot be clicked if disabled', () => {
const onToggleOn = sinon.spy()
const onToggleOff = sinon.spy()
const tree = mount(<ToggleIcon {...makeProps({onToggleOn, onToggleOff, disabled: true})} />)
tree.find('.onIcon').simulate('click')
strictEqual(onToggleOff.callCount, 0)
strictEqual(onToggleOn.callCount, 0)
})

View File

@ -1,89 +0,0 @@
/*
* Copyright (C) 2018 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react'
import {mount} from 'enzyme'
import TruncateWithTooltip from '@canvas/grade-summary/react/TruncateWithTooltip'
let componentHost
let tooltipHost
let component
QUnit.module('TruncateWithTooltip', {
beforeEach: () => {
componentHost = document.createElement('div')
componentHost.setAttribute('id', 'TruncateWithTooltipComponent')
tooltipHost = document.createElement('div')
tooltipHost.setAttribute('id', 'TruncateWithTooltipTooltip')
document.body.appendChild(componentHost)
document.body.appendChild(tooltipHost)
},
afterEach: () => {
if (component) {
component.unmount()
}
if (componentHost) {
componentHost.remove()
}
if (tooltipHost) {
tooltipHost.remove()
}
},
})
const render = (text, width = null) =>
mount(
<div style={{width}}>
<TruncateWithTooltip mountNode={tooltipHost}>{text}</TruncateWithTooltip>
</div>,
{
attachTo: componentHost,
}
)
test('renders the TruncateWithTooltip component', () => {
component = render('Boo')
ok(component.exists())
})
test('renders short text', () => {
component = render('This is some text')
equal(component.text(), 'This is some text')
})
test('truncates long text', () => {
const long =
'This is some long long long long long long long long long long long long long long text'
component = render(long, '100px')
notEqual(component.text(), long)
ok(component.text().includes('\u2026'))
})
test('does not include a popover for short text', () => {
component = render('This is some text')
const tooltip = tooltipHost.querySelector('[role="tooltip"]')
notOk(tooltip)
})
test('includes a popover for long text', () => {
const long =
'This is some long long long long long long long long long long long long long long text'
component = render(long, '100px')
const tooltip = tooltipHost.querySelector('[role="tooltip"]')
ok(tooltip)
equal(tooltip.textContent, long)
})

View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2018 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react'
import {fireEvent, render, screen} from '@testing-library/react'
import ToggleIcon from '../ToggleIcon'
const defaultProps = (props = {}) => ({
toggled: true,
OnIcon: <span className="onIcon" />,
OffIcon: <span className="offIcon" />,
onToggleOn: () => {},
onToggleOff: () => {},
disabled: false,
...props,
})
const renderToggleIcon = (props = {}) => render(<ToggleIcon {...defaultProps(props)} />)
describe('ToggleIcon', () => {
it('renders the ToggleIcon component', () => {
renderToggleIcon()
expect(screen.getByRole('button')).toBeInTheDocument()
expect(screen.getByRole('button')).toHaveClass('toggle-button')
})
it('adds the className to the container', () => {
const {container} = renderToggleIcon({className: 'foo'})
expect(container.querySelector('.foo')).toBeInTheDocument()
})
it('renders the on icon when toggled', () => {
const {container} = renderToggleIcon()
expect(container.querySelector('.onIcon')).toBeInTheDocument()
expect(container.querySelector('.offIcon')).not.toBeInTheDocument()
})
it('renders the off icon when untoggled', () => {
const {container} = renderToggleIcon({toggled: false})
expect(container.querySelector('.onIcon')).not.toBeInTheDocument()
expect(container.querySelector('.offIcon')).toBeInTheDocument()
})
it('calls onToggleOff when clicked while toggled', () => {
const onToggleOn = jest.fn()
const onToggleOff = jest.fn()
const {container} = renderToggleIcon({onToggleOn, onToggleOff})
fireEvent.click(container.querySelector('.onIcon'))
expect(onToggleOff).toHaveBeenCalledTimes(1)
expect(onToggleOn).toHaveBeenCalledTimes(0)
})
it('calls onToggleOn when clicked while untoggled', () => {
const onToggleOn = jest.fn()
const onToggleOff = jest.fn()
const {container} = renderToggleIcon({onToggleOn, onToggleOff, toggled: false})
fireEvent.click(container.querySelector('.offIcon'))
expect(onToggleOff).toHaveBeenCalledTimes(0)
expect(onToggleOn).toHaveBeenCalledTimes(1)
})
it('cannot be clicked if disabled', () => {
const onToggleOn = jest.fn()
const onToggleOff = jest.fn()
const {container} = renderToggleIcon({onToggleOn, onToggleOff, disabled: true})
fireEvent.click(container.querySelector('.onIcon'))
expect(screen.getByRole('button')).toBeDisabled()
expect(screen.getByRole('button')).toHaveClass('disabled-toggle-button')
expect(onToggleOff).toHaveBeenCalledTimes(0)
expect(onToggleOn).toHaveBeenCalledTimes(0)
})
})

View File

@ -19,28 +19,34 @@
import React from 'react'
import $ from 'jquery'
import 'jquery-migrate'
import {mount} from 'enzyme'
import TermsOfServiceModal from 'ui/features/terms_of_service_modal/react/TermsOfServiceModal'
import {render, screen} from '@testing-library/react'
import TermsOfServiceModal from '../TermsOfServiceModal'
QUnit.module('Terms of Service Modal Link', {
beforeEach() {
const renderTermsOfServiceModal = (props = {}) => render(<TermsOfServiceModal {...props} />)
describe('TermsOfServiceModal', () => {
beforeEach(() => {
$('#fixtures').html('<div id="main">')
},
afterEach() {
window.ENV = {
TERMS_OF_SERVICE_CUSTOM_CONTENT: 'Hello World',
}
})
afterEach(() => {
$('#fixtures').empty()
},
})
delete window.ENV
})
test('renders correct link when preview is provided', () => {
ENV.TERMS_OF_SERVICE_CUSTOM_CONTENT = 'Hello World'
const wrapper = mount(<TermsOfServiceModal preview />)
const renderedLink = wrapper.find('a')
equal(renderedLink.text(), 'Preview')
})
it('renders correct link when preview is provided', () => {
renderTermsOfServiceModal({preview: true})
test('renders correct link when preview is not provided', () => {
ENV.TERMS_OF_SERVICE_CUSTOM_CONTENT = 'Hello World'
const wrapper = mount(<TermsOfServiceModal />)
const renderedLink = wrapper.find('a')
equal(renderedLink.text(), 'Acceptable Use Policy')
expect(screen.getByText('Preview')).toBeInTheDocument()
})
it('renders correct link when preview is not provided', () => {
renderTermsOfServiceModal()
expect(screen.getByText('Acceptable Use Policy')).toBeInTheDocument()
})
})

View File

@ -20,7 +20,7 @@
// with the shared utilities created in g/something.
import {useScope as useI18nScope} from '@canvas/i18n'
import React, {Component} from 'react'
import {bool, node, string, func, shape, arrayOf, oneOf} from 'prop-types'
import {bool, node, string, func, shape, oneOf} from 'prop-types'
import cx from 'classnames'
import {Text} from '@instructure/ui-text'
@ -41,7 +41,7 @@ const I18n = useI18nScope('shared_components')
export default class CourseItemRow extends Component {
static propTypes = {
actionsContent: arrayOf(node),
actionsContent: node,
metaContent: node,
masterCourse: shape({
courseData: masterCourseDataShape,

View File

@ -0,0 +1,226 @@
/*
* Copyright (C) 2017 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react'
import {fireEvent, render, screen} from '@testing-library/react'
import AnnouncementRow from '../AnnouncementRow'
const mockLockIconView = {
render: jest.fn(),
remove: jest.fn(),
}
jest.mock('@canvas/lock-icon', () => jest.fn(() => mockLockIconView))
const defaultProps = (props = {}) => ({
canManage: false,
masterCourseData: {},
...props,
announcement: {
id: '1',
position: 1,
published: true,
title: 'Hello World',
message: 'Foo bar bar baz boop beep bop Foo',
posted_at: 'January 10, 2019 at 10:00 AM',
author: {
id: '5',
name: 'John Smith',
display_name: 'John Smith',
html_url: '',
avatar_image_url: null,
},
read_state: 'unread',
unread_count: 0,
discussion_subentry_count: 0,
locked: false,
html_url: '',
user_count: 10,
permissions: {
reply: true,
},
...props.announcement,
},
})
const renderAnnouncementRow = (props = {}) => render(<AnnouncementRow {...defaultProps(props)} />)
describe('AnnouncementRow', () => {
it('renders the AnnouncementRow component', () => {
renderAnnouncementRow()
expect(screen.getByText('Hello World')).toBeInTheDocument()
})
it('renders a checkbox if canManage: true', () => {
renderAnnouncementRow({canManage: true})
expect(screen.getByRole('checkbox')).toBeInTheDocument()
})
it('renders no checkbox if canManage: false', () => {
renderAnnouncementRow({canManage: false})
expect(screen.queryByRole('checkbox')).not.toBeInTheDocument()
})
it('renders UnreadBadge if announcement has replies > 0', () => {
renderAnnouncementRow({announcement: {unread_count: 2, discussion_subentry_count: 5}})
expect(
screen.getByRole('tooltip', {
name: /2 unread replies/i,
})
).toBeInTheDocument()
})
it('renders UnreadBadge if announcement has replies == 0', () => {
renderAnnouncementRow({announcement: {discussion_subentry_count: 0}})
expect(screen.getByText(/unread/)).toBeInTheDocument()
})
it('renders "Delayed" date label if announcement is delayed', () => {
const tomorrow = new Date()
const dateOptions = {
year: 'numeric',
month: 'long',
day: 'numeric',
timeZone: 'UTC',
}
const announcement = {}
tomorrow.setDate(tomorrow.getDate() + 1)
announcement.delayed_post_at = tomorrow.toISOString()
renderAnnouncementRow({announcement})
const expectedDate = new Intl.DateTimeFormat('en-US', dateOptions).format(tomorrow)
expect(screen.getByText(/delayed until:/i)).toBeInTheDocument()
expect(screen.getByText(new RegExp(expectedDate, 'i'))).toBeInTheDocument()
})
it('renders "Posted on" date label if announcement is not delayed', () => {
const test_date = '1/24/2018'
renderAnnouncementRow({announcement: {delayed_post_at: null, posted_at: test_date}})
expect(screen.getByText(/Jan 24, 2018/)).toBeInTheDocument()
})
it('renders the SectionsTooltip component if canHaveSections: true', () => {
const announcement = {user_count: 200}
renderAnnouncementRow({announcement, canHaveSections: true})
expect(screen.getByText('All Sections')).toBeInTheDocument()
})
it('does not render the SectionsTooltip component if canHaveSections: false', () => {
const announcement = {user_count: 200, canHaveSections: false}
renderAnnouncementRow({announcement})
expect(screen.queryByText('All Sections')).not.toBeInTheDocument()
})
it('renders the SectionsTooltip component with sections', () => {
const announcement = {
sections: [
{id: 6, course_id: 1, name: 'section 4', user_count: 2},
{id: 5, course_id: 1, name: 'section 2', user_count: 1},
],
}
renderAnnouncementRow({announcement, canHaveSections: true})
expect(screen.getByText('2 Sections')).toBeInTheDocument()
expect(screen.getByText('section 4')).toBeInTheDocument()
expect(screen.getByText('section 2')).toBeInTheDocument()
})
it('does not render master course lock icon if masterCourseData is not provided', () => {
const masterCourseData = null
const {container} = renderAnnouncementRow({masterCourseData})
expect(container.querySelector('.lock-icon .lock-icon')).not.toBeInTheDocument()
})
it('renders master course lock icon if masterCourseData is provided', () => {
const masterCourseData = {isMasterCourse: true, masterCourse: {id: '1'}}
renderAnnouncementRow({masterCourseData})
expect(mockLockIconView.render).toHaveBeenCalled()
})
it('renders reply button icon if user has reply permission', () => {
renderAnnouncementRow({announcement: {permissions: {reply: true}}})
expect(screen.getByRole('link', {name: /reply/i})).toBeInTheDocument()
})
it('does not render reply button icon if user does not have reply permission', () => {
renderAnnouncementRow({announcement: {permissions: {reply: false}}})
expect(screen.queryByRole('link', {name: /reply/i})).not.toBeInTheDocument()
})
it('removes non-text content from announcement message', () => {
const messageHtml = `
<p data-testid="custom-html-text1">This is a message within custom HTML</p>
<img data-testid="custom-html-image" src="/apple-touch-icon.png" alt=""/>
<p data-testid="custom-html-text2">This is also a message within custom HTML</p>
`
renderAnnouncementRow({announcement: {message: messageHtml}})
expect(screen.getByText(/This is a message within custom HTML/)).toBeInTheDocument()
expect(screen.getByText(/This is also a message within custom HTML/)).toBeInTheDocument()
expect(screen.queryByTestId('custom-html-image')).not.toBeInTheDocument()
expect(screen.queryByTestId('custom-html-text1')).not.toBeInTheDocument()
expect(screen.queryByTestId('custom-html-text2')).not.toBeInTheDocument()
expect(screen.queryByText('/images/stuff/things.png')).not.toBeInTheDocument()
})
it('does not render manage menu if canManage is false', () => {
renderAnnouncementRow({canManage: false})
expect(screen.queryByText(/Manage options for /)).not.toBeInTheDocument()
})
it('renders manage menu if canManage is true', () => {
renderAnnouncementRow({canManage: true})
expect(screen.getByText(/Manage options for /)).toBeInTheDocument()
})
it('does not render Allow Comments menu item if announcements are globally locked', () => {
renderAnnouncementRow({
canManage: true,
canDelete: false,
announcementsLocked: true,
})
fireEvent.click(screen.getByRole('button', {name: /Manage options for /}))
expect(screen.queryByText(/Allow Comments/)).not.toBeInTheDocument()
})
})

View File

@ -0,0 +1,263 @@
/*
* Copyright (C) 2017 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react'
import {fireEvent, render, screen} from '@testing-library/react'
import CourseItemRow from '../CourseItemRow'
import AnnouncementModel from '@canvas/discussions/backbone/models/Announcement'
const mockLockIconView = {
render: jest.fn(),
remove: jest.fn(),
}
jest.mock('@canvas/lock-icon', () => jest.fn(() => mockLockIconView))
const defaultProps = {
title: 'Hello CourseItemRow title',
body: <p>Hello CourseItemRow body</p>,
id: '5',
position: 1,
published: true,
message: 'Foo bar bar baz boop beep bop Foo',
posted_at: 'January 10, 2019 at 10:00 AM',
author: {
id: '5',
name: 'John Smith',
display_name: 'John Smith',
html_url: '',
avatar_image_url: null,
},
read_state: 'unread',
unread_count: 0,
discussion_subentry_count: 0,
locked: false,
html_url: '',
user_count: 10,
permissions: {
reply: true,
},
}
const renderCourseItemRow = (props = {}) => {
const activeProps = {...defaultProps, ...props}
return render(<CourseItemRow {...activeProps} />)
}
describe('CourseItemRow', () => {
afterEach(() => {
jest.clearAllMocks()
})
it('renders the CourseItemRow component', () => {
renderCourseItemRow()
expect(screen.getByText('Hello CourseItemRow body')).toBeInTheDocument()
})
it("renders the CourseItemRow component when author doesn't exist", () => {
renderCourseItemRow({author: null})
expect(screen.getByText('Hello CourseItemRow body')).toBeInTheDocument()
})
it('renders children inside content column', () => {
const {container} = renderCourseItemRow({
body: <span className="find-me2" />,
sectionToolTip: <span className="find-me3" />,
replyButton: <span className="find-me4" />,
})
expect(screen.getByText('Hello CourseItemRow title')).toBeInTheDocument()
expect(container.querySelector('.ic-item-row__content-col .find-me2')).toBeInTheDocument()
expect(container.querySelector('.ic-item-row__content-col .find-me3')).toBeInTheDocument()
expect(container.querySelector('.ic-item-row__content-col .find-me4')).toBeInTheDocument()
})
it('renders clickable children inside content link', () => {
const {container} = renderCourseItemRow({
replyButton: <span className="find-me" />,
})
expect(container.querySelector('a.ic-item-row__content-link h3')).toBeInTheDocument()
expect(container.querySelector('a.ic-item-row__content-link .find-me')).toBeInTheDocument()
})
it('renders actions inside actions wrapper', () => {
const {container} = renderCourseItemRow({
actionsContent: <span className="find-me" />,
})
expect(container.querySelector('.ic-item-row__meta-actions .find-me')).toBeInTheDocument()
})
it('renders metaContent inside meta content wrapper', () => {
const {container} = renderCourseItemRow({
metaContent: <span className="find-me" />,
})
expect(container.querySelector('.ic-item-row__meta-content .find-me')).toBeInTheDocument()
})
it('renders a checkbox if selectable: true', () => {
renderCourseItemRow({selectable: true})
expect(screen.getByRole('checkbox')).toBeInTheDocument()
})
it('renders a drag handle if draggable: true', () => {
const {container} = renderCourseItemRow({draggable: true})
expect(container.querySelector('.ic-item-row__drag-col')).toBeInTheDocument()
})
it('renders inputted icon', () => {
renderCourseItemRow({icon: <span data-testid="custom-icon" />})
expect(screen.queryByTestId('custom-icon')).toBeInTheDocument()
})
it('renders no checkbox if selectable: false', () => {
renderCourseItemRow({selectable: false})
expect(screen.queryByRole('checkbox')).not.toBeInTheDocument()
})
it('renders an accessible avatar if showAvatar: true', () => {
renderCourseItemRow({showAvatar: true})
expect(screen.getByAltText('John Smith')).toBeInTheDocument()
})
it('renders no avatar if showAvatar: false', () => {
renderCourseItemRow({showAvatar: false})
expect(screen.queryByAltText('John Smith')).not.toBeInTheDocument()
})
it('renders unread indicator if isRead: false', () => {
renderCourseItemRow({isRead: false})
expect(screen.getByText(/unread,/)).toBeInTheDocument()
})
it('renders no unread indicator if isRead: true', () => {
renderCourseItemRow({isRead: true})
expect(screen.queryByText(/unread,/)).not.toBeInTheDocument()
})
it('passes down className prop to component', () => {
const {container} = renderCourseItemRow({className: 'find-me'})
expect(container.querySelector('.ic-item-row')).toHaveClass('find-me')
})
it('renders master course lock icon if isMasterCourse', () => {
const masterCourse = {
courseData: {isMasterCourse: true, masterCourse: {id: '1'}},
getLockOptions: () => ({
model: new AnnouncementModel(defaultProps.announcement),
unlockedText: '',
lockedText: '',
course_id: '3',
content_id: '5',
content_type: 'announcement',
}),
}
renderCourseItemRow({masterCourse})
expect(mockLockIconView.render).toHaveBeenCalled()
})
it('renders peer review icon if peer review', () => {
const {container} = renderCourseItemRow({peerReview: true})
expect(container.querySelector('.ic-item-row__peer_review')).toBeInTheDocument()
})
it('renders master course lock icon if isChildCourse', () => {
const masterCourse = {
courseData: {isChildCourse: true, masterCourse: {id: '1'}},
getLockOptions: () => ({
model: new AnnouncementModel(defaultProps.announcement),
unlockedText: '',
lockedText: '',
course_id: '3',
content_id: '5',
content_type: 'announcement',
}),
}
renderCourseItemRow({masterCourse})
expect(mockLockIconView.render).toHaveBeenCalled()
})
it('renders no master course lock icon if no master course data provided', () => {
const masterCourse = {
courseData: {},
getLockOptions: () => ({}),
}
renderCourseItemRow({masterCourse})
expect(mockLockIconView.render).not.toHaveBeenCalled()
})
it('renders no master course lock icon if isMasterCourse and isChildCourse are false', () => {
const masterCourse = {
courseData: {isMasterCourse: false, isChildCourse: false},
getLockOptions: () => ({}),
}
renderCourseItemRow({masterCourse})
expect(mockLockIconView.render).not.toHaveBeenCalled()
})
it('calls onSelectChanged when checkbox is toggled', () => {
const onSelectedChanged = jest.fn()
renderCourseItemRow({selectable: true, onSelectedChanged})
fireEvent.click(screen.getByRole('checkbox'))
expect(onSelectedChanged).toHaveBeenCalledWith({id: '5', selected: true})
})
it('renders no manage menu when showManageMenu is false', () => {
renderCourseItemRow({showManageMenu: false})
expect(screen.queryByText(/Manage options for /)).not.toBeInTheDocument()
})
it('renders manage menu when showManageMenu is true and manageMenuOptions is not empty', () => {
renderCourseItemRow({showManageMenu: true, manageMenuOptions: () => null})
expect(screen.getByText(/Manage options for /)).toBeInTheDocument()
})
it('does not render a clickable div if the body is empty', () => {
const {container} = renderCourseItemRow({body: null})
expect(container.querySelectorAll('.ic-item-row__content-link')).toHaveLength(1)
})
})

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2018 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react'
import {render, screen} from '@testing-library/react'
import TruncateWithTooltip from '../TruncateWithTooltip'
jest.mock('@instructure/ui-truncate-text', () => ({
TruncateText: ({children}) => <span data-testid="truncate-text">{children}</span>,
}))
jest.mock('@instructure/ui-tooltip', () => ({
Tooltip: ({renderTip}) => <div role="tooltip">{renderTip}</div>,
}))
const defaultProps = (props = {}) => ({
mountNode: document.createElement('div'),
...props,
})
const renderTruncateWithTooltip = (text, props = {}) => {
const ref = React.createRef()
const wrapper = render(
<TruncateWithTooltip {...defaultProps(props)} ref={ref}>
{text}
</TruncateWithTooltip>
)
return {wrapper, ref}
}
describe('TruncateWithTooltip', () => {
it('renders short text', () => {
renderTruncateWithTooltip('This is some text')
expect(screen.getByText('This is some text')).toBeInTheDocument()
})
it('shows TruncateText if text is not truncated', () => {
renderTruncateWithTooltip('TruncateText')
expect(screen.getByText('TruncateText')).toBeInTheDocument()
expect(screen.getByTestId('truncate-text')).toBeInTheDocument()
})
it('does not include a tooltip for short text', () => {
renderTruncateWithTooltip('This is some text')
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument()
})
it('shows Tooltip for truncated text', () => {
const {ref} = renderTruncateWithTooltip('Tooltip', '100px')
ref.current.setState({isTruncated: true})
expect(screen.getByRole('tooltip')).toBeInTheDocument()
})
})

View File

@ -17,31 +17,36 @@
*/
import React from 'react'
import {mount, shallow} from 'enzyme'
import UnreadBadge from '@canvas/unread-badge'
import {render, screen} from '@testing-library/react'
QUnit.module('UnreadBadge component')
const defaultProps = () => ({
const defaultProps = (props = {}) => ({
unreadCount: 2,
totalCount: 5,
unreadLabel: '2 unread replies',
totalLabel: '5 total replies',
unreadLabel: 'unreadLabel',
totalLabel: 'totalLabel',
...props,
})
test('renders the UnreadBadge component', () => {
const tree = mount(<UnreadBadge {...defaultProps()} />)
ok(tree.exists())
})
const renderUnreadBadge = (props = {}) => render(<UnreadBadge {...defaultProps(props)} />)
test('renders the correct unread count', () => {
const tree = shallow(<UnreadBadge {...defaultProps()} />)
const node = tree.find('.ic-unread-badge__unread-count')
equal(node.text(), '2')
})
describe('UnreadBadge', () => {
it('renders the UnreadBadge component', () => {
renderUnreadBadge()
test('renders the correct total count', () => {
const tree = shallow(<UnreadBadge {...defaultProps()} />)
const node = tree.find('.ic-unread-badge__total-count')
equal(node.text(), '5')
expect(screen.getByText('unreadLabel')).toBeInTheDocument()
expect(screen.getByText('totalLabel')).toBeInTheDocument()
})
it('renders the correct unread count', () => {
renderUnreadBadge()
expect(screen.getByText('2 unread replies')).toBeInTheDocument()
})
it('renders the correct total count', () => {
renderUnreadBadge()
expect(screen.getByText('5 total replies')).toBeInTheDocument()
})
})