@instructure/canvas-rce 7.3.0 → 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.
Files changed (162) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/{es/rce/plugins/shared/ai_tools/index.js → __mocks__/@instructure/ui-media-player/_mockUiMediaPlayer.js} +4 -4
  3. package/__tests__/common/mimeClass.test.js +25 -1
  4. package/__tests__/rcs/api.test.js +280 -251
  5. package/es/canvasFileBrowser/FileBrowser.d.ts +2 -2
  6. package/es/canvasFileBrowser/FileBrowser.js +8 -7
  7. package/es/common/mimeClass.js +3 -1
  8. package/es/defaultTinymceConfig.js +47 -49
  9. package/es/enhance-user-content/doc_previews.js +5 -0
  10. package/es/enhance-user-content/enhance_user_content.js +6 -8
  11. package/es/enhance-user-content/index.d.ts +3 -1
  12. package/es/enhance-user-content/index.js +3 -1
  13. package/es/enhance-user-content/instructure_helper.js +1 -0
  14. package/es/enhance-user-content/youtube_overlay.js +18 -0
  15. package/es/getThemeVars.d.ts +1 -1
  16. package/es/getThemeVars.js +23 -26
  17. package/es/rce/AlertMessageArea.d.ts +2 -2
  18. package/es/rce/AlertMessageArea.js +3 -3
  19. package/es/rce/KeyboardShortcutModal.js +2 -2
  20. package/es/rce/RCE.d.ts +9 -0
  21. package/es/rce/RCE.js +4 -0
  22. package/es/rce/RCEGlobals.d.ts +2 -0
  23. package/es/rce/RCEGlobals.js +1 -0
  24. package/es/rce/RCEVariants.d.ts +1 -2
  25. package/es/rce/RCEVariants.js +1 -2
  26. package/es/rce/RCEWrapper.d.ts +16 -26
  27. package/es/rce/RCEWrapper.js +227 -271
  28. package/es/rce/RCEWrapper.utils.d.ts +1 -1
  29. package/es/rce/RCEWrapperProps.d.ts +2 -1
  30. package/es/rce/RCEWrapperProps.js +2 -1
  31. package/es/rce/StatusBar.d.ts +0 -1
  32. package/es/rce/StatusBar.js +3 -28
  33. package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.d.ts +2 -1
  34. package/es/rce/plugins/instructure_equation/EquationEditorModal/advancedOnlySyntax.js +3 -1
  35. package/es/rce/plugins/instructure_equation/EquationEditorModal/index.d.ts +1 -0
  36. package/es/rce/plugins/instructure_equation/EquationEditorModal/index.js +12 -2
  37. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageOptions.js +2 -2
  38. package/es/rce/plugins/instructure_icon_maker/components/CreateIconMakerForm/ImageSection/ImageSection.js +3 -3
  39. package/es/rce/plugins/instructure_icon_maker/svg/constants.d.ts +20 -5
  40. package/es/rce/plugins/instructure_icon_maker/svg/utils.d.ts +1 -1
  41. package/es/rce/plugins/instructure_icon_maker/utils/IconMakerFormHasChanges.js +2 -2
  42. package/es/rce/plugins/instructure_image/ImageEmbedOptions.d.ts +0 -2
  43. package/es/rce/plugins/instructure_image/ImageEmbedOptions.js +2 -9
  44. package/es/rce/plugins/instructure_paste/plugin.js +18 -12
  45. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolDialog/ExternalToolDialogModal.d.ts +1 -1
  46. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.d.ts +1 -1
  47. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.js +25 -25
  48. package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.d.ts +4 -0
  49. package/es/rce/plugins/instructure_rce_external_tools/components/util/ToolLaunchIframe.js +4 -0
  50. package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.d.ts +11 -2
  51. package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +92 -10
  52. package/es/rce/plugins/instructure_record/AudioOptionsTray/index.d.ts +13 -1
  53. package/es/rce/plugins/instructure_record/AudioOptionsTray/index.js +216 -24
  54. package/es/rce/plugins/instructure_record/MediaPanel/index.js +16 -5
  55. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.d.ts +14 -13
  56. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +110 -39
  57. package/es/rce/plugins/instructure_record/VideoOptionsTray/index.d.ts +11 -1
  58. package/es/rce/plugins/instructure_record/VideoOptionsTray/index.js +242 -67
  59. package/es/rce/plugins/instructure_record/clickCallback.js +19 -4
  60. package/es/rce/plugins/instructure_record/mediaTranslations.js +1 -1
  61. package/es/rce/plugins/instructure_record/playerLayoutOptions.d.ts +25 -0
  62. package/es/rce/plugins/instructure_record/playerLayoutOptions.js +91 -0
  63. package/es/rce/plugins/instructure_record/plugin.js +2 -5
  64. package/es/rce/plugins/instructure_record/utils.d.ts +3 -0
  65. package/es/rce/plugins/instructure_record/utils.js +31 -0
  66. package/es/rce/plugins/instructure_studio_media_options/plugin.js +82 -24
  67. package/es/rce/plugins/instructure_wordcount/components/WordCountModal.js +1 -0
  68. package/es/rce/plugins/shared/ContentSelection.d.ts +6 -1
  69. package/es/rce/plugins/shared/ContentSelection.js +15 -6
  70. package/es/rce/plugins/shared/DimensionsInput/DimensionInput.js +1 -2
  71. package/es/rce/plugins/shared/DimensionsInput/index.js +11 -12
  72. package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.d.ts +1 -1
  73. package/es/rce/plugins/shared/DimensionsInput/useDimensionsState.js +4 -3
  74. package/es/rce/plugins/shared/StudioLtiSupportUtils.d.ts +27 -5
  75. package/es/rce/plugins/shared/StudioLtiSupportUtils.js +94 -9
  76. package/es/rce/plugins/shared/Upload/UploadFile.js +1 -8
  77. package/es/rce/style.d.ts +2 -1
  78. package/es/rce/style.js +4 -2
  79. package/es/rcs/api.d.ts +5 -10
  80. package/es/rcs/api.js +15 -21
  81. package/es/rcs/fake.d.ts +1 -7
  82. package/es/rcs/fake.js +1 -47
  83. package/es/sidebar/actions/media.d.ts +19 -6
  84. package/es/sidebar/actions/media.js +17 -4
  85. package/es/sidebar/actions/upload.d.ts +3 -3
  86. package/es/sidebar/actions/upload.js +9 -9
  87. package/es/sidebar/containers/Sidebar.js +0 -2
  88. package/es/sidebar/containers/sidebarHandlers.d.ts +2 -4
  89. package/es/sidebar/containers/sidebarHandlers.js +2 -5
  90. package/es/sidebar/reducers/index.d.ts +0 -1
  91. package/es/sidebar/reducers/index.js +0 -2
  92. package/es/sidebar/store/initialState.d.ts +0 -1
  93. package/es/sidebar/store/initialState.js +0 -5
  94. package/es/translations/locales/ar.js +77 -77
  95. package/es/translations/locales/ca.js +77 -77
  96. package/es/translations/locales/cy.js +77 -77
  97. package/es/translations/locales/da-x-k12.js +77 -77
  98. package/es/translations/locales/da.js +77 -77
  99. package/es/translations/locales/de.js +77 -77
  100. package/es/translations/locales/el.js +0 -9
  101. package/es/translations/locales/en-AU-x-unimelb.js +77 -77
  102. package/es/translations/locales/en-GB-x-ukhe.js +77 -77
  103. package/es/translations/locales/en.js +67 -79
  104. package/es/translations/locales/en_AU.js +77 -77
  105. package/es/translations/locales/en_CA.js +77 -77
  106. package/es/translations/locales/en_CY.js +77 -77
  107. package/es/translations/locales/en_GB.js +77 -77
  108. package/es/translations/locales/es.js +77 -77
  109. package/es/translations/locales/es_ES.js +77 -77
  110. package/es/translations/locales/fa_IR.js +0 -9
  111. package/es/translations/locales/fi.js +77 -77
  112. package/es/translations/locales/fr.js +77 -77
  113. package/es/translations/locales/fr_CA.js +77 -77
  114. package/es/translations/locales/ga.js +77 -77
  115. package/es/translations/locales/he.js +0 -9
  116. package/es/translations/locales/hi.js +77 -77
  117. package/es/translations/locales/ht.js +77 -77
  118. package/es/translations/locales/hu.js +0 -36
  119. package/es/translations/locales/hy.js +0 -9
  120. package/es/translations/locales/id.js +77 -77
  121. package/es/translations/locales/is.js +77 -77
  122. package/es/translations/locales/it.js +77 -77
  123. package/es/translations/locales/ja.js +77 -77
  124. package/es/translations/locales/ko.js +2455 -133
  125. package/es/translations/locales/mi.js +77 -77
  126. package/es/translations/locales/ms.js +77 -77
  127. package/es/translations/locales/nb-x-k12.js +77 -77
  128. package/es/translations/locales/nb.js +77 -77
  129. package/es/translations/locales/nl.js +78 -78
  130. package/es/translations/locales/nn.js +0 -36
  131. package/es/translations/locales/pl.js +77 -77
  132. package/es/translations/locales/pt.js +77 -77
  133. package/es/translations/locales/pt_BR.js +77 -77
  134. package/es/translations/locales/ru.js +77 -77
  135. package/es/translations/locales/sl.js +77 -77
  136. package/es/translations/locales/sv-x-k12.js +77 -77
  137. package/es/translations/locales/sv.js +77 -77
  138. package/es/translations/locales/th.js +77 -77
  139. package/es/translations/locales/tr.js +1962 -18
  140. package/es/translations/locales/uk_UA.js +0 -9
  141. package/es/translations/locales/vi.js +77 -77
  142. package/es/translations/locales/zh-Hans.js +77 -77
  143. package/es/translations/locales/zh-Hant.js +77 -77
  144. package/es/translations/locales/zh.js +77 -77
  145. package/es/translations/locales/zh_HK.js +77 -77
  146. package/eslint.config.js +16 -147
  147. package/jest/jest-setup.js +1 -0
  148. package/jest.config.js +2 -0
  149. package/oxlint.json +84 -0
  150. package/package.json +86 -62
  151. package/tsconfig.json +3 -2
  152. package/es/rce/plugins/shared/ai_tools/AIResponseModal.d.ts +0 -10
  153. package/es/rce/plugins/shared/ai_tools/AIResponseModal.js +0 -67
  154. package/es/rce/plugins/shared/ai_tools/AIToolsTray.d.ts +0 -18
  155. package/es/rce/plugins/shared/ai_tools/AIToolsTray.js +0 -489
  156. package/es/rce/plugins/shared/ai_tools/aiicons.d.ts +0 -7
  157. package/es/rce/plugins/shared/ai_tools/aiicons.js +0 -60
  158. package/es/rce/plugins/shared/ai_tools/index.d.ts +0 -3
  159. package/es/sidebar/actions/flickr.d.ts +0 -20
  160. package/es/sidebar/actions/flickr.js +0 -60
  161. package/es/sidebar/reducers/flickr.d.ts +0 -1
  162. 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
