@instructure/canvas-rce 6.0.0 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/__tests__/common/indicate.test.js +5 -6
- package/es/bridge/Bridge.js +2 -4
- package/es/canvasFileBrowser/FileBrowser.js +2 -4
- package/es/common/browser.js +2 -2
- package/es/common/fileUrl.js +13 -3
- package/es/defaultTinymceConfig.d.ts +1 -1
- package/es/defaultTinymceConfig.js +149 -114
- package/es/enhance-user-content/enhance_user_content.js +7 -1
- package/es/enhance-user-content/instructure_helper.js +4 -0
- package/es/enhance-user-content/youtube_overlay.d.ts +1 -0
- package/es/enhance-user-content/youtube_overlay.js +87 -0
- package/es/format-message.d.js +1 -0
- package/es/format-message.js +5 -0
- package/es/index.d.ts +1 -1
- package/es/rce/RCE.d.ts +0 -1
- package/es/rce/RCE.js +5 -10
- package/es/rce/RCEGlobals.d.ts +0 -2
- package/es/rce/RCEGlobals.js +0 -1
- package/es/rce/RCEVariants.d.ts +8 -3
- package/es/rce/RCEVariants.js +36 -10
- package/es/rce/RCEWrapper.d.ts +3 -5
- package/es/rce/RCEWrapper.js +68 -83
- package/es/rce/RCEWrapperProps.d.ts +1 -1
- package/es/rce/ShowOnFocusButton/index.js +4 -2
- package/es/rce/StatusBar.js +61 -15
- package/es/rce/alertHandler.js +6 -7
- package/es/rce/plugins/instructure-ui-icons/plugin.js +2 -2
- package/es/rce/plugins/instructure_equation/EquationEditorModal/index.d.ts +1 -1
- package/es/rce/plugins/instructure_equation/EquationEditorModal/index.js +6 -10
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Course.d.ts +5 -15
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/Course.js +4 -10
- package/es/rce/plugins/instructure_image/ImageEmbedOptions.d.ts +7 -0
- package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +49 -9
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.d.ts +1 -8
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialog.js +13 -33
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.d.ts +1 -2
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.js +2 -1
- package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +1 -2
- package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +2 -4
- package/es/rce/plugins/instructure_record/VideoOptionsTray/index.js +10 -7
- package/es/rce/plugins/shared/ContentSelection.js +4 -7
- package/es/rce/plugins/shared/DimensionsInput/index.js +3 -3
- package/es/rce/plugins/shared/FixedContentTray.d.ts +7 -23
- package/es/rce/plugins/shared/FixedContentTray.js +7 -16
- package/es/rce/plugins/shared/ImageCropper/constants.d.ts +1 -1
- package/es/rce/plugins/shared/ImageCropper/constants.js +1 -1
- package/es/rce/plugins/shared/ImageCropper/controls/CustomNumberInput.js +1 -1
- package/es/rce/plugins/shared/PreviewIcon.js +1 -1
- package/es/rce/plugins/shared/ai_tools/aiicons.d.ts +3 -3
- package/es/rce/plugins/shared/ai_tools/aiicons.js +11 -11
- package/es/rce/plugins/shared/fileTypeUtils.js +1 -1
- package/es/rce/plugins/tinymce-a11y-checker/components/checker.js +7 -1
- package/es/rce/plugins/tinymce-a11y-checker/plugin.js +50 -52
- package/es/rce/plugins/tinymce-a11y-checker/utils/indicate.d.ts +1 -1
- package/es/rce/plugins/tinymce-a11y-checker/utils/indicate.js +1 -1
- package/es/rce/style.js +47 -45
- package/es/rcs/api.d.ts +4 -1
- package/es/rcs/api.js +9 -13
- package/es/translations/locales/ar.js +29 -5
- package/es/translations/locales/ca.js +32 -8
- package/es/translations/locales/cy.js +29 -5
- package/es/translations/locales/da-x-k12.js +29 -5
- package/es/translations/locales/da.js +29 -5
- package/es/translations/locales/de.js +29 -5
- package/es/translations/locales/en-AU-x-unimelb.js +29 -5
- package/es/translations/locales/en-GB-x-ukhe.js +29 -5
- package/es/translations/locales/en.js +20 -5
- package/es/translations/locales/en_AU.js +29 -5
- package/es/translations/locales/en_CA.js +29 -5
- package/es/translations/locales/en_CY.js +29 -5
- package/es/translations/locales/en_GB.js +29 -5
- package/es/translations/locales/es.js +29 -5
- package/es/translations/locales/es_ES.js +29 -5
- package/es/translations/locales/fa_IR.js +0 -3
- package/es/translations/locales/fi.js +29 -5
- package/es/translations/locales/fr.js +29 -5
- package/es/translations/locales/fr_CA.js +30 -6
- package/es/translations/locales/ga.js +46 -22
- package/es/translations/locales/hi.js +29 -5
- package/es/translations/locales/ht.js +29 -5
- package/es/translations/locales/hu.js +0 -6
- package/es/translations/locales/id.js +29 -5
- package/es/translations/locales/is.js +23 -5
- package/es/translations/locales/it.js +29 -5
- package/es/translations/locales/ja.js +29 -5
- package/es/translations/locales/mi.js +29 -5
- package/es/translations/locales/ms.js +29 -5
- package/es/translations/locales/nb-x-k12.js +29 -5
- package/es/translations/locales/nb.js +29 -5
- package/es/translations/locales/nl.js +29 -5
- package/es/translations/locales/nn.js +0 -6
- package/es/translations/locales/pl.js +29 -5
- package/es/translations/locales/pt.js +29 -5
- package/es/translations/locales/pt_BR.js +29 -5
- package/es/translations/locales/ru.js +29 -5
- package/es/translations/locales/sl.js +29 -5
- package/es/translations/locales/sv-x-k12.js +29 -5
- package/es/translations/locales/sv.js +29 -5
- package/es/translations/locales/th.js +29 -5
- package/es/translations/locales/uk_UA.js +0 -6
- package/es/translations/locales/vi.js +29 -5
- package/es/translations/locales/zh-Hans.js +29 -5
- package/es/translations/locales/zh-Hant.js +29 -5
- package/es/translations/locales/zh.js +29 -5
- package/es/translations/locales/zh_HK.js +29 -5
- package/es/util/contextHelper.d.ts +7 -0
- package/{testcafe/axe.test.js → es/util/contextHelper.js} +10 -21
- package/es/util/loadingPlaceholder.js +11 -11
- package/eslint.config.js +3 -25
- package/jest/jest-setup.js +27 -2
- package/jest.config.js +5 -1
- package/package.json +62 -85
- package/testcafe/RCEWrapper.test.js +0 -319
- package/testcafe/StatusBar.test.js +0 -108
- package/testcafe/enhanceUserContent.html +0 -58
- package/testcafe/enhanceUserContent.test.js +0 -44
- package/testcafe/entry.jsx +0 -77
- package/testcafe/testcafe.html +0 -14
- package/webpack.testcafe.config.js +0 -61
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (C) 2019 - present Instructure, Inc.
|
|
3
|
-
*
|
|
4
|
-
* This file is part of Canvas.
|
|
5
|
-
*
|
|
6
|
-
* Canvas is free software: you can redistribute it and/or modify it under
|
|
7
|
-
* the terms of the GNU Affero General Public License as published by the Free
|
|
8
|
-
* Software Foundation, version 3 of the License.
|
|
9
|
-
*
|
|
10
|
-
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
11
|
-
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
12
|
-
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
13
|
-
* details.
|
|
14
|
-
*
|
|
15
|
-
* You should have received a copy of the GNU Affero General Public License along
|
|
16
|
-
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import {Selector, ClientFunction} from 'testcafe'
|
|
20
|
-
fixture`RCEWrapper`.page`./testcafe.html`
|
|
21
|
-
const tinyIframe = Selector('#textarea_ifr')
|
|
22
|
-
const textArea = Selector('.RceHtmlEditor')
|
|
23
|
-
const rceContainer = Selector('.tox-tinymce')
|
|
24
|
-
const toggleButton = Selector('button').withText('</>')
|
|
25
|
-
const linksButton = Selector('[role="button"][aria-label="Links"] .tox-split-button__chevron')
|
|
26
|
-
const externalLinksMenuItem = Selector('[role^="menuitem"][title="External Links"]')
|
|
27
|
-
const courseLinksMenuItem = Selector('[role^="menuitem"][title="Course Links"]')
|
|
28
|
-
const editLinkMenuItem = Selector('[role^="menuitem"][title="Edit Link"]')
|
|
29
|
-
const removeLinkMenuItem = Selector('[role^="menuitem"][title="Remove Link"]')
|
|
30
|
-
const linkDialog = Selector('[data-testid="RCELinkOptionsDialog"]')
|
|
31
|
-
const linkTray = Selector('[data-testid="RCELinkOptionsTray"]')
|
|
32
|
-
const selectword = Selector('#selectword')
|
|
33
|
-
const linksTraySelector = '[role="dialog"][aria-label="Course Links"]'
|
|
34
|
-
const rceBody = Selector('#tinymce')
|
|
35
|
-
const linksPanelSelector = Selector('[data-testid="instructure_links-LinksPanel"]')
|
|
36
|
-
|
|
37
|
-
function named(name) {
|
|
38
|
-
return Selector(`[name="${name}"]`)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function specificValue(value) {
|
|
42
|
-
return Selector(`[value="${value}"]`)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
test.skip('edits in the textarea are reflected in the editor', async t => {
|
|
46
|
-
// const myText = ClientFunction(() => document.querySelector('#tinymce').innerText)
|
|
47
|
-
await t
|
|
48
|
-
.click(toggleButton)
|
|
49
|
-
.typeText(textArea, 'this is new content')
|
|
50
|
-
.click(toggleButton)
|
|
51
|
-
.switchToIframe(tinyIframe)
|
|
52
|
-
|
|
53
|
-
await t.expect(rceBody.withExactText('this is new content').visible).ok().switchToMainWindow()
|
|
54
|
-
|
|
55
|
-
await t
|
|
56
|
-
.click(toggleButton)
|
|
57
|
-
.pressKey('end')
|
|
58
|
-
.typeText(textArea, 'this is more content')
|
|
59
|
-
.click(toggleButton)
|
|
60
|
-
.switchToIframe(tinyIframe)
|
|
61
|
-
.expect(Selector('body').find('p').withExactText('this is new content').visible)
|
|
62
|
-
.ok()
|
|
63
|
-
.expect(Selector('body').find('p').withExactText('this is more content').visible)
|
|
64
|
-
.ok()
|
|
65
|
-
.switchToMainWindow()
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
test.skip('shows the create links menu', async t => {
|
|
69
|
-
await t
|
|
70
|
-
.click(toggleButton)
|
|
71
|
-
.typeText(textArea, '<div>this is <span id="selectword">selected</span> text</div>')
|
|
72
|
-
.click(toggleButton)
|
|
73
|
-
.expect(rceContainer.visible)
|
|
74
|
-
.ok()
|
|
75
|
-
|
|
76
|
-
await t
|
|
77
|
-
.switchToIframe(tinyIframe)
|
|
78
|
-
.selectEditableContent(selectword, selectword)
|
|
79
|
-
.switchToMainWindow()
|
|
80
|
-
.click(linksButton)
|
|
81
|
-
.expect(externalLinksMenuItem.visible)
|
|
82
|
-
.ok()
|
|
83
|
-
.expect(courseLinksMenuItem.visible)
|
|
84
|
-
.ok()
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
test.skip('shows the edit links menu', async t => {
|
|
88
|
-
await t
|
|
89
|
-
.click(toggleButton)
|
|
90
|
-
.typeText(
|
|
91
|
-
textArea,
|
|
92
|
-
'<div>this is <a href="http://example.com"><span id="selectword">selected</span></a> text</div>'
|
|
93
|
-
)
|
|
94
|
-
.click(toggleButton)
|
|
95
|
-
.expect(rceContainer.visible)
|
|
96
|
-
.ok()
|
|
97
|
-
|
|
98
|
-
await t
|
|
99
|
-
.switchToIframe(tinyIframe)
|
|
100
|
-
.selectEditableContent(selectword, selectword)
|
|
101
|
-
.switchToMainWindow()
|
|
102
|
-
.click(linksButton)
|
|
103
|
-
.expect(editLinkMenuItem.visible)
|
|
104
|
-
.ok()
|
|
105
|
-
.expect(removeLinkMenuItem.visible)
|
|
106
|
-
.ok()
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
test.skip('can create an external link', async t => {
|
|
110
|
-
await t
|
|
111
|
-
.click(toggleButton)
|
|
112
|
-
.typeText(textArea, '<div>this is <span id="selectword">selected</span> text</div>')
|
|
113
|
-
.click(toggleButton)
|
|
114
|
-
.expect(rceContainer.visible)
|
|
115
|
-
.ok()
|
|
116
|
-
|
|
117
|
-
await t
|
|
118
|
-
.switchToIframe(tinyIframe)
|
|
119
|
-
.selectEditableContent(selectword, selectword)
|
|
120
|
-
.switchToMainWindow()
|
|
121
|
-
.click(linksButton)
|
|
122
|
-
.click(externalLinksMenuItem)
|
|
123
|
-
.expect(linkDialog.visible)
|
|
124
|
-
.ok()
|
|
125
|
-
.expect(named('linklink').visible)
|
|
126
|
-
.ok()
|
|
127
|
-
|
|
128
|
-
await t
|
|
129
|
-
.typeText(named('linklink'), 'https://instructure.com')
|
|
130
|
-
.click(Selector('button').withText('Done'))
|
|
131
|
-
.switchToIframe(tinyIframe)
|
|
132
|
-
.expect(
|
|
133
|
-
Selector('a')
|
|
134
|
-
.withAttribute('href', 'https://instructure.com')
|
|
135
|
-
.withAttribute('target', '_blank').exists
|
|
136
|
-
)
|
|
137
|
-
.ok()
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
test.skip('can edit an external link', async t => {
|
|
141
|
-
await t
|
|
142
|
-
.click(toggleButton)
|
|
143
|
-
.typeText(
|
|
144
|
-
textArea,
|
|
145
|
-
'<div>this is <a href="http://example.com"><span id="selectword">selected</span></a> text</div>'
|
|
146
|
-
)
|
|
147
|
-
.click(toggleButton)
|
|
148
|
-
.expect(rceContainer.visible)
|
|
149
|
-
.ok()
|
|
150
|
-
|
|
151
|
-
await t
|
|
152
|
-
.switchToIframe(tinyIframe)
|
|
153
|
-
.selectEditableContent(selectword, selectword)
|
|
154
|
-
.switchToMainWindow()
|
|
155
|
-
|
|
156
|
-
// the link is selected, the toolbar button should be enabled
|
|
157
|
-
// await t.expect(linksButton.hasClass('tox-tbtn--enabled')).ok()
|
|
158
|
-
|
|
159
|
-
await t.click(linksButton).click(editLinkMenuItem).expect(linkTray.visible).ok()
|
|
160
|
-
|
|
161
|
-
await t.expect(specificValue('http://example.com').exists).ok()
|
|
162
|
-
await t.expect(specificValue('selected').exists).ok()
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
test.skip('expands selection to edit external link', async t => {
|
|
166
|
-
await t
|
|
167
|
-
.click(toggleButton)
|
|
168
|
-
.typeText(
|
|
169
|
-
textArea,
|
|
170
|
-
'<div>this is <a href="http://example.com"><span id="selectword">selected</span></a> text</div>'
|
|
171
|
-
)
|
|
172
|
-
.click(toggleButton)
|
|
173
|
-
.expect(rceContainer.visible)
|
|
174
|
-
.ok()
|
|
175
|
-
|
|
176
|
-
await t.switchToIframe(tinyIframe).click(selectword, {caretPos: 1}).switchToMainWindow()
|
|
177
|
-
|
|
178
|
-
await t.click(linksButton).click(editLinkMenuItem).expect(linkTray.visible).ok()
|
|
179
|
-
|
|
180
|
-
await t.expect(specificValue('http://example.com').exists).ok()
|
|
181
|
-
await t.expect(specificValue('selected').exists).ok()
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
test.skip('can remove a link', async t => {
|
|
185
|
-
await t
|
|
186
|
-
.click(toggleButton)
|
|
187
|
-
.typeText(
|
|
188
|
-
textArea,
|
|
189
|
-
'<div>this is <a href="http://example.com"><span id="selectword">selected</span></a> text</div>'
|
|
190
|
-
)
|
|
191
|
-
.click(toggleButton)
|
|
192
|
-
.expect(rceContainer.visible)
|
|
193
|
-
.ok()
|
|
194
|
-
|
|
195
|
-
await t
|
|
196
|
-
.switchToIframe(tinyIframe)
|
|
197
|
-
.selectEditableContent(selectword, selectword)
|
|
198
|
-
.switchToMainWindow()
|
|
199
|
-
.click(linksButton)
|
|
200
|
-
.click(removeLinkMenuItem)
|
|
201
|
-
|
|
202
|
-
await t.switchToIframe(tinyIframe).expect(Selector('a').exists).notOk()
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
// fails at the React.lazy call to load the tray's panel
|
|
206
|
-
test.skip('focus returns on dismissing tray', async t => {
|
|
207
|
-
const tinymceSelection = ClientFunction(() => tinymce.get('textarea').selection.getContent()) // the textarea id is from testcafe.html
|
|
208
|
-
const focusedId = ClientFunction(() => document.activeElement.id)
|
|
209
|
-
const focusedTag = ClientFunction(() => document.activeElement.tagName)
|
|
210
|
-
const ltSelector = Selector(linksTraySelector)
|
|
211
|
-
|
|
212
|
-
await t
|
|
213
|
-
.click(toggleButton)
|
|
214
|
-
.typeText(textArea, '<div>this is <span id="selectword">selected</span> text</div>')
|
|
215
|
-
.click(toggleButton)
|
|
216
|
-
.expect(rceContainer.visible)
|
|
217
|
-
.ok()
|
|
218
|
-
|
|
219
|
-
await t
|
|
220
|
-
.switchToIframe(tinyIframe)
|
|
221
|
-
.selectEditableContent(selectword, selectword)
|
|
222
|
-
.switchToMainWindow()
|
|
223
|
-
.click(linksButton)
|
|
224
|
-
.click(courseLinksMenuItem)
|
|
225
|
-
|
|
226
|
-
await linksPanelSelector()
|
|
227
|
-
|
|
228
|
-
await t
|
|
229
|
-
.expect(ltSelector.visible)
|
|
230
|
-
.ok()
|
|
231
|
-
.click(Selector(`${linksTraySelector} button`).withText('Close'))
|
|
232
|
-
.expect(ltSelector.exist)
|
|
233
|
-
.notOk()
|
|
234
|
-
|
|
235
|
-
await t
|
|
236
|
-
.expect(tinymceSelection())
|
|
237
|
-
.eql('selected')
|
|
238
|
-
.expect(focusedId())
|
|
239
|
-
.eql('textarea_ifr')
|
|
240
|
-
|
|
241
|
-
.switchToIframe(tinyIframe)
|
|
242
|
-
.expect(focusedTag())
|
|
243
|
-
.eql('BODY')
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
test.skip('show the kb shortcut modal various ways', async t => {
|
|
247
|
-
const kbshortcutbutton = Selector('button[data-testid="ShowOnFocusButton__button"]')
|
|
248
|
-
const sbKbshortcutbutton = Selector('button[title="View keyboard shortcuts"]')
|
|
249
|
-
const shortcutmodal = Selector('[data-testid="RCE_KeyboardShortcutModal"]')
|
|
250
|
-
const focusKBSCBtn = ClientFunction(() => kbshortcutbutton().focus(), {
|
|
251
|
-
dependencies: {kbshortcutbutton},
|
|
252
|
-
})
|
|
253
|
-
const focusSBKBSCBtn = ClientFunction(() => sbKbshortcutbutton().focus(), {
|
|
254
|
-
dependencies: {sbKbshortcutbutton},
|
|
255
|
-
})
|
|
256
|
-
|
|
257
|
-
await t.expect(kbshortcutbutton.exists).ok()
|
|
258
|
-
|
|
259
|
-
await focusKBSCBtn()
|
|
260
|
-
|
|
261
|
-
// open keyboard shortcut modal from the show-on-focus button
|
|
262
|
-
// close with escape
|
|
263
|
-
await t
|
|
264
|
-
.expect(kbshortcutbutton.visible)
|
|
265
|
-
.ok()
|
|
266
|
-
.click(kbshortcutbutton)
|
|
267
|
-
.expect(shortcutmodal.visible)
|
|
268
|
-
.ok()
|
|
269
|
-
.pressKey('esc')
|
|
270
|
-
.expect(shortcutmodal.exists)
|
|
271
|
-
.notOk()
|
|
272
|
-
|
|
273
|
-
// open modal using alt+0
|
|
274
|
-
// close with the close button
|
|
275
|
-
// await t
|
|
276
|
-
// .switchToIframe(tinyIframe)
|
|
277
|
-
// .click(Selector('body'))
|
|
278
|
-
// .pressKey('alt+0')
|
|
279
|
-
// .expect(shortcutmodal.visible)
|
|
280
|
-
// .ok()
|
|
281
|
-
// .click(Selector('button').withText('Close'))
|
|
282
|
-
// .expect(shortcutmodal.exists)
|
|
283
|
-
// .notOk()
|
|
284
|
-
|
|
285
|
-
// open modal from button in status bar
|
|
286
|
-
// debugger;
|
|
287
|
-
// close with the close button
|
|
288
|
-
await focusSBKBSCBtn()
|
|
289
|
-
|
|
290
|
-
await t
|
|
291
|
-
.expect(sbKbshortcutbutton.exists)
|
|
292
|
-
.ok()
|
|
293
|
-
.click(sbKbshortcutbutton)
|
|
294
|
-
.expect(shortcutmodal.visible)
|
|
295
|
-
.ok()
|
|
296
|
-
.click(Selector('button').withText('Close'))
|
|
297
|
-
.expect(shortcutmodal.exists)
|
|
298
|
-
.notOk()
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
// test.skip('can bring up the images dialog', async t => {
|
|
302
|
-
// await t
|
|
303
|
-
// .switchToMainWindow()
|
|
304
|
-
// .click(imagesButton())
|
|
305
|
-
// .click(uploadImageMenuItem())
|
|
306
|
-
// .expect(uploadImageDialog().visible)
|
|
307
|
-
// .ok()
|
|
308
|
-
|
|
309
|
-
// await t
|
|
310
|
-
// .typeText(named('linklink'), 'https://instructure.com')
|
|
311
|
-
// .click(Selector('button').withText('Done'))
|
|
312
|
-
// .switchToIframe(tinyIframe)
|
|
313
|
-
// .expect(
|
|
314
|
-
// Selector('a')
|
|
315
|
-
// .withAttribute('href', 'https://instructure.com')
|
|
316
|
-
// .withAttribute('target', '_blank').exists
|
|
317
|
-
// )
|
|
318
|
-
// .ok()
|
|
319
|
-
// })
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (C) 2019 - present Instructure, Inc.
|
|
3
|
-
*
|
|
4
|
-
* This file is part of Canvas.
|
|
5
|
-
*
|
|
6
|
-
* Canvas is free software: you can redistribute it and/or modify it under
|
|
7
|
-
* the terms of the GNU Affero General Public License as published by the Free
|
|
8
|
-
* Software Foundation, version 3 of the License.
|
|
9
|
-
*
|
|
10
|
-
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
11
|
-
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
12
|
-
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
13
|
-
* details.
|
|
14
|
-
*
|
|
15
|
-
* You should have received a copy of the GNU Affero General Public License along
|
|
16
|
-
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import {Selector} from 'testcafe'
|
|
20
|
-
fixture`StatusBar`.page`./testcafe.html`
|
|
21
|
-
|
|
22
|
-
const tinyIframe = Selector('#textarea_ifr')
|
|
23
|
-
const application = Selector('.tox-tinymce[role="document"]')
|
|
24
|
-
const textArea = Selector('.RceHtmlEditor')
|
|
25
|
-
const statusPath = Selector('[data-testid="whole-status-bar-path"]')
|
|
26
|
-
const wordCount = Selector('[data-testid="status-bar-word-count"]')
|
|
27
|
-
const toggleButton = Selector('button').withText('</>')
|
|
28
|
-
const dragHandle = Selector('[name="IconDragHandle"]')
|
|
29
|
-
|
|
30
|
-
test.skip('toggles between rce and html views', async t => {
|
|
31
|
-
const rceContainer = Selector('.tox-tinymce')
|
|
32
|
-
const keyboardButton = Selector('button[title="View keyboard shortcuts"]')
|
|
33
|
-
const a11yButton = Selector('button[title="Accessibility Checker"]')
|
|
34
|
-
await t.expect(rceContainer.visible).ok('rce should be initially visible')
|
|
35
|
-
// initially empty, so it's not visible, but we just care that it exists
|
|
36
|
-
await t.expect(statusPath.exists).ok('rich content path should be visible')
|
|
37
|
-
await t.expect(keyboardButton.visible).ok('keyboard button should be visible')
|
|
38
|
-
await t.expect(a11yButton.visible).ok('a11yButton button should be visible')
|
|
39
|
-
await t.expect(wordCount.visible).ok('word count should be visible')
|
|
40
|
-
await t.expect(textArea.visible).notOk('textarea should be initially invisible')
|
|
41
|
-
await t.click(toggleButton)
|
|
42
|
-
await t.expect(rceContainer.visible).notOk('rce should be invisible after toggle')
|
|
43
|
-
await t.expect(textArea.visible).ok('textarea should be visible after toggle')
|
|
44
|
-
await t.expect(statusPath.count).eql(0)
|
|
45
|
-
await t.expect(wordCount.count).eql(0)
|
|
46
|
-
await t.expect(keyboardButton.count).eql(0)
|
|
47
|
-
await t.expect(a11yButton.count).eql(0)
|
|
48
|
-
await t.expect(dragHandle.visible).ok('drag handle should still be visible')
|
|
49
|
-
await t.click(toggleButton)
|
|
50
|
-
await t.expect(rceContainer.visible).ok('rce should be visible after toggling again')
|
|
51
|
-
await t.expect(statusPath.visible).ok('rich content path should be visible again')
|
|
52
|
-
await t.expect(keyboardButton.visible).ok('keyboard button should be visible again')
|
|
53
|
-
await t.expect(a11yButton.visible).ok('a11yButton button should be visible again')
|
|
54
|
-
await t.expect(wordCount.visible).ok('word count should be visible again')
|
|
55
|
-
await t.expect(textArea.visible).notOk('textarea should be hidden after toggling again')
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
test.skip('counts words', async t => {
|
|
59
|
-
// search for the exact text for the selector will wait for it to change to this text
|
|
60
|
-
await t.expect(wordCount.withText('0 words').exists).ok()
|
|
61
|
-
|
|
62
|
-
await t.switchToIframe(tinyIframe).typeText('body', 'foo')
|
|
63
|
-
await t.switchToMainWindow().expect(wordCount.withText('1 word').exists).ok()
|
|
64
|
-
|
|
65
|
-
await t.switchToIframe(tinyIframe).typeText('body', ' bar baz bing')
|
|
66
|
-
await t.switchToMainWindow().expect(Selector('span').withText('4 words').exists).ok()
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
test.skip('displays the current html path', async t => {
|
|
70
|
-
await t.switchToIframe(tinyIframe).typeText('body', 'foo ')
|
|
71
|
-
await t.expect(Selector(statusPath).withText(/p.*strong.*em/).exists).notOk()
|
|
72
|
-
await t.switchToMainWindow().click(Selector('button[title="Bold"]'))
|
|
73
|
-
await t.switchToIframe(tinyIframe).typeText('body', 'bar ')
|
|
74
|
-
await t.switchToMainWindow().click(Selector('button[title="Italic"]'))
|
|
75
|
-
await t.switchToIframe(tinyIframe).typeText('body', 'baz ')
|
|
76
|
-
await t
|
|
77
|
-
.switchToMainWindow()
|
|
78
|
-
.expect(statusPath.withText(/p.*strong.*em/).exists)
|
|
79
|
-
.ok()
|
|
80
|
-
})
|
|
81
|
-
// these 2 tests fail in headless
|
|
82
|
-
test.skip('drag handle resizes the editor', async t => {
|
|
83
|
-
const initialSize = await tinyIframe.boundingClientRect
|
|
84
|
-
await t.drag(dragHandle, -100, 400)
|
|
85
|
-
let finalSize = await tinyIframe.boundingClientRect
|
|
86
|
-
await t.expect(finalSize.height).eql(initialSize.height + 400)
|
|
87
|
-
await t.expect(finalSize.width).eql(initialSize.width)
|
|
88
|
-
await t.drag(dragHandle, -100, -300)
|
|
89
|
-
finalSize = await tinyIframe.boundingClientRect
|
|
90
|
-
await t.expect(finalSize.height).eql(initialSize.height + 100)
|
|
91
|
-
await t.expect(finalSize.width).eql(initialSize.width)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
test.skip('drag handle in rce mode also resizes the textarea', async t => {
|
|
95
|
-
await t.drag(dragHandle, 0, 400)
|
|
96
|
-
const applicationHeight = await application.getStyleProperty('height')
|
|
97
|
-
await t.click(toggleButton)
|
|
98
|
-
const textareaHeight = await textArea.getStyleProperty('height')
|
|
99
|
-
await t.expect(textareaHeight).eql(applicationHeight)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
test.skip('drag handle in textarea mode also resizes the rce', async t => {
|
|
103
|
-
await t.click(toggleButton).drag(dragHandle, 0, 400)
|
|
104
|
-
const textareaHeight = await textArea.getStyleProperty('height')
|
|
105
|
-
await t.click(toggleButton)
|
|
106
|
-
const rceHeight = await application.getStyleProperty('height')
|
|
107
|
-
await t.expect(rceHeight).eql(textareaHeight)
|
|
108
|
-
})
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html dir="ltr" lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<title>enhanceUserContent TestCafe page</title>
|
|
5
|
-
</head>
|
|
6
|
-
<body>
|
|
7
|
-
<div id="enhance_me">
|
|
8
|
-
<div class="user_content">
|
|
9
|
-
<p>
|
|
10
|
-
external link:
|
|
11
|
-
<a class="inline_disabled" href="https://instructure.com/" target="_blank" rel="noopener">
|
|
12
|
-
instructure is external
|
|
13
|
-
</a>
|
|
14
|
-
</p>
|
|
15
|
-
<p>
|
|
16
|
-
inline preview:
|
|
17
|
-
<a
|
|
18
|
-
class="instructure_file_link instructure_scribd_file"
|
|
19
|
-
href="http://localhost:3000/courses/1/files/13?wrap=1"
|
|
20
|
-
target="_blank"
|
|
21
|
-
rel="noopener"
|
|
22
|
-
data-canvas-previewable="true"
|
|
23
|
-
data-api-endpoint="http://localhost:3000/api/v1/courses/1/files/13"
|
|
24
|
-
data-api-returntype="File"
|
|
25
|
-
>wrappedOpts.pdf</a
|
|
26
|
-
>
|
|
27
|
-
</p>
|
|
28
|
-
<p>
|
|
29
|
-
auto inline:
|
|
30
|
-
<a
|
|
31
|
-
class="instructure_file_link instructure_scribd_file auto_open"
|
|
32
|
-
href="http://localhost:3000/courses/1/files/13?wrap=1"
|
|
33
|
-
target="_blank"
|
|
34
|
-
rel="noopener"
|
|
35
|
-
data-canvas-previewable="true"
|
|
36
|
-
data-api-endpoint="http://localhost:3000/api/v1/courses/1/files/13"
|
|
37
|
-
data-api-returntype="File"
|
|
38
|
-
>wrappedOpts.pdf</a
|
|
39
|
-
>
|
|
40
|
-
</p>
|
|
41
|
-
<p>
|
|
42
|
-
in overlay:
|
|
43
|
-
<a
|
|
44
|
-
class="instructure_file_link instructure_scribd_file inline_disabled"
|
|
45
|
-
href="http://localhost:3000/courses/1/files/13?wrap=1"
|
|
46
|
-
target="_blank"
|
|
47
|
-
rel="noopener"
|
|
48
|
-
data-canvas-previewable="true"
|
|
49
|
-
data-api-endpoint="http://localhost:3000/api/v1/courses/1/files/13"
|
|
50
|
-
data-api-returntype="File"
|
|
51
|
-
>wrappedOpts.pdf</a
|
|
52
|
-
>
|
|
53
|
-
</p>
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
<script src="build/testcafe.js"></script>
|
|
57
|
-
</body>
|
|
58
|
-
</html>
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (C) 2022 - present Instructure, Inc.
|
|
3
|
-
*
|
|
4
|
-
* This file is part of Canvas.
|
|
5
|
-
*
|
|
6
|
-
* Canvas is free software: you can redistribute it and/or modify it under
|
|
7
|
-
* the terms of the GNU Affero General Public License as published by the Free
|
|
8
|
-
* Software Foundation, version 3 of the License.
|
|
9
|
-
*
|
|
10
|
-
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
11
|
-
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
12
|
-
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
13
|
-
* details.
|
|
14
|
-
*
|
|
15
|
-
* You should have received a copy of the GNU Affero General Public License along
|
|
16
|
-
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import {Selector} from 'testcafe'
|
|
20
|
-
fixture`EnhanceUserContent`.page`./enhanceUserContent.html`
|
|
21
|
-
|
|
22
|
-
// const externalLink = Selector(
|
|
23
|
-
// 'a.inline_disabled.external[target="_blank"][rel="noreferrer noopener"]'
|
|
24
|
-
// )
|
|
25
|
-
const externalLinkIcon = Selector('span.external_link_icon svg')
|
|
26
|
-
const downloadButton = Selector('a.file_download_btn svg')
|
|
27
|
-
// const loadingPreviewSpinner = Selector('.loading_image_holder')
|
|
28
|
-
const autoOpenInlinePreviewLink = Selector('.auto_open.file_preview_link')
|
|
29
|
-
const inlinePreviewContainer = Selector('.preview_container')
|
|
30
|
-
const overlayPreviewLink = Selector('.inline_disabled.preview_in_overlay')
|
|
31
|
-
|
|
32
|
-
test('enhances user_content', async t => {
|
|
33
|
-
// this one passes locally, but fails in jenkins!?!
|
|
34
|
-
// await t.expect(externalLink.exists).ok('there should be a decorated external link')
|
|
35
|
-
await t.expect(externalLinkIcon.exists).ok('there should be an external link icon')
|
|
36
|
-
await t.expect(downloadButton.exists).ok('there should be a download button')
|
|
37
|
-
// this has started failing too. I don't know why
|
|
38
|
-
// await t.expect(loadingPreviewSpinner.exists).ok('there should be a loading spinner')
|
|
39
|
-
await t
|
|
40
|
-
.expect(autoOpenInlinePreviewLink.exists)
|
|
41
|
-
.ok('there should be an auto_open inline preview link')
|
|
42
|
-
await t.expect(inlinePreviewContainer.exists).ok('there should be an inline preview container')
|
|
43
|
-
await t.expect(overlayPreviewLink.exists).ok('there should be an overlay preveiw link')
|
|
44
|
-
})
|
package/testcafe/entry.jsx
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (C) 2019 - present Instructure, Inc.
|
|
3
|
-
*
|
|
4
|
-
* This file is part of Canvas.
|
|
5
|
-
*
|
|
6
|
-
* Canvas is free software: you can redistribute it and/or modify it under
|
|
7
|
-
* the terms of the GNU Affero General Public License as published by the Free
|
|
8
|
-
* Software Foundation, version 3 of the License.
|
|
9
|
-
*
|
|
10
|
-
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
11
|
-
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
12
|
-
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
13
|
-
* details.
|
|
14
|
-
*
|
|
15
|
-
* You should have received a copy of the GNU Affero General Public License along
|
|
16
|
-
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
// import React from 'react'
|
|
20
|
-
// import ReactDOM from 'react-dom'
|
|
21
|
-
|
|
22
|
-
import '@instructure/canvas-theme'
|
|
23
|
-
|
|
24
|
-
import {renderIntoDiv} from '../src'
|
|
25
|
-
import {enhanceUserContent} from '../src/enhance-user-content'
|
|
26
|
-
|
|
27
|
-
const content = document.getElementById('content')
|
|
28
|
-
if (content) {
|
|
29
|
-
renderIntoDiv(content, {
|
|
30
|
-
textareaId: 'textarea',
|
|
31
|
-
editorOptions: () => {
|
|
32
|
-
return {
|
|
33
|
-
plugins: [
|
|
34
|
-
'autolink',
|
|
35
|
-
'media',
|
|
36
|
-
'paste',
|
|
37
|
-
'table',
|
|
38
|
-
'link',
|
|
39
|
-
'image',
|
|
40
|
-
'instructure_upload_image',
|
|
41
|
-
'directionality',
|
|
42
|
-
'lists',
|
|
43
|
-
'wordcount',
|
|
44
|
-
'instructure-ui-icons',
|
|
45
|
-
'instructure_equation',
|
|
46
|
-
'instructure-embeds',
|
|
47
|
-
'instructure_condensed_buttons',
|
|
48
|
-
'instructure_documents',
|
|
49
|
-
'instructure_equation',
|
|
50
|
-
'instructure_media_embed',
|
|
51
|
-
'instructure_image',
|
|
52
|
-
'instructure_rce_external_tools',
|
|
53
|
-
'instructure_record',
|
|
54
|
-
'instructure_links',
|
|
55
|
-
'instructure_html_view',
|
|
56
|
-
],
|
|
57
|
-
menubar: true,
|
|
58
|
-
screenReaderOnly: false,
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
trayProps: {
|
|
62
|
-
contextType: 'course',
|
|
63
|
-
contextId: '17',
|
|
64
|
-
containingContext: {type: 'course', contextId: '17', userId: '3', contextType: 'course'},
|
|
65
|
-
canUploadFiles: true,
|
|
66
|
-
host: 'someOtherHost',
|
|
67
|
-
jwt: 'someJWT',
|
|
68
|
-
liveRegion: () => {},
|
|
69
|
-
screenReaderOnly: false,
|
|
70
|
-
},
|
|
71
|
-
})
|
|
72
|
-
} else {
|
|
73
|
-
const user_content = document.getElementById('enhance_me')
|
|
74
|
-
if (user_content) {
|
|
75
|
-
enhanceUserContent(user_content)
|
|
76
|
-
}
|
|
77
|
-
}
|
package/testcafe/testcafe.html
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html dir="ltr" lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<title>
|
|
5
|
-
Basic RCE TestCafe page
|
|
6
|
-
</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<main id="content">
|
|
10
|
-
<textarea id="textarea"></textarea>
|
|
11
|
-
</main>
|
|
12
|
-
<script src="build/testcafe.js"></script>
|
|
13
|
-
</body>
|
|
14
|
-
</html>
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (C) 2019 - present Instructure, Inc.
|
|
3
|
-
*
|
|
4
|
-
* This file is part of Canvas.
|
|
5
|
-
*
|
|
6
|
-
* Canvas is free software: you can redistribute it and/or modify it under
|
|
7
|
-
* the terms of the GNU Affero General Public License as published by the Free
|
|
8
|
-
* Software Foundation, version 3 of the License.
|
|
9
|
-
*
|
|
10
|
-
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
11
|
-
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
12
|
-
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
13
|
-
* details.
|
|
14
|
-
*
|
|
15
|
-
* You should have received a copy of the GNU Affero General Public License along
|
|
16
|
-
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
const path = require('path')
|
|
20
|
-
const webpack = require('webpack')
|
|
21
|
-
|
|
22
|
-
module.exports = {
|
|
23
|
-
module: {
|
|
24
|
-
rules: [
|
|
25
|
-
{
|
|
26
|
-
test: /\.(js|ts|jsx|tsx)$/,
|
|
27
|
-
exclude: /node_modules/,
|
|
28
|
-
loader: 'babel-loader',
|
|
29
|
-
options: {
|
|
30
|
-
plugins: [
|
|
31
|
-
'@babel/plugin-proposal-class-properties',
|
|
32
|
-
'@babel/plugin-proposal-optional-chaining',
|
|
33
|
-
'@babel/plugin-proposal-nullish-coalescing-operator',
|
|
34
|
-
],
|
|
35
|
-
presets: [['@babel/preset-react'], ['@babel/preset-typescript']],
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
{test: /(\.css$)/, include: /node_modules/, loaders: ['style-loader', 'css-loader']},
|
|
39
|
-
{test: /\.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000'},
|
|
40
|
-
],
|
|
41
|
-
},
|
|
42
|
-
resolve: {
|
|
43
|
-
extensions: ['.ts', '.js', '.tsx', '.jsx'],
|
|
44
|
-
},
|
|
45
|
-
plugins: [
|
|
46
|
-
new webpack.DefinePlugin({
|
|
47
|
-
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
|
48
|
-
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
|
|
49
|
-
ENV: JSON.stringify(process.env.NODE_ENV),
|
|
50
|
-
}),
|
|
51
|
-
],
|
|
52
|
-
mode: 'development',
|
|
53
|
-
entry: {
|
|
54
|
-
testcafe: [path.join(__dirname, 'testcafe', 'entry.js')],
|
|
55
|
-
},
|
|
56
|
-
output: {
|
|
57
|
-
path: path.join(__dirname, 'testcafe/build'),
|
|
58
|
-
filename: '[name].js',
|
|
59
|
-
publicPath: path.join(__dirname, 'testcafe/build/'),
|
|
60
|
-
},
|
|
61
|
-
}
|