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()
+ })
})
})