diff --git a/jest/jest-setup.js b/jest/jest-setup.js index f4a17e1c0a3..0ac541597c4 100644 --- a/jest/jest-setup.js +++ b/jest/jest-setup.js @@ -56,7 +56,10 @@ const ignoredErrors = [ /Warning: This synthetic event is reused for performance reasons/, ] const globalWarn = global.console.warn -const ignoredWarnings = [/JQMIGRATE:/] // ignore warnings about jquery migrate; these are muted globally when not in a jest test +const ignoredWarnings = [ + /JQMIGRATE:/, // ignore warnings about jquery migrate; these are muted globally when not in a jest test + /componentWillReceiveProps/, // ignore warnings about componentWillReceiveProps; this method is deprecated and will be removed with react upgrades +] global.console = { log: console.log, diff --git a/spec/javascripts/jsx/calendar/scheduler/components/FindAppointmentSpec.jsx b/spec/javascripts/jsx/calendar/scheduler/components/FindAppointmentSpec.jsx deleted file mode 100644 index 126ef45eb20..00000000000 --- a/spec/javascripts/jsx/calendar/scheduler/components/FindAppointmentSpec.jsx +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2016 - 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 . - */ - -import React from 'react' -import {shallow, mount} from 'enzyme' -import FindAppointmentApp from 'ui/features/calendar/react/scheduler/components/FindAppointment' - -QUnit.module('FindAppointmentApp') - -test('renders the FindAppoint component', () => { - const courses = [ - {name: 'testCourse1', asset_string: 'thing1'}, - {name: 'testCourse2', asset_string: 'thing2'}, - ] - - const store = { - getState() { - return { - inFindAppointmentMode: false, - } - }, - } - - const wrapper = shallow() - equal(wrapper.find('#FindAppointmentButton').text(), 'Find Appointment') -}) - -test('correct button renders', () => { - const courses = [ - {name: 'testCourse1', asset_string: 'thing1'}, - {name: 'testCourse2', asset_string: 'thing2'}, - ] - - const store = { - getState() { - return { - inFindAppointmentMode: true, - } - }, - } - - const wrapper = shallow() - equal(wrapper.find('#FindAppointmentButton').text(), 'Close') -}) - -test('selectCourse sets the proper selected course', () => { - const courses = [ - {id: 1, name: 'testCourse1', asset_string: 'thing1'}, - {id: 2, name: 'testCourse2', asset_string: 'thing2'}, - ] - - const store = { - getState() { - return { - inFindAppointmentMode: false, - } - }, - } - - const wrapper = mount() - wrapper.instance().selectCourse(2) - deepEqual(wrapper.state('selectedCourse'), courses[1]) -}) diff --git a/spec/javascripts/jsx/calendar/scheduler/components/appointment_groups/TimeBlockSelectorSpec.jsx b/spec/javascripts/jsx/calendar/scheduler/components/appointment_groups/TimeBlockSelectorSpec.jsx deleted file mode 100644 index d3bb73cbbe2..00000000000 --- a/spec/javascripts/jsx/calendar/scheduler/components/appointment_groups/TimeBlockSelectorSpec.jsx +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2016 - 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 . - */ - -import React from 'react' -import ReactDOM from 'react-dom' -import TestUtils from 'react-dom/test-utils' -import {mount, shallow} from 'enzyme' -import TimeBlockSelector from 'ui/features/calendar_appointment_group_edit/react/TimeBlockSelector' -import TimeBlockSelectRow from 'ui/features/calendar_appointment_group_edit/react/TimeBlockSelectRow' - -let props - -QUnit.module('TimeBlockSelector', { - setup() { - props = { - timeData: [], - onChange() {}, - } - }, - teardown() { - props = null - }, -}) - -test('it renders', () => { - const wrapper = mount() - ok(wrapper) -}) - -test('it renders TimeBlockSelectRows in their own container', () => { - // Adding new blank rows is dependent on TimeBlockSelectRows being the last - // item in the container - const wrapper = shallow() - const children = wrapper.find('.TimeBlockSelector__Rows').children() - equal(children.last().type(), TimeBlockSelectRow) -}) - -test('handleSlotDivision divides slots and adds new rows to the selector', () => { - const component = TestUtils.renderIntoDocument() - // eslint-disable-next-line react/no-find-dom-node - const domNode = ReactDOM.findDOMNode(component) - const input = domNode.querySelector('#TimeBlockSelector__DivideSection-Input') - input.value = 60 - TestUtils.Simulate.change(input) - const newRow = component.state.timeBlockRows[0] - newRow.timeData.startTime = new Date('2016-10-26T15:00:00.000Z') - newRow.timeData.endTime = new Date('2016-10-26T20:00:00.000Z') - component.setState({ - timeBlockRows: [newRow, {timeData: {startTime: null, endTime: null}}], - }) - component.handleSlotDivision() - equal(component.state.timeBlockRows.length, 6) -}) - -test('handleSlotAddition adds new time slot with time', () => { - const component = TestUtils.renderIntoDocument() - const newRow = component.state.timeBlockRows[0] - newRow.timeData.startTime = new Date('Oct 26 2016 10:00') - newRow.timeData.endTime = new Date('Oct 26 2016 15:00') - equal(component.state.timeBlockRows.length, 1) - component.addRow(newRow) - equal(component.state.timeBlockRows.length, 2) -}) - -test('handleSlotAddition adds new time slot without time', () => { - const component = TestUtils.renderIntoDocument() - equal(component.state.timeBlockRows.length, 1) - component.addRow() - equal(component.state.timeBlockRows.length, 2) -}) - -test('handleSlotDeletion delete a time slot with time', () => { - const component = TestUtils.renderIntoDocument() - const newRow = component.state.timeBlockRows[0] - newRow.timeData.startTime = new Date('Oct 26 2016 10:00') - newRow.timeData.endTime = new Date('Oct 26 2016 15:00') - component.addRow(newRow) - equal(component.state.timeBlockRows.length, 2) - component.deleteRow(component.state.timeBlockRows[1].slotEventId) - equal(component.state.timeBlockRows.length, 1) -}) - -test('handleSetData setting time data', () => { - const component = TestUtils.renderIntoDocument() - const newRow = component.state.timeBlockRows[0] - newRow.timeData.startTime = new Date('Oct 26 2016 10:00') - newRow.timeData.endTime = new Date('Oct 26 2016 15:00') - component.addRow(newRow) - newRow.timeData.startTime = new Date('Oct 26 2016 11:00') - newRow.timeData.endTime = new Date('Oct 26 2016 16:00') - component.handleSetData(component.state.timeBlockRows[1].slotEventId, newRow) - deepEqual(component.state.timeBlockRows[0].timeData.endTime, new Date('Oct 26 2016 16:00')) -}) - -test('calls onChange when there are modifications made', () => { - props.onChange = sinon.spy() - const component = TestUtils.renderIntoDocument() - // eslint-disable-next-line react/no-find-dom-node - const domNode = ReactDOM.findDOMNode(component) - const input = domNode.querySelector('#TimeBlockSelector__DivideSection-Input') - input.value = 60 - TestUtils.Simulate.change(input) - const newRow = component.state.timeBlockRows[0] - newRow.timeData.startTime = new Date('Oct 26 2016 10:00') - newRow.timeData.endTime = new Date('Oct 26 2016 15:00') - component.setState({ - timeBlockRows: [newRow], - }) - ok(props.onChange.called) -}) diff --git a/spec/javascripts/jsx/collaborations/CollaborationsToolLaunchSpec.jsx b/spec/javascripts/jsx/collaborations/CollaborationsToolLaunchSpec.jsx deleted file mode 100644 index d1c9b3426ff..00000000000 --- a/spec/javascripts/jsx/collaborations/CollaborationsToolLaunchSpec.jsx +++ /dev/null @@ -1,96 +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 . - */ - -import $ from 'jquery' -import 'jquery-migrate' -import React from 'react' -import {mount} from 'enzyme' -import CollaborationsToolLaunch from 'ui/features/lti_collaborations/react/CollaborationsToolLaunch' - -let fixtures - -QUnit.module('CollaborationsToolLaunch screenreader functionality', { - setup() { - fixtures = $('#fixtures') - fixtures.append('
') - ENV.LTI_LAUNCH_FRAME_ALLOWANCES = ['midi', 'media'] - }, - - teardown() { - fixtures.empty() - ENV.LTI_LAUNCH_FRAME_ALLOWANCES = undefined - }, -}) - -test('shows beginning info alert and adds styles to iframe', () => { - const wrapper = mount() - wrapper.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) - wrapper.find('.before_external_content_info_alert').simulate('focus') - equal(wrapper.state().beforeExternalContentAlertClass, '') - deepEqual(wrapper.state().iframeStyle, {border: '2px solid #0374B5', width: '-4px'}) -}) - -test('shows ending info alert and adds styles to iframe', () => { - const wrapper = mount() - wrapper.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) - wrapper.find('.after_external_content_info_alert').simulate('focus') - equal(wrapper.state().afterExternalContentAlertClass, '') - deepEqual(wrapper.state().iframeStyle, {border: '2px solid #0374B5', width: '-4px'}) -}) - -test('hides beginning info alert and adds styles to iframe', () => { - const wrapper = mount() - wrapper.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) - wrapper.find('.before_external_content_info_alert').simulate('focus') - wrapper.find('.before_external_content_info_alert').simulate('blur') - equal(wrapper.state().beforeExternalContentAlertClass, 'screenreader-only') - deepEqual(wrapper.state().iframeStyle, {border: 'none', width: '100%'}) -}) - -test('hides ending info alert and adds styles to iframe', () => { - const wrapper = mount() - wrapper.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) - wrapper.find('.after_external_content_info_alert').simulate('focus') - wrapper.find('.after_external_content_info_alert').simulate('blur') - equal(wrapper.state().afterExternalContentAlertClass, 'screenreader-only') - deepEqual(wrapper.state().iframeStyle, {border: 'none', width: '100%'}) -}) - -test("doesn't show alerts or add border to iframe by default", () => { - const wrapper = mount() - wrapper.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) - equal(wrapper.state().beforeExternalContentAlertClass, 'screenreader-only') - equal(wrapper.state().afterExternalContentAlertClass, 'screenreader-only') - deepEqual(wrapper.state().iframeStyle, {}) -}) - -test('sets the iframe allowances', () => { - const wrapper = mount() - wrapper.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) - equal(wrapper.state().beforeExternalContentAlertClass, 'screenreader-only') - equal(wrapper.state().afterExternalContentAlertClass, 'screenreader-only') - ok( - wrapper.find('.tool_launch').instance().getAttribute('allow'), - ENV.LTI_LAUNCH_FRAME_ALLOWANCES.join('; ') - ) -}) - -test("sets the 'data-lti-launch' attribute on the iframe", () => { - const wrapper = mount() - equal(wrapper.find('.tool_launch').instance().getAttribute('data-lti-launch'), 'true') -}) diff --git a/spec/javascripts/jsx/collaborations/GettingStartedCollaborationsSpec.jsx b/spec/javascripts/jsx/collaborations/GettingStartedCollaborationsSpec.jsx deleted file mode 100644 index c549d994849..00000000000 --- a/spec/javascripts/jsx/collaborations/GettingStartedCollaborationsSpec.jsx +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2016 - 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 . - */ - -import React from 'react' -import {mount} from 'enzyme' -import GettingStartedCollaborations from 'ui/features/lti_collaborations/react/GettingStartedCollaborations' - -QUnit.module('GettingStartedCollaborations') - -function setEnvironment(roles, context) { - ENV.context_asset_string = context - ENV.current_user_roles = roles - ENV.CREATE_PERMISSION = true -} - -test('renders the Getting Startted app div', () => { - setEnvironment([], 'course_4') - const wrapper = mount( - - ) - equal(wrapper.find('.GettingStartedCollaborations').length, 1) -}) - -test('renders the correct content with lti tools configured as a teacher', () => { - setEnvironment(['teacher'], 'course_4') - const wrapper = mount( - - ) - const expectedHeader = 'Getting started with Collaborations' - const expectedContent = - 'Collaborations are web-based tools to work collaboratively on tasks like taking notes or grouped papers. Get started by clicking on the "+ Collaboration" button.' - const expectedLinkText = 'Learn more about collaborations' - - equal(expectedHeader, wrapper.find('.ic-Action-header__Heading').text()) - equal(expectedContent, wrapper.find('p').text()) - equal(expectedLinkText, wrapper.find('a').text()) -}) - -test('renders the correct content with no lti tools configured data as a teacher', () => { - setEnvironment(['teacher'], 'course_4') - const wrapper = mount( - - ) - const expectedHeader = 'No Collaboration Apps' - const expectedContent = - 'Collaborations are web-based tools to work collaboratively on tasks like taking notes or grouped papers. Get started by adding a collaboration app.' - const expectedLinkText = 'Learn more about collaborations' - - equal(expectedHeader, wrapper.find('.ic-Action-header__Heading').text()) - equal(expectedContent, wrapper.find('p').text()) - equal(expectedLinkText, wrapper.find('a').text()) -}) - -test('renders the correct content with no collaborations data as a student', () => { - setEnvironment(['student'], 'course_4') - const wrapper = mount( - - ) - const expectedHeader = 'No Collaboration Apps' - const expectedContent = - 'You have no Collaboration apps configured. Talk to your teacher to get some set up.' - - equal(expectedHeader, wrapper.find('.ic-Action-header__Heading').text()) - equal(expectedContent, wrapper.find('p').text()) -}) - -test('renders the correct content with lti tools configured as a student with create permission enabled', () => { - setEnvironment(['student'], 'course_4') - const wrapper = mount( - - ) - const expectedHeader = 'Getting started with Collaborations' - const expectedContent = - 'Collaborations are web-based tools to work collaboratively on tasks like taking notes or grouped papers. Get started by clicking on the "+ Collaboration" button.' - const expectedLinkText = 'Learn more about collaborations' - - equal(expectedHeader, wrapper.find('.ic-Action-header__Heading').text()) - equal(expectedContent, wrapper.find('p').text()) - equal(expectedLinkText, wrapper.find('a').text()) -}) - -test('renders the correct content with lti tools configured as a student with create permission disabled', () => { - setEnvironment(['student'], 'course_4') - ENV.CREATE_PERMISSION = false - - const wrapper = mount( - - ) - const expectedHeader = 'Getting started with Collaborations' - const expectedContent = - 'Collaborations are web-based tools to work collaboratively on tasks like taking notes or grouped papers. Talk to your teacher to get started.' - const expectedLinkText = 'Learn more about collaborations' - - equal(expectedHeader, wrapper.find('.ic-Action-header__Heading').text()) - equal(expectedContent, wrapper.find('p').text()) - equal(expectedLinkText, wrapper.find('a').text()) -}) diff --git a/ui/features/calendar/react/scheduler/components/__tests__/FindAppointment.test.jsx b/ui/features/calendar/react/scheduler/components/__tests__/FindAppointment.test.jsx new file mode 100644 index 00000000000..8f7e1f65def --- /dev/null +++ b/ui/features/calendar/react/scheduler/components/__tests__/FindAppointment.test.jsx @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2024 - 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 . + */ + +import React from 'react' +import {shallow} from 'enzyme' +import {render} from '@testing-library/react' +import FindAppointmentApp from '../FindAppointment' + + +const courses = [ + {id: 1, name: 'testCourse1', asset_string: 'thing1'}, + {id: 2, name: 'testCourse2', asset_string: 'thing2'}, +] +describe('FindAppointmentApp', () => { + + test('renders the FindAppoint component', () => { + const store = { + getState() { + return { + inFindAppointmentMode: false, + } + }, + } + + const wrapper = shallow() + expect(wrapper.find('#FindAppointmentButton').text()).toEqual('Find Appointment') + }) + + test('correct button renders', () => { + const store = { + getState() { + return { + inFindAppointmentMode: true, + } + }, + } + + const wrapper = shallow() + expect(wrapper.find('#FindAppointmentButton').text()).toEqual('Close') + }) + + test('selectCourse sets the proper selected course', () => { + const store = { + getState() { + return { + inFindAppointmentMode: false, + } + }, + } + + const ref = React.createRef() + render() + ref.current.selectCourse(2) + expect(ref.current.state.selectedCourse).toEqual(courses[1]) + }) +}) diff --git a/ui/features/calendar_appointment_group_edit/react/TimeBlockSelectRow.jsx b/ui/features/calendar_appointment_group_edit/react/TimeBlockSelectRow.jsx index 748407f3933..6853ea5bd9d 100644 --- a/ui/features/calendar_appointment_group_edit/react/TimeBlockSelectRow.jsx +++ b/ui/features/calendar_appointment_group_edit/react/TimeBlockSelectRow.jsx @@ -45,9 +45,9 @@ const timeToString = (dateObj, format) => { class TimeBlockSelectorRow extends React.Component { static propTypes = { timeData: PropTypes.shape({ - date: PropTypes.date, - startTime: PropTypes.date, - endTime: PropTypes.date, + date: PropTypes.any, + startTime: PropTypes.any, + endTime: PropTypes.any, }).isRequired, slotEventId: PropTypes.string, readOnly: PropTypes.bool, diff --git a/ui/features/calendar_appointment_group_edit/react/__tests__/TimeBlockSelector.test.jsx b/ui/features/calendar_appointment_group_edit/react/__tests__/TimeBlockSelector.test.jsx new file mode 100644 index 00000000000..b72405bbb95 --- /dev/null +++ b/ui/features/calendar_appointment_group_edit/react/__tests__/TimeBlockSelector.test.jsx @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2024 - 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 . + */ + +import React from 'react' +import TestUtils from 'react-dom/test-utils' +import {render} from '@testing-library/react' +import {shallow} from 'enzyme' +import TimeBlockSelector from '../TimeBlockSelector' +import TimeBlockSelectRow from '../TimeBlockSelectRow' +import sinon from 'sinon' + +let props = { + timeData: [ + { + slotEventId: '1', + timeData: { + date: '2016-10-26', + startTime: '10:00', + endTime: '15:00', + } + } + ], + onChange() {}, +} + +describe('TimeBlockSelector', () => { + test('it renders', () => { + const wrapper = render() + expect(wrapper).toBeTruthy() + }) + + test('it renders TimeBlockSelectRows in their own container', () => { + // Adding new blank rows is dependent on TimeBlockSelectRows being the last + // item in the container + const wrapper = shallow() + const children = wrapper.find('.TimeBlockSelector__Rows').children() + expect(children.last().type()).toEqual(TimeBlockSelectRow) + }) + + test('handleSlotDivision divides slots and adds new rows to the selector', () => { + const ref = React.createRef() + const component = render() + const input = component.container.querySelector('#TimeBlockSelector__DivideSection-Input') + input.value = 60 + TestUtils.Simulate.change(input) + const newRow = ref.current.state.timeBlockRows[0] + newRow.timeData.startTime = new Date('2016-10-26T15:00:00.000Z') + newRow.timeData.endTime = new Date('2016-10-26T20:00:00.000Z') + ref.current.setState({ + timeBlockRows: [newRow, {slotEventId: 'asdf', timeData: {startTime: null, endTime: null}}], + }) + ref.current.handleSlotDivision() + expect(ref.current.state.timeBlockRows.length).toEqual(6) + }) + + test('handleSlotAddition adds new time slot with time', () => { + const ref = React.createRef() + render() + const newRow = ref.current.state.timeBlockRows[0] + newRow.timeData.startTime = new Date('Oct 26 2016 10:00') + newRow.timeData.endTime = new Date('Oct 26 2016 15:00') + expect(ref.current.state.timeBlockRows.length).toEqual(1) + ref.current.addRow(newRow) + expect(ref.current.state.timeBlockRows.length).toEqual(2) + }) + + test('handleSlotAddition adds new time slot without time', () => { + const ref = React.createRef() + render() + expect(ref.current.state.timeBlockRows.length).toEqual(1) + ref.current.addRow() + expect(ref.current.state.timeBlockRows.length).toEqual(2) + }) + + test('handleSlotDeletion delete a time slot with time', () => { + const ref = React.createRef() + render() + const newRow = ref.current.state.timeBlockRows[0] + newRow.timeData.startTime = new Date('Oct 26 2016 10:00') + newRow.timeData.endTime = new Date('Oct 26 2016 15:00') + ref.current.addRow(newRow) + expect(ref.current.state.timeBlockRows.length).toEqual(2) + ref.current.deleteRow(ref.current.state.timeBlockRows[1].slotEventId) + expect(ref.current.state.timeBlockRows.length).toEqual(1) + }) + + test('handleSetData setting time data', () => { + const ref = React.createRef() + const component = render() + const newRow = ref.current.state.timeBlockRows[0] + newRow.timeData.startTime = new Date('Oct 26 2016 10:00') + newRow.timeData.endTime = new Date('Oct 26 2016 15:00') + ref.current.addRow(newRow) + newRow.timeData.startTime = new Date('Oct 26 2016 11:00') + newRow.timeData.endTime = new Date('Oct 26 2016 16:00') + ref.current.handleSetData(ref.current.state.timeBlockRows[1].slotEventId, newRow) + expect(ref.current.state.timeBlockRows[0].timeData.endTime).toEqual(new Date('Oct 26 2016 16:00')) + }) + + test('calls onChange when there are modifications made', async () => { + props.onChange = sinon.spy() + const ref = React.createRef() + const component = render() + const input = component.container.querySelector('#TimeBlockSelector__DivideSection-Input') + input.value = 60 + // const user = userEvent.setup({delay: null}) + // await user.focus(input) + const newRow = ref.current.state.timeBlockRows[0] + newRow.timeData.startTime = new Date('Oct 26 2016 10:00') + newRow.timeData.endTime = new Date('Oct 26 2016 15:00') + ref.current.setState({ + timeBlockRows: [newRow], + }) + expect(props.onChange.called).toBeTruthy() + }) +}) diff --git a/ui/features/lti_collaborations/react/__tests__/CollaborationsToolLaunch.test.jsx b/ui/features/lti_collaborations/react/__tests__/CollaborationsToolLaunch.test.jsx new file mode 100644 index 00000000000..811b0a84aa8 --- /dev/null +++ b/ui/features/lti_collaborations/react/__tests__/CollaborationsToolLaunch.test.jsx @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2024 - 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 . + */ + +import $ from 'jquery' +import 'jquery-migrate' +import React from 'react' +import {render} from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import CollaborationsToolLaunch from '../CollaborationsToolLaunch' + +let fixtures + +describe('CollaborationsToolLaunch screenreader functionality', () => { + beforeEach(() => { + document.body.innerHTML = '
' + fixtures = document.getElementById('main') + ENV.LTI_LAUNCH_FRAME_ALLOWANCES = ['midi', 'media'] + }) + + afterEach(() => { + fixtures.innerHTML = '' + ENV.LTI_LAUNCH_FRAME_ALLOWANCES = undefined + }) + + + test('shows beginning info alert and adds styles to iframe', () => { + const ref = React.createRef() + const wrapper = render() + ref.current.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) + const alert = wrapper.container.querySelector('.before_external_content_info_alert') + alert.focus() + expect(ref.current.state.beforeExternalContentAlertClass).toEqual('') + expect(ref.current.state.iframeStyle).toEqual({border: '2px solid #0374B5', width: '-4px'}) + }) + + test('shows ending info alert and adds styles to iframe', () => { + const ref = React.createRef() + const wrapper = render() + ref.current.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) + const alert = wrapper.container.querySelector('.after_external_content_info_alert') + alert.focus() + expect(ref.current.state.afterExternalContentAlertClass).toEqual('') + expect(ref.current.state.iframeStyle).toEqual({border: '2px solid #0374B5', width: '-4px'}) + }) + + test('hides beginning info alert and adds styles to iframe', () => { + const ref = React.createRef() + const wrapper = render() + ref.current.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) + const alert = wrapper.container.querySelector('.before_external_content_info_alert') + alert.focus() + alert.blur() + expect(ref.current.state.beforeExternalContentAlertClass).toEqual('screenreader-only') + expect(ref.current.state.iframeStyle).toEqual({border: 'none', width: '100%'}) + }) + + test('hides ending info alert and adds styles to iframe', () => { + const ref = React.createRef() + const wrapper = render() + ref.current.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) + const alert = wrapper.container.querySelector('.after_external_content_info_alert') + alert.focus() + alert.blur() + expect(ref.current.state.afterExternalContentAlertClass).toEqual('screenreader-only') + expect(ref.current.state.iframeStyle).toEqual({border: 'none', width: '100%'}) + }) + + test("doesn't show alerts or add border to iframe by default", () => { + const ref = React.createRef() + render() + ref.current.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) + expect(ref.current.state.beforeExternalContentAlertClass).toEqual('screenreader-only') + expect(ref.current.state.afterExternalContentAlertClass).toEqual('screenreader-only') + expect(ref.current.state.iframeStyle).toEqual({}) + }) + + test('sets the iframe allowances', () => { + const ref = React.createRef() + const wrapper = render() + ref.current.setState({toolLaunchUrl: 'http://localhost:3000/messages/blti'}) + expect(ref.current.state.beforeExternalContentAlertClass).toEqual('screenreader-only') + expect(ref.current.state.afterExternalContentAlertClass).toEqual('screenreader-only') + expect(wrapper.container.querySelector('.tool_launch').getAttribute('allow')).toEqual(ENV.LTI_LAUNCH_FRAME_ALLOWANCES.join('; ')) + }) + + test("sets the 'data-lti-launch' attribute on the iframe", () => { + const wrapper = render() + expect(wrapper.container.querySelector('.tool_launch').getAttribute('data-lti-launch')).toEqual('true') + }) +}) diff --git a/ui/features/lti_collaborations/react/__tests__/GettingStartedCollaborations.test.jsx b/ui/features/lti_collaborations/react/__tests__/GettingStartedCollaborations.test.jsx new file mode 100644 index 00000000000..0f20466738d --- /dev/null +++ b/ui/features/lti_collaborations/react/__tests__/GettingStartedCollaborations.test.jsx @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2024 - 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 . + */ + +import React from 'react' +import {render} from '@testing-library/react' +import GettingStartedCollaborations from '../GettingStartedCollaborations' + +describe('GettingStartedCollaborations', () => { + + function setEnvironment(roles, context) { + ENV.context_asset_string = context + ENV.current_user_roles = roles + ENV.CREATE_PERMISSION = true + } + + test('renders the Getting Startted app div', () => { + setEnvironment([], 'course_4') + const wrapper = render( + + ) + expect(wrapper.container.querySelectorAll('.GettingStartedCollaborations').length).toEqual(1) + }) + + test('renders the correct content with lti tools configured as a teacher', () => { + setEnvironment(['teacher'], 'course_4') + const wrapper = render( + + ) + const expectedHeader = 'Getting started with Collaborations' + const expectedContent = + 'Collaborations are web-based tools to work collaboratively on tasks like taking notes or grouped papers. Get started by clicking on the "+ Collaboration" button.' + const expectedLinkText = 'Learn more about collaborations' + + expect(expectedHeader).toEqual(wrapper.container.querySelector('.ic-Action-header__Heading').textContent) + expect(expectedContent).toEqual(wrapper.container.querySelector('p').textContent) + expect(expectedLinkText).toEqual(wrapper.container.querySelector('a').textContent) + }) + + test('renders the correct content with no lti tools configured data as a teacher', () => { + setEnvironment(['teacher'], 'course_4') + const wrapper = render( + + ) + const expectedHeader = 'No Collaboration Apps' + const expectedContent = + 'Collaborations are web-based tools to work collaboratively on tasks like taking notes or grouped papers. Get started by adding a collaboration app.' + const expectedLinkText = 'Learn more about collaborations' + + expect(expectedHeader).toEqual(wrapper.container.querySelector('.ic-Action-header__Heading').textContent) + expect(expectedContent).toEqual(wrapper.container.querySelector('p').textContent) + expect(expectedLinkText).toEqual(wrapper.container.querySelector('a').textContent) + }) + + test('renders the correct content with no collaborations data as a student', () => { + setEnvironment(['student'], 'course_4') + const wrapper = render( + + ) + const expectedHeader = 'No Collaboration Apps' + const expectedContent = + 'You have no Collaboration apps configured. Talk to your teacher to get some set up.' + + expect(expectedHeader).toEqual(wrapper.container.querySelector('.ic-Action-header__Heading').textContent) + expect(expectedContent).toEqual(wrapper.container.querySelector('p').textContent) + }) + + test('renders the correct content with lti tools configured as a student with create permission enabled', () => { + setEnvironment(['student'], 'course_4') + const wrapper = render( + + ) + const expectedHeader = 'Getting started with Collaborations' + const expectedContent = + 'Collaborations are web-based tools to work collaboratively on tasks like taking notes or grouped papers. Get started by clicking on the "+ Collaboration" button.' + const expectedLinkText = 'Learn more about collaborations' + + expect(expectedHeader).toEqual(wrapper.container.querySelector('.ic-Action-header__Heading').textContent) + expect(expectedContent).toEqual(wrapper.container.querySelector('p').textContent) + expect(expectedLinkText).toEqual(wrapper.container.querySelector('a').textContent) + }) + + test('renders the correct content with lti tools configured as a student with create permission disabled', () => { + setEnvironment(['student'], 'course_4') + ENV.CREATE_PERMISSION = false + + const wrapper = render( + + ) + const expectedHeader = 'Getting started with Collaborations' + const expectedContent = + 'Collaborations are web-based tools to work collaboratively on tasks like taking notes or grouped papers. Talk to your teacher to get started.' + const expectedLinkText = 'Learn more about collaborations' + + expect(expectedHeader).toEqual(wrapper.container.querySelector('.ic-Action-header__Heading').textContent) + expect(expectedContent).toEqual(wrapper.container.querySelector('p').textContent) + expect(expectedLinkText).toEqual(wrapper.container.querySelector('a').textContent) + }) +}) diff --git a/spec/javascripts/jsx/canvas_cropper/canvasCropperSpec.jsx b/ui/shared/avatar-dialog-view/react/__tests__/cropper.test.jsx similarity index 95% rename from spec/javascripts/jsx/canvas_cropper/canvasCropperSpec.jsx rename to ui/shared/avatar-dialog-view/react/__tests__/cropper.test.jsx index 4f747749267..15e46c27f8b 100644 --- a/spec/javascripts/jsx/canvas_cropper/canvasCropperSpec.jsx +++ b/ui/shared/avatar-dialog-view/react/__tests__/cropper.test.jsx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 - present Instructure, Inc. + * Copyright (C) 2024 - present Instructure, Inc. * * This file is part of Canvas. * @@ -17,40 +17,37 @@ */ import React from 'react' -import {shallow, mount} from 'enzyme' -import Cropper from '@canvas/avatar-dialog-view/react/cropper' +import {render} from '@testing-library/react' +import Cropper from '../cropper' -let file, wrapper +let file, wrapper, ref -QUnit.module('CanvasCropper', hooks => { - hooks.beforeEach(() => { +describe('CanvasCropper', () => { + beforeEach(() => { const blob = dataURItoBlob(filedata) + ref = React.createRef() file = new File([blob], 'test.jpg', { type: 'image/jpeg', lastModified: Date.now(), }) - wrapper = mount() + wrapper = render() }) test('renders the component', () => { - ok(wrapper.find('.CanvasCropper').exists(), 'cropper is in the DOM') + expect(wrapper.container.querySelector('.CanvasCropper')).toBeTruthy() }) test('renders the image', () => { - ok(wrapper.find('.Cropper-image').exists(), 'cropper image is in the DOM') + expect(wrapper.container.querySelector('.Cropper-image')).toBeTruthy() }) - test('getImage returns cropped image object', assert => { - assert.expect(1) - const done = assert.async() - - wrapper - .instance() - .crop() - .then(image => { - ok(image instanceof Blob, 'image object is a blob') - done() - }) + test('getImage returns cropped image object', async () => { + const done = jest.fn() + ref.current.crop().then(image => { + expect(image instanceof Blob).toBeTruthy() + expect(done).toHaveBeenCalledTimes(1) + done() + }) }) })