@instructure/canvas-rce 7.3.1 → 8.0.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 +65 -0
- package/{es/rce/plugins/shared/ai_tools/index.js → __mocks__/@instructure/ui-media-player/_mockUiMediaPlayer.js} +4 -4
- package/__tests__/common/mimeClass.test.js +25 -1
- package/__tests__/rcs/api.test.js +280 -251
- package/es/canvasFileBrowser/FileBrowser.d.ts +2 -2
- package/es/canvasFileBrowser/FileBrowser.js +8 -7
- package/es/common/mimeClass.js +3 -1
- package/es/defaultTinymceConfig.js +47 -49
- package/es/enhance-user-content/enhance_user_content.js +6 -8
- package/es/enhance-user-content/index.d.ts +3 -1
- package/es/enhance-user-content/index.js +3 -1
- package/es/enhance-user-content/youtube_overlay.js +18 -0
- package/es/getThemeVars.d.ts +1 -1
- package/es/getThemeVars.js +23 -26
- package/es/rce/KeyboardShortcutModal.js +1 -1
- package/es/rce/RCE.d.ts +9 -0
- package/es/rce/RCE.js +4 -0
- package/es/rce/RCEGlobals.d.ts +2 -0
- package/es/rce/RCEGlobals.js +1 -0
- package/es/rce/RCEVariants.d.ts +1 -2
- package/es/rce/RCEVariants.js +1 -2
- package/es/rce/RCEWrapper.d.ts +6 -16
- package/es/rce/RCEWrapper.js +18 -87
- package/es/rce/RCEWrapper.utils.d.ts +1 -1
- package/es/rce/RCEWrapperProps.d.ts +2 -1
- package/es/rce/RCEWrapperProps.js +2 -1
- package/es/rce/StatusBar.d.ts +0 -1
- package/es/rce/StatusBar.js +3 -28
- package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.d.ts +2 -1
- package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.js +3 -1
- package/es/rce/plugins/instructure_equation/EquationEditorModal/index.d.ts +1 -0
- package/es/rce/plugins/instructure_equation/EquationEditorModal/index.js +12 -2
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageOptions.js +2 -2
- package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageSection.js +3 -3
- package/es/rce/plugins/instructure_icon_maker/svg/constants.d.ts +20 -5
- package/es/rce/plugins/instructure_icon_maker/svg/utils.d.ts +1 -1
- package/es/rce/plugins/instructure_icon_maker/utils/IconMakerFormHasChanges.js +2 -2
- package/es/rce/plugins/instructure_image/ImageEmbedOptions.d.ts +0 -2
- package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +2 -9
- package/es/rce/plugins/instructure_paste/plugin.js +18 -12
- package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.d.ts +1 -1
- package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.d.ts +4 -0
- package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.js +4 -0
- package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.d.ts +11 -2
- package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +92 -10
- package/es/rce/plugins/instructure_record/AudioOptionsTray/index.d.ts +13 -1
- package/es/rce/plugins/instructure_record/AudioOptionsTray/index.js +216 -24
- package/es/rce/plugins/instructure_record/MediaPanel/index.js +16 -5
- package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.d.ts +14 -13
- package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +110 -39
- package/es/rce/plugins/instructure_record/VideoOptionsTray/index.d.ts +11 -1
- package/es/rce/plugins/instructure_record/VideoOptionsTray/index.js +242 -67
- package/es/rce/plugins/instructure_record/clickCallback.js +19 -4
- package/es/rce/plugins/instructure_record/mediaTranslations.js +1 -1
- package/es/rce/plugins/instructure_record/playerLayoutOptions.d.ts +25 -0
- package/es/rce/plugins/instructure_record/playerLayoutOptions.js +91 -0
- package/es/rce/plugins/instructure_record/plugin.js +2 -5
- package/es/rce/plugins/instructure_record/utils.d.ts +3 -0
- package/es/rce/plugins/instructure_record/utils.js +31 -0
- package/es/rce/plugins/instructure_studio_media_options/plugin.js +82 -26
- package/es/rce/plugins/shared/ContentSelection.d.ts +6 -1
- package/es/rce/plugins/shared/ContentSelection.js +15 -6
- package/es/rce/plugins/shared/DimensionsInput/DimensionInput.js +1 -2
- package/es/rce/plugins/shared/DimensionsInput/index.js +11 -12
- package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.d.ts +1 -1
- package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.js +4 -3
- package/es/rce/plugins/shared/StudioLtiSupportUtils.d.ts +27 -6
- package/es/rce/plugins/shared/StudioLtiSupportUtils.js +82 -13
- package/es/rce/plugins/shared/Upload/UploadFile.js +1 -8
- package/es/rce/style.d.ts +2 -1
- package/es/rce/style.js +4 -2
- package/es/rcs/api.d.ts +5 -10
- package/es/rcs/api.js +15 -21
- package/es/rcs/fake.d.ts +1 -7
- package/es/rcs/fake.js +1 -47
- package/es/sidebar/actions/media.d.ts +19 -6
- package/es/sidebar/actions/media.js +17 -4
- package/es/sidebar/actions/upload.d.ts +3 -3
- package/es/sidebar/actions/upload.js +9 -9
- package/es/sidebar/containers/Sidebar.js +0 -2
- package/es/sidebar/containers/sidebarHandlers.d.ts +2 -4
- package/es/sidebar/containers/sidebarHandlers.js +2 -5
- package/es/sidebar/reducers/index.d.ts +0 -1
- package/es/sidebar/reducers/index.js +0 -2
- package/es/sidebar/store/initialState.d.ts +0 -1
- package/es/sidebar/store/initialState.js +0 -5
- package/es/translations/locales/ar.js +65 -80
- package/es/translations/locales/ca.js +65 -80
- package/es/translations/locales/cy.js +65 -80
- package/es/translations/locales/da-x-k12.js +65 -80
- package/es/translations/locales/da.js +65 -80
- package/es/translations/locales/de.js +65 -80
- package/es/translations/locales/el.js +0 -9
- package/es/translations/locales/en-AU-x-unimelb.js +65 -80
- package/es/translations/locales/en-GB-x-ukhe.js +65 -80
- package/es/translations/locales/en.js +61 -79
- package/es/translations/locales/en_AU.js +65 -80
- package/es/translations/locales/en_CA.js +65 -80
- package/es/translations/locales/en_CY.js +65 -80
- package/es/translations/locales/en_GB.js +65 -80
- package/es/translations/locales/es.js +65 -80
- package/es/translations/locales/es_ES.js +65 -80
- package/es/translations/locales/fa_IR.js +0 -9
- package/es/translations/locales/fi.js +65 -80
- package/es/translations/locales/fr.js +65 -80
- package/es/translations/locales/fr_CA.js +65 -80
- package/es/translations/locales/ga.js +65 -80
- package/es/translations/locales/he.js +0 -9
- package/es/translations/locales/hi.js +65 -80
- package/es/translations/locales/ht.js +65 -80
- package/es/translations/locales/hu.js +0 -36
- package/es/translations/locales/hy.js +0 -9
- package/es/translations/locales/id.js +65 -80
- package/es/translations/locales/is.js +65 -80
- package/es/translations/locales/it.js +65 -80
- package/es/translations/locales/ja.js +65 -80
- package/es/translations/locales/ko.js +2455 -133
- package/es/translations/locales/mi.js +65 -80
- package/es/translations/locales/ms.js +65 -80
- package/es/translations/locales/nb-x-k12.js +65 -80
- package/es/translations/locales/nb.js +65 -80
- package/es/translations/locales/nl.js +66 -81
- package/es/translations/locales/nn.js +0 -36
- package/es/translations/locales/pl.js +65 -80
- package/es/translations/locales/pt.js +65 -80
- package/es/translations/locales/pt_BR.js +65 -80
- package/es/translations/locales/ru.js +65 -80
- package/es/translations/locales/sl.js +65 -80
- package/es/translations/locales/sv-x-k12.js +65 -80
- package/es/translations/locales/sv.js +65 -80
- package/es/translations/locales/th.js +65 -80
- package/es/translations/locales/tr.js +1962 -18
- package/es/translations/locales/uk_UA.js +0 -9
- package/es/translations/locales/vi.js +65 -80
- package/es/translations/locales/zh-Hans.js +65 -80
- package/es/translations/locales/zh-Hant.js +65 -80
- package/es/translations/locales/zh.js +65 -80
- package/es/translations/locales/zh_HK.js +65 -80
- package/eslint.config.js +16 -147
- package/jest/jest-setup.js +1 -0
- package/jest.config.js +2 -0
- package/oxlint.json +84 -0
- package/package.json +86 -62
- package/tsconfig.json +3 -2
- package/es/rce/plugins/shared/ai_tools/AIResponseModal.d.ts +0 -10
- package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +0 -67
- package/es/rce/plugins/shared/ai_tools/AIToolsTray.d.ts +0 -18
- package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +0 -489
- package/es/rce/plugins/shared/ai_tools/aiicons.d.ts +0 -7
- package/es/rce/plugins/shared/ai_tools/aiicons.js +0 -60
- package/es/rce/plugins/shared/ai_tools/index.d.ts +0 -3
- package/es/sidebar/actions/flickr.d.ts +0 -20
- package/es/sidebar/actions/flickr.js +0 -60
- package/es/sidebar/reducers/flickr.d.ts +0 -1
- package/es/sidebar/reducers/flickr.js +0 -49
|
@@ -16,11 +16,16 @@
|
|
|
16
16
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import {http, HttpResponse} from 'msw'
|
|
20
|
+
import {setupServer} from 'msw/node'
|
|
19
21
|
import RceApiSource, {headerFor, originFromHost} from '../../src/rcs/api'
|
|
20
|
-
import fetchMock from 'fetch-mock'
|
|
21
22
|
import * as fileUrl from '../../src/common/fileUrl'
|
|
22
23
|
import {ICON_MAKER_ICONS} from '../../src/rce/plugins/instructure_icon_maker/svg/constants'
|
|
23
24
|
|
|
25
|
+
const BASE = 'http://localhost'
|
|
26
|
+
|
|
27
|
+
const server = setupServer(http.get(`${BASE}/api/session`, () => HttpResponse.json({})))
|
|
28
|
+
|
|
24
29
|
describe('sources/api', () => {
|
|
25
30
|
const endpoint = 'wikiPages'
|
|
26
31
|
const props = {
|
|
@@ -33,22 +38,38 @@ describe('sources/api', () => {
|
|
|
33
38
|
let setProps = {}
|
|
34
39
|
let apiSource
|
|
35
40
|
let alertFuncSpy
|
|
41
|
+
let noHostApiSource
|
|
42
|
+
|
|
43
|
+
beforeAll(() => {
|
|
44
|
+
server.listen({onUnhandledRequest: 'error'})
|
|
45
|
+
})
|
|
36
46
|
|
|
37
47
|
beforeEach(() => {
|
|
38
48
|
alertFuncSpy = jest.fn()
|
|
39
49
|
apiSource = new RceApiSource({
|
|
50
|
+
jwt: 'theJWT',
|
|
51
|
+
host: 'localhost',
|
|
52
|
+
refreshToken: callback => {
|
|
53
|
+
callback('freshJWT')
|
|
54
|
+
},
|
|
55
|
+
alertFunc: alertFuncSpy,
|
|
56
|
+
})
|
|
57
|
+
noHostApiSource = new RceApiSource({
|
|
40
58
|
jwt: 'theJWT',
|
|
41
59
|
refreshToken: callback => {
|
|
42
60
|
callback('freshJWT')
|
|
43
61
|
},
|
|
44
62
|
alertFunc: alertFuncSpy,
|
|
45
63
|
})
|
|
46
|
-
fetchMock.mock('/api/session', '{}')
|
|
47
64
|
})
|
|
48
65
|
|
|
49
66
|
afterEach(() => {
|
|
50
|
-
|
|
51
|
-
jest.
|
|
67
|
+
server.resetHandlers()
|
|
68
|
+
jest.restoreAllMocks()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
afterAll(() => {
|
|
72
|
+
server.close()
|
|
52
73
|
})
|
|
53
74
|
|
|
54
75
|
describe('initializeCollection', () => {
|
|
@@ -63,15 +84,15 @@ describe('sources/api', () => {
|
|
|
63
84
|
|
|
64
85
|
it('creates a collection with a bookmark derived from props', () => {
|
|
65
86
|
expect(collection.bookmark).toEqual(
|
|
66
|
-
`${window.location.protocol}//example.host/api/wikiPages?contextType=group&contextId=123&search_term=panda
|
|
87
|
+
`${window.location.protocol}//example.host/api/wikiPages?contextType=group&contextId=123&search_term=panda`,
|
|
67
88
|
)
|
|
68
89
|
})
|
|
69
90
|
|
|
70
91
|
it('bookmark omits host if not in props', () => {
|
|
71
92
|
const noHostProps = {...props, host: undefined}
|
|
72
|
-
collection =
|
|
93
|
+
collection = noHostApiSource.initializeCollection(endpoint, noHostProps)
|
|
73
94
|
expect(collection.bookmark).toEqual(
|
|
74
|
-
'/api/wikiPages?contextType=group&contextId=123&search_term=panda'
|
|
95
|
+
'/api/wikiPages?contextType=group&contextId=123&search_term=panda',
|
|
75
96
|
)
|
|
76
97
|
})
|
|
77
98
|
|
|
@@ -101,7 +122,7 @@ describe('sources/api', () => {
|
|
|
101
122
|
})
|
|
102
123
|
|
|
103
124
|
it('uses a path for no-host url construction', () => {
|
|
104
|
-
const uri =
|
|
125
|
+
const uri = noHostApiSource.baseUri('files')
|
|
105
126
|
expect(uri).toEqual('/api/files')
|
|
106
127
|
})
|
|
107
128
|
|
|
@@ -113,7 +134,7 @@ describe('sources/api', () => {
|
|
|
113
134
|
|
|
114
135
|
it('never applies protocol to path', () => {
|
|
115
136
|
const fakeWindow = {location: {protocol: 'https:'}}
|
|
116
|
-
const uri =
|
|
137
|
+
const uri = noHostApiSource.baseUri('files', null, fakeWindow)
|
|
117
138
|
expect(uri).toEqual('/api/files')
|
|
118
139
|
})
|
|
119
140
|
|
|
@@ -136,74 +157,78 @@ describe('sources/api', () => {
|
|
|
136
157
|
})
|
|
137
158
|
|
|
138
159
|
it('gets documents', () => {
|
|
139
|
-
const uri =
|
|
160
|
+
const uri = noHostApiSource.uriFor('documents', setProps)
|
|
140
161
|
expect(uri).toEqual(
|
|
141
|
-
'/api/documents?contextType=course&contextId=17&exclude_content_types=image,video,audio&sort=name&order=asc&search_term=hello%20world'
|
|
162
|
+
'/api/documents?contextType=course&contextId=17&exclude_content_types=image,video,audio&sort=name&order=asc&search_term=hello%20world',
|
|
142
163
|
)
|
|
143
164
|
})
|
|
144
165
|
|
|
145
166
|
it('gets images', () => {
|
|
146
|
-
const uri =
|
|
167
|
+
const uri = noHostApiSource.uriFor('images', setProps)
|
|
147
168
|
expect(uri).toEqual(
|
|
148
|
-
'/api/documents?contextType=course&contextId=17&content_types=image&sort=name&order=asc&search_term=hello%20world'
|
|
169
|
+
'/api/documents?contextType=course&contextId=17&content_types=image&sort=name&order=asc&search_term=hello%20world',
|
|
149
170
|
)
|
|
150
171
|
})
|
|
151
172
|
|
|
152
173
|
// this endpoint isn't actually used yet, but could be if media_objects all had associated Attachments
|
|
153
174
|
it('gets media', () => {
|
|
154
|
-
const uri =
|
|
175
|
+
const uri = noHostApiSource.uriFor('media', setProps)
|
|
155
176
|
expect(uri).toEqual(
|
|
156
|
-
'/api/documents?contextType=course&contextId=17&content_types=video,audio&sort=name&order=asc&search_term=hello%20world'
|
|
177
|
+
'/api/documents?contextType=course&contextId=17&content_types=video,audio&sort=name&order=asc&search_term=hello%20world',
|
|
157
178
|
)
|
|
158
179
|
})
|
|
159
180
|
|
|
160
181
|
it('gets media_objects', () => {
|
|
161
|
-
const uri =
|
|
182
|
+
const uri = noHostApiSource.uriFor('media_objects', setProps)
|
|
162
183
|
expect(uri).toEqual(
|
|
163
|
-
'/api/media_objects?contextType=course&contextId=17&sort=title&order=asc&search_term=hello%20world'
|
|
184
|
+
'/api/media_objects?contextType=course&contextId=17&sort=title&order=asc&search_term=hello%20world',
|
|
164
185
|
)
|
|
165
186
|
})
|
|
166
187
|
})
|
|
167
188
|
|
|
168
189
|
describe('fetchPage', () => {
|
|
169
|
-
const uri =
|
|
170
|
-
const fakePageBody =
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
190
|
+
const uri = `${BASE}/theURI`
|
|
191
|
+
const fakePageBody = {
|
|
192
|
+
bookmark: 'newBookmark',
|
|
193
|
+
links: [
|
|
194
|
+
{href: 'link1', title: 'Link 1'},
|
|
195
|
+
{href: 'link2', title: 'Link 2'},
|
|
196
|
+
],
|
|
197
|
+
}
|
|
174
198
|
|
|
175
199
|
it('includes jwt in Authorization header', async () => {
|
|
176
|
-
|
|
200
|
+
let capturedRequest
|
|
201
|
+
server.use(
|
|
202
|
+
http.get(uri, ({request}) => {
|
|
203
|
+
capturedRequest = request
|
|
204
|
+
return HttpResponse.json({})
|
|
205
|
+
}),
|
|
206
|
+
)
|
|
177
207
|
await apiSource.fetchPage(uri)
|
|
178
|
-
expect(
|
|
208
|
+
expect(capturedRequest.headers.get('Authorization')).toEqual('Bearer theJWT')
|
|
179
209
|
})
|
|
180
210
|
|
|
181
211
|
it('converts 400+ statuses to errors', async () => {
|
|
182
|
-
|
|
212
|
+
server.use(
|
|
213
|
+
http.get(uri, () => new HttpResponse(null, {status: 403, statusText: 'Forbidden'})),
|
|
214
|
+
)
|
|
183
215
|
await expect(apiSource.fetchPage(uri)).rejects.toThrow('Forbidden')
|
|
184
216
|
})
|
|
185
217
|
|
|
186
218
|
it('parses server response before handing it back', async () => {
|
|
187
|
-
|
|
219
|
+
server.use(http.get(uri, () => HttpResponse.json(fakePageBody)))
|
|
188
220
|
const page = await apiSource.fetchPage(uri)
|
|
189
|
-
expect(page).toEqual(
|
|
190
|
-
bookmark: 'newBookmark',
|
|
191
|
-
links: [
|
|
192
|
-
{href: 'link1', title: 'Link 1'},
|
|
193
|
-
{href: 'link2', title: 'Link 2'},
|
|
194
|
-
],
|
|
195
|
-
})
|
|
221
|
+
expect(page).toEqual(fakePageBody)
|
|
196
222
|
})
|
|
197
223
|
|
|
198
224
|
it('retries once on 401 with a renewed token', async () => {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
225
|
+
server.use(
|
|
226
|
+
http.get(uri, ({request}) => {
|
|
227
|
+
const auth = request.headers.get('Authorization')
|
|
228
|
+
if (auth === 'Bearer theJWT') return new HttpResponse(null, {status: 401})
|
|
229
|
+
return HttpResponse.json(fakePageBody)
|
|
230
|
+
}),
|
|
231
|
+
)
|
|
207
232
|
const page = await apiSource.fetchPage(uri, 'theJWT')
|
|
208
233
|
expect(page.bookmark).toEqual('newBookmark')
|
|
209
234
|
expect(apiSource.jwt).toEqual('freshJWT')
|
|
@@ -223,14 +248,14 @@ describe('sources/api', () => {
|
|
|
223
248
|
})
|
|
224
249
|
|
|
225
250
|
it('proxies the call to fetchPage', async () => {
|
|
226
|
-
const
|
|
227
|
-
const body = await apiSource.fetchFiles(
|
|
228
|
-
expect(apiSource.fetchPage).toHaveBeenCalledWith(
|
|
251
|
+
const fetchUri = `${BASE}/files-uri`
|
|
252
|
+
const body = await apiSource.fetchFiles(fetchUri)
|
|
253
|
+
expect(apiSource.fetchPage).toHaveBeenCalledWith(fetchUri)
|
|
229
254
|
expect(body.bookmark).toEqual(bookmark)
|
|
230
255
|
})
|
|
231
256
|
|
|
232
257
|
it('converts file urls from download to preview', async () => {
|
|
233
|
-
const body = await apiSource.fetchFiles(
|
|
258
|
+
const body = await apiSource.fetchFiles(`${BASE}/foo`)
|
|
234
259
|
files.forEach((file, i) => {
|
|
235
260
|
expect(fileUrl.downloadToWrap).toHaveBeenCalledWith(file.url)
|
|
236
261
|
expect(body.files[i].href).toEqual(wrapUrl)
|
|
@@ -253,7 +278,7 @@ describe('sources/api', () => {
|
|
|
253
278
|
`${window.location.protocol}//canvas.rce/api/folders/2`,
|
|
254
279
|
{
|
|
255
280
|
Authorization: 'Bearer theJWT',
|
|
256
|
-
}
|
|
281
|
+
},
|
|
257
282
|
)
|
|
258
283
|
})
|
|
259
284
|
|
|
@@ -281,7 +306,7 @@ describe('sources/api', () => {
|
|
|
281
306
|
'https://canvas.rce/api/files/2?per_page=50',
|
|
282
307
|
{
|
|
283
308
|
Authorization: 'Bearer theJWT',
|
|
284
|
-
}
|
|
309
|
+
},
|
|
285
310
|
)
|
|
286
311
|
})
|
|
287
312
|
})
|
|
@@ -312,10 +337,6 @@ describe('sources/api', () => {
|
|
|
312
337
|
onError = jest.fn()
|
|
313
338
|
})
|
|
314
339
|
|
|
315
|
-
afterEach(() => {
|
|
316
|
-
jest.resetAllMocks()
|
|
317
|
-
})
|
|
318
|
-
|
|
319
340
|
const subject = () =>
|
|
320
341
|
apiSource.fetchBookmarkedData(fetchFunction, properties, onSuccess, onError)
|
|
321
342
|
|
|
@@ -324,7 +345,7 @@ describe('sources/api', () => {
|
|
|
324
345
|
expect(fetchFunction).toHaveBeenCalledWith(properties, undefined)
|
|
325
346
|
expect(fetchFunction).toHaveBeenCalledWith(
|
|
326
347
|
properties,
|
|
327
|
-
'https://canvas.rce/api/thing/1?page=2'
|
|
348
|
+
'https://canvas.rce/api/thing/1?page=2',
|
|
328
349
|
)
|
|
329
350
|
expect(fetchFunction).toHaveBeenCalledTimes(2)
|
|
330
351
|
})
|
|
@@ -336,7 +357,7 @@ describe('sources/api', () => {
|
|
|
336
357
|
|
|
337
358
|
describe('when "fetchFunction" throws an exception', () => {
|
|
338
359
|
beforeEach(() => {
|
|
339
|
-
|
|
360
|
+
fetchFunction.mockReset()
|
|
340
361
|
fetchFunction.mockRejectedValue('error')
|
|
341
362
|
})
|
|
342
363
|
|
|
@@ -365,7 +386,7 @@ describe('sources/api', () => {
|
|
|
365
386
|
})
|
|
366
387
|
.then(() => {
|
|
367
388
|
expect(apiSource.fetchPage).toHaveBeenCalledWith(
|
|
368
|
-
|
|
389
|
+
`${BASE}/api/folders/icon_maker?contextType=course&contextId=22`,
|
|
369
390
|
)
|
|
370
391
|
})
|
|
371
392
|
})
|
|
@@ -387,94 +408,94 @@ describe('sources/api', () => {
|
|
|
387
408
|
})
|
|
388
409
|
.then(() => {
|
|
389
410
|
expect(apiSource.fetchPage).toHaveBeenCalledWith(
|
|
390
|
-
|
|
411
|
+
`${BASE}/api/folders/media?contextType=course&contextId=22`,
|
|
391
412
|
)
|
|
392
413
|
})
|
|
393
414
|
})
|
|
394
415
|
})
|
|
395
416
|
|
|
396
417
|
describe('preflightUpload', () => {
|
|
397
|
-
const uri =
|
|
418
|
+
const uri = `${BASE}/api/upload`
|
|
398
419
|
const fileProps = {}
|
|
399
420
|
const apiProps = {}
|
|
400
421
|
|
|
401
|
-
|
|
402
|
-
|
|
422
|
+
it('includes "onDuplicate"', async () => {
|
|
423
|
+
let capturedBody
|
|
424
|
+
server.use(
|
|
425
|
+
http.post(uri, async ({request}) => {
|
|
426
|
+
capturedBody = await request.json()
|
|
427
|
+
return HttpResponse.json({})
|
|
428
|
+
}),
|
|
429
|
+
)
|
|
430
|
+
await apiSource.preflightUpload(fileProps, {onDuplicate: 'overwrite'}, apiProps)
|
|
431
|
+
expect(capturedBody.onDuplicate).toEqual('overwrite')
|
|
403
432
|
})
|
|
404
433
|
|
|
405
|
-
it('includes "
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
434
|
+
it('includes "category"', async () => {
|
|
435
|
+
let capturedBody
|
|
436
|
+
server.use(
|
|
437
|
+
http.post(uri, async ({request}) => {
|
|
438
|
+
capturedBody = await request.json()
|
|
439
|
+
return HttpResponse.json({})
|
|
440
|
+
}),
|
|
441
|
+
)
|
|
442
|
+
await apiSource.preflightUpload(fileProps, {category: ICON_MAKER_ICONS}, apiProps)
|
|
443
|
+
expect(capturedBody.category).toEqual(ICON_MAKER_ICONS)
|
|
412
444
|
})
|
|
413
445
|
|
|
414
|
-
it('includes
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
446
|
+
it('includes jwt in Authorization header', async () => {
|
|
447
|
+
let capturedRequest
|
|
448
|
+
server.use(
|
|
449
|
+
http.post(uri, ({request}) => {
|
|
450
|
+
capturedRequest = request
|
|
451
|
+
return HttpResponse.json({})
|
|
452
|
+
}),
|
|
453
|
+
)
|
|
454
|
+
await apiSource.preflightUpload(fileProps, apiProps)
|
|
455
|
+
expect(capturedRequest.headers.get('Authorization')).toEqual('Bearer theJWT')
|
|
423
456
|
})
|
|
424
457
|
|
|
425
|
-
it('
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
458
|
+
it('retries once with fresh token on 401', async () => {
|
|
459
|
+
server.use(
|
|
460
|
+
http.post(uri, ({request}) => {
|
|
461
|
+
const auth = request.headers.get('Authorization')
|
|
462
|
+
if (auth === 'Bearer theJWT') return new HttpResponse(null, {status: 401})
|
|
463
|
+
return HttpResponse.json({upload: 'done'})
|
|
464
|
+
}),
|
|
465
|
+
)
|
|
466
|
+
const response = await apiSource.preflightUpload(fileProps, apiProps)
|
|
467
|
+
expect(response.upload).toEqual('done')
|
|
431
468
|
})
|
|
432
469
|
|
|
433
|
-
it('
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
expect(response.upload).toEqual('done')
|
|
444
|
-
})
|
|
470
|
+
it('notifies a provided callback when a new token is fetched', async () => {
|
|
471
|
+
server.use(
|
|
472
|
+
http.post(uri, ({request}) => {
|
|
473
|
+
const auth = request.headers.get('Authorization')
|
|
474
|
+
if (auth === 'Bearer theJWT') return new HttpResponse(null, {status: 401})
|
|
475
|
+
return HttpResponse.json({upload: 'done'})
|
|
476
|
+
}),
|
|
477
|
+
)
|
|
478
|
+
await apiSource.preflightUpload(fileProps, apiProps)
|
|
479
|
+
expect(apiSource.jwt).toEqual('freshJWT')
|
|
445
480
|
})
|
|
446
481
|
|
|
447
|
-
it('
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}, '{"upload": "done"}')
|
|
455
|
-
|
|
456
|
-
return apiSource.preflightUpload(fileProps, apiProps).then(() => {
|
|
457
|
-
expect(apiSource.jwt).toEqual('freshJWT')
|
|
482
|
+
it('calls alertFunc when an error occurs', async () => {
|
|
483
|
+
jest.spyOn(console, 'error').mockImplementation(() => {})
|
|
484
|
+
server.use(http.post(uri, () => HttpResponse.json({}, {status: 500})))
|
|
485
|
+
await apiSource.preflightUpload(fileProps, apiProps).catch(() => {})
|
|
486
|
+
expect(alertFuncSpy).toHaveBeenCalledWith({
|
|
487
|
+
text: 'Something went wrong. Check your connection, reload the page, and try again.',
|
|
488
|
+
variant: 'error',
|
|
458
489
|
})
|
|
459
490
|
})
|
|
460
491
|
|
|
461
|
-
it('calls alertFunc when an error occurs', () => {
|
|
462
|
-
fetchMock.mock(uri, 500)
|
|
463
|
-
return apiSource
|
|
464
|
-
.preflightUpload(fileProps, apiProps)
|
|
465
|
-
.then(() => {
|
|
466
|
-
expect(alertFuncSpy).toHaveBeenCalledWith({
|
|
467
|
-
text: 'Something went wrong uploading, check your connection and try again.',
|
|
468
|
-
variant: 'error',
|
|
469
|
-
})
|
|
470
|
-
})
|
|
471
|
-
.catch(() => {
|
|
472
|
-
// This will re-throw so we just catch it here.
|
|
473
|
-
})
|
|
474
|
-
})
|
|
475
|
-
|
|
476
492
|
it('throws an exception when an error occurs', () => {
|
|
477
|
-
|
|
493
|
+
server.use(
|
|
494
|
+
http.post(
|
|
495
|
+
uri,
|
|
496
|
+
() => new HttpResponse(null, {status: 500, statusText: 'Internal Server Error'}),
|
|
497
|
+
),
|
|
498
|
+
)
|
|
478
499
|
return apiSource.preflightUpload(fileProps, apiProps).catch(e => {
|
|
479
500
|
expect(e).not.toBeNull()
|
|
480
501
|
})
|
|
@@ -482,10 +503,11 @@ describe('sources/api', () => {
|
|
|
482
503
|
|
|
483
504
|
describe('when the file storage quota is exceeded', () => {
|
|
484
505
|
beforeEach(() => {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
506
|
+
server.use(
|
|
507
|
+
http.post(uri, () =>
|
|
508
|
+
HttpResponse.json({message: 'file size exceeds quota'}, {status: 413}),
|
|
509
|
+
),
|
|
510
|
+
)
|
|
489
511
|
})
|
|
490
512
|
|
|
491
513
|
it('gives a "quota" error if quota is full', async () => {
|
|
@@ -497,7 +519,7 @@ describe('sources/api', () => {
|
|
|
497
519
|
})
|
|
498
520
|
} catch (e) {
|
|
499
521
|
return e
|
|
500
|
-
}
|
|
522
|
+
}
|
|
501
523
|
})
|
|
502
524
|
})
|
|
503
525
|
})
|
|
@@ -507,21 +529,22 @@ describe('sources/api', () => {
|
|
|
507
529
|
|
|
508
530
|
beforeEach(() => {
|
|
509
531
|
fileDomObject = new window.Blob()
|
|
510
|
-
uploadUrl = 'upload-url'
|
|
532
|
+
uploadUrl = 'http://upload-url/'
|
|
511
533
|
preflightProps = {
|
|
512
534
|
upload_params: {},
|
|
513
535
|
upload_url: uploadUrl,
|
|
514
536
|
}
|
|
515
537
|
file = {url: 'file-url'}
|
|
516
|
-
|
|
517
|
-
})
|
|
518
|
-
|
|
519
|
-
afterEach(() => {
|
|
520
|
-
fetchMock.restore()
|
|
538
|
+
server.use(http.post(uploadUrl, () => HttpResponse.json(file)))
|
|
521
539
|
})
|
|
522
540
|
|
|
523
541
|
it('calls alertFunc if there is a problem', () => {
|
|
524
|
-
|
|
542
|
+
server.use(
|
|
543
|
+
http.post(
|
|
544
|
+
uploadUrl,
|
|
545
|
+
() => new HttpResponse(null, {status: 500, statusText: 'Internal Server Error'}),
|
|
546
|
+
),
|
|
547
|
+
)
|
|
525
548
|
return apiSource
|
|
526
549
|
.uploadFRD(fileDomObject, preflightProps)
|
|
527
550
|
.then(() => {
|
|
@@ -541,73 +564,75 @@ describe('sources/api', () => {
|
|
|
541
564
|
jest.spyOn(apiSource, 'getFile').mockReturnValue(Promise.resolve(file))
|
|
542
565
|
})
|
|
543
566
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
it('includes credentials in non-S3 upload', () => {
|
|
567
|
+
it('includes credentials in non-S3 upload', async () => {
|
|
568
|
+
// request.credentials is not available in MSW Node.js handlers; spy on fetch directly
|
|
569
|
+
const fetchSpy = jest.spyOn(global, 'fetch')
|
|
549
570
|
preflightProps.upload_params.success_url = undefined
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
571
|
+
await apiSource.uploadFRD(fileDomObject, preflightProps)
|
|
572
|
+
const uploadCall = fetchSpy.mock.calls.find(([url]) => String(url).includes('upload-url'))
|
|
573
|
+
expect(uploadCall?.[1]?.credentials).toEqual('include')
|
|
553
574
|
})
|
|
554
575
|
|
|
555
|
-
it('does not include credentials in S3 upload', () => {
|
|
576
|
+
it('does not include credentials in S3 upload', async () => {
|
|
577
|
+
const fetchSpy = jest.spyOn(global, 'fetch')
|
|
556
578
|
preflightProps.upload_params['x-amz-signature'] = 'success-url'
|
|
557
|
-
preflightProps.upload_params.success_url = 'success-url'
|
|
579
|
+
preflightProps.upload_params.success_url = 'http://success-url/'
|
|
558
580
|
const s3File = {url: 's3-file-url'}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
581
|
+
server.use(http.get('http://success-url/', () => HttpResponse.json(s3File)))
|
|
582
|
+
await apiSource.uploadFRD(fileDomObject, preflightProps)
|
|
583
|
+
const uploadCall = fetchSpy.mock.calls.find(([url]) => String(url).includes('upload-url'))
|
|
584
|
+
expect(uploadCall?.[1]?.credentials).not.toEqual('include')
|
|
563
585
|
})
|
|
564
586
|
|
|
565
|
-
it('does not include credentials in a local cross-origin upload', () => {
|
|
587
|
+
it('does not include credentials in a local cross-origin upload', async () => {
|
|
588
|
+
const fetchSpy = jest.spyOn(global, 'fetch')
|
|
566
589
|
preflightProps.upload_params.success_url = undefined
|
|
567
|
-
const crossOriginUploadUrl = 'cross-origin.site/files_api'
|
|
590
|
+
const crossOriginUploadUrl = 'http://cross-origin.site/files_api'
|
|
568
591
|
preflightProps.upload_url = crossOriginUploadUrl
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
592
|
+
server.use(http.post(crossOriginUploadUrl, () => HttpResponse.json(file)))
|
|
593
|
+
await apiSource.uploadFRD(fileDomObject, preflightProps)
|
|
594
|
+
const uploadCall = fetchSpy.mock.calls.find(([url]) =>
|
|
595
|
+
String(url).includes('cross-origin.site'),
|
|
596
|
+
)
|
|
597
|
+
expect(uploadCall?.[1]?.credentials).not.toEqual('include')
|
|
573
598
|
})
|
|
574
599
|
|
|
575
600
|
it('handles s3 post-flight', async () => {
|
|
576
|
-
preflightProps.upload_params.success_url = 'success-url'
|
|
601
|
+
preflightProps.upload_params.success_url = 'http://success-url/'
|
|
577
602
|
const s3File = {url: 's3-file-url'}
|
|
578
|
-
|
|
603
|
+
server.use(http.get('http://success-url/', () => HttpResponse.json(s3File)))
|
|
579
604
|
const result = await apiSource.uploadFRD(fileDomObject, preflightProps)
|
|
580
605
|
expect(result).toEqual(s3File)
|
|
581
606
|
})
|
|
582
607
|
|
|
583
|
-
it('handles inst-fs post-flight', () => {
|
|
584
|
-
preflightProps.upload_url = 'instfs-upload-url'
|
|
608
|
+
it('handles inst-fs post-flight', async () => {
|
|
585
609
|
const fileId = '123'
|
|
610
|
+
const instFsUrl = 'http://instfs-upload-url/'
|
|
611
|
+
preflightProps.upload_url = instFsUrl
|
|
586
612
|
const response = {
|
|
587
613
|
location: `http://canvas/api/v1/files/${fileId}?foo=bar`,
|
|
588
614
|
uuid: 'xyzzy',
|
|
589
615
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
})
|
|
616
|
+
server.use(http.post(instFsUrl, () => HttpResponse.json(response)))
|
|
617
|
+
const resp = await apiSource.uploadFRD(fileDomObject, preflightProps)
|
|
618
|
+
expect(apiSource.getFile).toHaveBeenCalledWith(fileId)
|
|
619
|
+
expect(resp.uuid).toEqual('xyzzy')
|
|
620
|
+
expect(resp.url).toEqual('file-url')
|
|
596
621
|
})
|
|
597
622
|
|
|
598
|
-
it('handles inst-fs post-flight with global file id', () => {
|
|
599
|
-
preflightProps.upload_url = 'instfs-upload-url'
|
|
623
|
+
it('handles inst-fs post-flight with global file id', async () => {
|
|
600
624
|
const fileId = '1023~789'
|
|
625
|
+
const instFsUrl = 'http://instfs-upload-url/'
|
|
626
|
+
preflightProps.upload_url = instFsUrl
|
|
601
627
|
const response = {
|
|
602
628
|
location: `http://canvas/api/v1/files/${fileId}?foo=bar`,
|
|
603
629
|
uuid: 'xyzzy',
|
|
604
630
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
})
|
|
631
|
+
server.use(http.post(instFsUrl, () => HttpResponse.json(response)))
|
|
632
|
+
const resp = await apiSource.uploadFRD(fileDomObject, preflightProps)
|
|
633
|
+
expect(apiSource.getFile).toHaveBeenCalledWith(fileId)
|
|
634
|
+
expect(resp.uuid).toEqual('xyzzy')
|
|
635
|
+
expect(resp.url).toEqual('file-url')
|
|
611
636
|
})
|
|
612
637
|
})
|
|
613
638
|
})
|
|
@@ -628,13 +653,13 @@ describe('sources/api', () => {
|
|
|
628
653
|
props.searchString = 'panda'
|
|
629
654
|
|
|
630
655
|
it('can fetch folders', async () => {
|
|
631
|
-
|
|
656
|
+
server.use(http.get(/\/api\/folders/, () => HttpResponse.json(body)))
|
|
632
657
|
const page = await apiSource.fetchRootFolder(props)
|
|
633
658
|
expect(page).toEqual(body)
|
|
634
659
|
})
|
|
635
660
|
|
|
636
661
|
it('requests images from API', async () => {
|
|
637
|
-
|
|
662
|
+
server.use(http.get(/\/api\/documents/, () => HttpResponse.json(body)))
|
|
638
663
|
const page = await apiSource.fetchImages(props)
|
|
639
664
|
expect(page).toEqual({
|
|
640
665
|
bookmark: 'mo.images',
|
|
@@ -644,9 +669,8 @@ describe('sources/api', () => {
|
|
|
644
669
|
})
|
|
645
670
|
|
|
646
671
|
it('requests subsequent page of images from API', async () => {
|
|
647
|
-
props.images.group.bookmark =
|
|
648
|
-
|
|
649
|
-
fetchMock.mock(/mo.images/, {body})
|
|
672
|
+
props.images.group.bookmark = `${BASE}/api/documents?page=2`
|
|
673
|
+
server.use(http.get(`${BASE}/api/documents`, () => HttpResponse.json(body)))
|
|
650
674
|
const page = await apiSource.fetchImages(props)
|
|
651
675
|
expect(page).toEqual({
|
|
652
676
|
bookmark: 'mo.images',
|
|
@@ -657,124 +681,129 @@ describe('sources/api', () => {
|
|
|
657
681
|
})
|
|
658
682
|
|
|
659
683
|
describe('getSession', () => {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
684
|
+
it('includes jwt in Authorization header', async () => {
|
|
685
|
+
let capturedRequest
|
|
686
|
+
server.use(
|
|
687
|
+
http.get(`${BASE}/api/session`, ({request}) => {
|
|
688
|
+
capturedRequest = request
|
|
689
|
+
return HttpResponse.json({})
|
|
690
|
+
}),
|
|
691
|
+
)
|
|
692
|
+
await apiSource.getSession()
|
|
693
|
+
expect(capturedRequest.headers.get('Authorization')).toEqual('Bearer theJWT')
|
|
666
694
|
})
|
|
667
695
|
})
|
|
668
696
|
|
|
669
697
|
describe('setUsageRights', () => {
|
|
670
|
-
const uri =
|
|
698
|
+
const uri = `${BASE}/api/usage_rights`
|
|
671
699
|
const fileId = 47
|
|
672
700
|
const usageRights = {usageRight: 'foo'}
|
|
673
701
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
702
|
+
it('includes jwt in Authorization header', async () => {
|
|
703
|
+
let capturedRequest
|
|
704
|
+
server.use(
|
|
705
|
+
http.post(uri, ({request}) => {
|
|
706
|
+
capturedRequest = request
|
|
707
|
+
return HttpResponse.json({})
|
|
708
|
+
}),
|
|
709
|
+
)
|
|
710
|
+
await apiSource.setUsageRights(fileId, usageRights)
|
|
711
|
+
expect(capturedRequest.headers.get('Authorization')).toEqual('Bearer theJWT')
|
|
682
712
|
})
|
|
683
713
|
|
|
684
|
-
it('posts file id and usage rights to the api', () => {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
})
|
|
714
|
+
it('posts file id and usage rights to the api', async () => {
|
|
715
|
+
let capturedBody
|
|
716
|
+
server.use(
|
|
717
|
+
http.post(uri, async ({request}) => {
|
|
718
|
+
capturedBody = await request.json()
|
|
719
|
+
return HttpResponse.json({})
|
|
720
|
+
}),
|
|
721
|
+
)
|
|
722
|
+
await apiSource.setUsageRights(fileId, usageRights)
|
|
723
|
+
expect(capturedBody).toEqual({
|
|
724
|
+
fileId,
|
|
725
|
+
usageRight: usageRights.usageRight,
|
|
691
726
|
})
|
|
692
727
|
})
|
|
693
728
|
})
|
|
694
729
|
|
|
695
730
|
describe('getFile', () => {
|
|
696
731
|
const id = 47
|
|
697
|
-
const uri =
|
|
732
|
+
const uri = `${BASE}/api/file/${id}`
|
|
698
733
|
let url = '/file/url'
|
|
699
734
|
setProps = {}
|
|
700
735
|
|
|
701
|
-
it('includes jwt in Authorization header', () => {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
736
|
+
it('includes jwt in Authorization header', async () => {
|
|
737
|
+
let capturedRequest
|
|
738
|
+
server.use(
|
|
739
|
+
http.get(uri, ({request}) => {
|
|
740
|
+
capturedRequest = request
|
|
741
|
+
return HttpResponse.json({url})
|
|
742
|
+
}),
|
|
743
|
+
)
|
|
744
|
+
await apiSource.getFile(id, setProps)
|
|
745
|
+
expect(capturedRequest.headers.get('Authorization')).toEqual('Bearer theJWT')
|
|
707
746
|
})
|
|
708
747
|
|
|
709
|
-
it('retries once with fresh token on 401', () => {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
return uri === fetchUrl && opts.headers.Authorization === 'Bearer freshJWT'
|
|
717
|
-
},
|
|
718
|
-
{upload: 'done', url}
|
|
748
|
+
it('retries once with fresh token on 401', async () => {
|
|
749
|
+
server.use(
|
|
750
|
+
http.get(uri, ({request}) => {
|
|
751
|
+
const auth = request.headers.get('Authorization')
|
|
752
|
+
if (auth === 'Bearer theJWT') return new HttpResponse(null, {status: 401})
|
|
753
|
+
return HttpResponse.json({upload: 'done', url})
|
|
754
|
+
}),
|
|
719
755
|
)
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
expect(response.upload).toEqual('done')
|
|
723
|
-
})
|
|
756
|
+
const response = await apiSource.getFile(id, setProps)
|
|
757
|
+
expect(response.upload).toEqual('done')
|
|
724
758
|
})
|
|
725
759
|
|
|
726
|
-
it('notifies a provided callback when a new token is fetched', () => {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
return uri === fetchUrl && opts.headers.Authorization === 'Bearer freshJWT'
|
|
734
|
-
},
|
|
735
|
-
{upload: 'done', url}
|
|
760
|
+
it('notifies a provided callback when a new token is fetched', async () => {
|
|
761
|
+
server.use(
|
|
762
|
+
http.get(uri, ({request}) => {
|
|
763
|
+
const auth = request.headers.get('Authorization')
|
|
764
|
+
if (auth === 'Bearer theJWT') return new HttpResponse(null, {status: 401})
|
|
765
|
+
return HttpResponse.json({upload: 'done', url})
|
|
766
|
+
}),
|
|
736
767
|
)
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
expect(apiSource.jwt).toEqual('freshJWT')
|
|
740
|
-
})
|
|
768
|
+
await apiSource.getFile(id, setProps)
|
|
769
|
+
expect(apiSource.jwt).toEqual('freshJWT')
|
|
741
770
|
})
|
|
742
771
|
|
|
743
|
-
it('transforms file url with downloadToWrap', () => {
|
|
772
|
+
it('transforms file url with downloadToWrap', async () => {
|
|
744
773
|
url = '/file/url?download_frd=1'
|
|
745
774
|
const wrapUrl = '/file/url?wrap=1'
|
|
746
|
-
|
|
775
|
+
server.use(http.get(uri, () => HttpResponse.json({url})))
|
|
747
776
|
jest.spyOn(fileUrl, 'downloadToWrap').mockReturnValue(wrapUrl)
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
fetchMock.restore()
|
|
752
|
-
})
|
|
777
|
+
const file = await apiSource.getFile(id)
|
|
778
|
+
expect(fileUrl.downloadToWrap).toHaveBeenCalledWith(url)
|
|
779
|
+
expect(file.href).toEqual(wrapUrl)
|
|
753
780
|
})
|
|
754
781
|
|
|
755
|
-
it('defaults display_name to name', () => {
|
|
782
|
+
it('defaults display_name to name', async () => {
|
|
756
783
|
url = '/file/url?download_frd=1'
|
|
757
784
|
const name = 'filename'
|
|
758
|
-
|
|
785
|
+
server.use(http.get(uri, () => HttpResponse.json({url, name})))
|
|
759
786
|
jest.spyOn(fileUrl, 'downloadToWrap')
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
fetchMock.restore()
|
|
763
|
-
})
|
|
787
|
+
const file = await apiSource.getFile(id)
|
|
788
|
+
expect(file.display_name).toEqual(name)
|
|
764
789
|
})
|
|
765
790
|
})
|
|
766
791
|
|
|
767
792
|
describe('media object apis', () => {
|
|
768
793
|
describe('updateMediaObject', () => {
|
|
769
794
|
it('PUTs to the media_object endpoint', async () => {
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
{
|
|
795
|
+
const mediaId = 'm-id'
|
|
796
|
+
const title = 'new title'
|
|
797
|
+
let capturedRequest
|
|
798
|
+
server.use(
|
|
799
|
+
http.put(`${BASE}/api/media_objects/:mediaId`, ({request}) => {
|
|
800
|
+
capturedRequest = request
|
|
801
|
+
return HttpResponse.json({media_id: mediaId, title})
|
|
802
|
+
}),
|
|
775
803
|
)
|
|
776
|
-
|
|
777
|
-
expect(
|
|
804
|
+
const response = await apiSource.updateMediaObject({}, {media_object_id: mediaId, title})
|
|
805
|
+
expect(capturedRequest.headers.get('Authorization')).toEqual('Bearer theJWT')
|
|
806
|
+
expect(response).toEqual({media_id: mediaId, title})
|
|
778
807
|
})
|
|
779
808
|
})
|
|
780
809
|
})
|