- fetchMock.restore()
51
- jest.resetAllMocks()
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 = apiSource.initializeCollection(endpoint, noHostProps)
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 = apiSource.baseUri('files')
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 = apiSource.baseUri('files', null, fakeWindow)
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 = apiSource.uriFor('documents', setProps)
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 = apiSource.uriFor('images', setProps)
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 = apiSource.uriFor('media', setProps)
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 = apiSource.uriFor('media_objects', setProps)
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 = 'theURI'
170
- const fakePageBody =
171
- '{"bookmark":"newBookmark","links":[' +
172
- '{"href":"link1","title":"Link 1"},' +
173
- '{"href":"link2","title":"Link 2"}]}'
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
- fetchMock.mock(uri, '{}')
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(fetchMock.lastOptions(uri).headers.Authorization).toEqual('Bearer theJWT')
208
+ expect(capturedRequest.headers.get('Authorization')).toEqual('Bearer theJWT')
179
209
  })
180
210
 
181
211
  it('converts 400+ statuses to errors', async () => {
182
- fetchMock.mock(uri, 403)
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
- fetchMock.mock(uri, fakePageBody)
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
- fetchMock.mock((fetchUrl, opts) => {
200
- return uri === fetchUrl && opts.headers.Authorization === 'Bearer theJWT'
201
- }, 401)
202
-
203
- fetchMock.mock((fetchUrl, opts) => {
204
- return uri === fetchUrl && opts.headers.Authorization === 'Bearer freshJWT'
205
- }, fakePageBody)
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 uri = 'files-uri'
227
- const body = await apiSource.fetchFiles(uri)
228
- expect(apiSource.fetchPage).toHaveBeenCalledWith(uri)
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('foo')
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
- jest.resetAllMocks()
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
- '/api/folders/icon_maker?contextType=course&contextId=22'
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
- '/api/folders/media?contextType=course&contextId=22'
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 = '/api/upload'
418
+ const uri = `${BASE}/api/upload`
398
419
  const fileProps = {}
399
420
  const apiProps = {}
400
421
 
401
- afterEach(() => {
402
- fetchMock.restore()
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 "onDuplicate"', () => {
406
- fetchMock.mock(uri, '{}')
407
-
408
- return apiSource.preflightUpload(fileProps, {onDuplicate: 'overwrite'}, apiProps).then(() => {
409
- const body = JSON.parse(fetchMock.lastOptions(uri).body)
410
- expect(body.onDuplicate).toEqual('overwrite')
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 "category"', () => {
415
- fetchMock.mock(uri, '{}')
416
-
417
- return apiSource
418
- .preflightUpload(fileProps, {category: ICON_MAKER_ICONS}, apiProps)
419
- .then(() => {
420
- const body = JSON.parse(fetchMock.lastOptions(uri).body)
421
- expect(body.category).toEqual(ICON_MAKER_ICONS)
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('includes jwt in Authorization header', () => {
426
- fetchMock.mock(uri, '{}')
427
-
428
- return apiSource.preflightUpload(fileProps, apiProps).then(() => {
429
- expect(fetchMock.lastOptions(uri).headers.Authorization).toEqual('Bearer theJWT')
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('retries once with fresh token on 401', () => {
434
- fetchMock.mock((fetchUrl, opts) => {
435
- return uri === fetchUrl && opts.headers.Authorization === 'Bearer theJWT'
436
- }, 401)
437
-
438
- fetchMock.mock((fetchUrl, opts) => {
439
- return uri === fetchUrl && opts.headers.Authorization === 'Bearer freshJWT'
440
- }, '{"upload": "done"}')
441
-
442
- return apiSource.preflightUpload(fileProps, apiProps).then(response => {
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('notifies a provided callback when a new token is fetched', () => {
448
- fetchMock.mock((fetchUrl, opts) => {
449
- return uri === fetchUrl && opts.headers.Authorization === 'Bearer theJWT'
450
- }, 401)
451
-
452
- fetchMock.mock((fetchUrl, opts) => {
453
- return uri === fetchUrl && opts.headers.Authorization === 'Bearer freshJWT'
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
- fetchMock.mock(uri, 500)
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
- const error = new Error('file size exceeds quota')
486
- error.response = {json: async () => ({message: 'file size exceeds quota'})}
487
-
488
- fetchMock.mock(uri, {throws: error}, {overwriteRoutes: true})
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
- } // This will re-throw so we just catch it here/
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
- fetchMock.mock(uploadUrl, file)
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
- fetchMock.once(uploadUrl, 500, {overwriteRoutes: true})
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
- afterEach(() => {
545
- jest.restoreAllMocks()
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
- return apiSource.uploadFRD(fileDomObject, preflightProps).then(() => {
551
- expect(fetchMock.lastOptions(uploadUrl).credentials).toEqual('include')
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
- fetchMock.mock(preflightProps.upload_params.success_url, s3File)
560
- return apiSource.uploadFRD(fileDomObject, preflightProps).then(() => {
561
- expect(fetchMock.lastOptions(uploadUrl).credentials).toBeUndefined()
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
- fetchMock.mock(crossOriginUploadUrl, file)
570
- return apiSource.uploadFRD(fileDomObject, preflightProps).then(() => {
571
- expect(fetchMock.lastOptions(crossOriginUploadUrl).credentials).toBeUndefined()
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
- fetchMock.mock(preflightProps.upload_params.success_url, s3File)
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
- fetchMock.mock(preflightProps.upload_url, response)
591
- return apiSource.uploadFRD(fileDomObject, preflightProps).then(resp => {
592
- expect(apiSource.getFile).toHaveBeenCalledWith(fileId)
593
- expect(resp.uuid).toEqual('xyzzy')
594
- expect(resp.url).toEqual('file-url')
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
- fetchMock.mock(preflightProps.upload_url, response)
606
- return apiSource.uploadFRD(fileDomObject, preflightProps).then(resp => {
607
- expect(apiSource.getFile).toHaveBeenCalledWith(fileId)
608
- expect(resp.uuid).toEqual('xyzzy')
609
- expect(resp.url).toEqual('file-url')
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
- fetchMock.mock(/\/folders\?/, {body})
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
- fetchMock.mock(/\/documents\?.*content_types=image/, {body})
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 = 'mo.images'
648
- fetchMock.mock(/\/documents\?.*content_types=image/, 'should not get here')
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
- const uri = '/api/session' // already mocked
661
-
662
- it('includes jwt in Authorization header', () => {
663
- return apiSource.getSession().then(() => {
664
- expect(fetchMock.lastOptions(uri).headers.Authorization).toEqual('Bearer theJWT')
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 = '/api/usage_rights'
698
+ const uri = `${BASE}/api/usage_rights`
671
699
  const fileId = 47
672
700
  const usageRights = {usageRight: 'foo'}
673
701
 
674
- beforeEach(() => {
675
- fetchMock.mock(uri, '{}')
676
- })
677
-
678
- it('includes jwt in Authorization header', () => {
679
- return apiSource.setUsageRights(fileId, usageRights).then(() => {
680
- expect(fetchMock.lastOptions(uri).headers.Authorization).toEqual('Bearer theJWT')
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
- return apiSource.setUsageRights(fileId, usageRights).then(() => {
686
- const postBody = JSON.parse(fetchMock.lastOptions(uri).body)
687
- expect(postBody).toEqual({
688
- fileId,
689
- usageRight: usageRights.usageRight,
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 = `/api/file/${id}`
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
- fetchMock.mock(uri, {url})
703
-
704
- return apiSource.getFile(id, setProps).then(() => {
705
- expect(fetchMock.lastOptions(uri).headers.Authorization).toEqual('Bearer theJWT')
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
- fetchMock.mock((fetchUrl, opts) => {
711
- return uri === fetchUrl && opts.headers.Authorization === 'Bearer theJWT'
712
- }, 401)
713
-
714
- fetchMock.mock(
715
- (fetchUrl, opts) => {
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
- return apiSource.getFile(id, setProps).then(response => {
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
- fetchMock.mock((fetchUrl, opts) => {
728
- return uri === fetchUrl && opts.headers.Authorization === 'Bearer theJWT'
729
- }, 401)
730
-
731
- fetchMock.mock(
732
- (fetchUrl, opts) => {
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
- return apiSource.getFile(id, setProps).then(() => {
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
- fetchMock.mock('*', {url})
775
+ server.use(http.get(uri, () => HttpResponse.json({url})))
747
776
  jest.spyOn(fileUrl, 'downloadToWrap').mockReturnValue(wrapUrl)
748
- return apiSource.getFile(id).then(file => {
749
- expect(fileUrl.downloadToWrap).toHaveBeenCalledWith(url)
750
- expect(file.href).toEqual(wrapUrl)
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
- fetchMock.mock('*', {url, name})
785
+ server.use(http.get(uri, () => HttpResponse.json({url, name})))
759
786
  jest.spyOn(fileUrl, 'downloadToWrap')
760
- return apiSource.getFile(id).then(file => {
761
- expect(file.display_name).toEqual(name)
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 uri = `/api/media_objects/m-id?user_entered_title=${encodeURIComponent('new title')}`
771
- fetchMock.put(uri, '{"media_id": "m-id", "title": "new title"}')
772
- const response = await apiSource.updateMediaObject(
773
- {},
774
- {media_object_id: 'm-id', title: 'new title'}
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
- expect(fetchMock.lastOptions(uri).headers.Authorization).toEqual('Bearer theJWT')
777
- expect(response).toEqual({media_id: 'm-id', title: 'new title'})
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
  })