11import { faker } from "@faker-js/faker/locale/en"
22import { factories , urls , makeRequest } from "api/test-utils"
3- import type {
4- LearningPathResource ,
5- PaginatedLearningResourceTopicList ,
3+ import {
4+ PrivacyLevelEnum ,
5+ type LearningPathResource ,
6+ type PaginatedLearningResourceTopicList ,
7+ type UserList ,
68} from "api"
79import { allowConsoleErrors , getDescriptionFor } from "ol-test-utilities"
8- import { manageLearningPathDialogs } from "./ManageListDialogs"
10+ import { manageListDialogs } from "./ManageListDialogs"
911import {
1012 screen ,
1113 renderWithProviders ,
@@ -39,6 +41,11 @@ const inputs = {
3941 const element = screen . getByLabelText ( value ? "Public" : "Private" )
4042 return element as HTMLInputElement
4143 } ,
44+ privacy_level : ( value ?: string ) => {
45+ invariant ( value !== undefined )
46+ const element = screen . getByDisplayValue ( value )
47+ return element as HTMLInputElement
48+ } ,
4249 title : ( ) => screen . getByLabelText ( "Title" , { exact : false } ) ,
4350 description : ( ) => screen . getByLabelText ( "Description" , { exact : false } ) ,
4451 topics : ( ) => screen . getByLabelText ( "Subjects" , { exact : false } ) ,
@@ -47,7 +54,7 @@ const inputs = {
4754 delete : ( ) => screen . getByRole ( "button" , { name : "Yes, delete" } ) ,
4855}
4956
50- describe ( "manageListDialogs.upsert " , ( ) => {
57+ describe ( "manageListDialogs.upsertLearningPath " , ( ) => {
5158 const setup = ( {
5259 resource,
5360 topics = factories . learningResources . topics ( { count : 10 } ) ,
@@ -70,7 +77,7 @@ describe("manageListDialogs.upsert", () => {
7077 renderWithProviders ( null , opts )
7178
7279 act ( ( ) => {
73- manageLearningPathDialogs . upsert ( resource )
80+ manageListDialogs . upsertLearningPath ( resource )
7481 } )
7582
7683 return { topics }
@@ -211,12 +218,145 @@ describe("manageListDialogs.upsert", () => {
211218 } )
212219} )
213220
214- describe ( "manageListDialogs.destroy" , ( ) => {
221+ describe ( "manageListDialogs.upsertUserList" , ( ) => {
222+ const setup = ( {
223+ userList,
224+ opts = {
225+ user : { is_authenticated : true } ,
226+ } ,
227+ } : {
228+ userList ?: UserList
229+ opts ?: Partial < TestAppOptions >
230+ } = { } ) => {
231+ renderWithProviders ( null , opts )
232+
233+ act ( ( ) => {
234+ manageListDialogs . upsertUserList ( userList )
235+ } )
236+ }
237+
238+ test . each ( [
239+ {
240+ userList : undefined ,
241+ expectedTitle : "Create User List" ,
242+ } ,
243+ {
244+ userList : factories . userLists . userList ( ) ,
245+ expectedTitle : "Edit User List" ,
246+ } ,
247+ ] ) (
248+ "Dialog title is $expectedTitle when userList=$userList" ,
249+ async ( { userList, expectedTitle } ) => {
250+ setup ( { userList } )
251+ const dialog = screen . getByRole ( "heading" , { name : expectedTitle } )
252+ expect ( dialog ) . toBeVisible ( )
253+ } ,
254+ )
255+
256+ test ( "'Cancel' closes dialog (and does not make request)" , async ( ) => {
257+ // behavior does not depend on stafflist / userlist, so just pick one
258+ setup ( {
259+ userList : factories . userLists . userList ( ) ,
260+ } )
261+ const dialog = screen . getByRole ( "dialog" )
262+ await user . click ( inputs . cancel ( ) )
263+ expect ( makeRequest ) . not . toHaveBeenCalledWith (
264+ "patch" ,
265+ expect . anything ( ) ,
266+ expect . anything ( ) ,
267+ )
268+ await waitForElementToBeRemoved ( dialog )
269+ } )
270+
271+ test ( "Validates required fields" , async ( ) => {
272+ setup ( )
273+ await user . click ( inputs . submit ( ) )
274+
275+ const titleInput = inputs . title ( )
276+ const titleFeedback = getDescriptionFor ( titleInput )
277+ expect ( titleInput ) . toBeInvalid ( )
278+ expect ( titleFeedback ) . toHaveTextContent ( "Title is required." )
279+
280+ const descriptionInput = inputs . description ( )
281+ const descriptionFeedback = getDescriptionFor ( descriptionInput )
282+ expect ( descriptionInput ) . toBeInvalid ( )
283+ expect ( descriptionFeedback ) . toHaveTextContent ( "Description is required." )
284+ } )
285+
286+ test ( "Form defaults are set" , ( ) => {
287+ setup ( )
288+ expect ( inputs . title ( ) ) . toHaveValue ( "" )
289+ expect ( inputs . description ( ) ) . toHaveValue ( "" )
290+ expect ( inputs . privacy_level ( PrivacyLevelEnum . Private ) . checked ) . toBe ( true )
291+ expect ( inputs . privacy_level ( PrivacyLevelEnum . Unlisted ) . checked ) . toBe ( false )
292+ } )
293+
294+ test ( "Editing form values" , async ( ) => {
295+ const userList = factories . userLists . userList ( )
296+ setup ( { userList : userList } )
297+ const patch = {
298+ title : faker . lorem . words ( ) ,
299+ description : faker . lorem . paragraph ( ) ,
300+ privacy_level : PrivacyLevelEnum . Unlisted ,
301+ }
302+
303+ // Title
304+ expect ( inputs . title ( ) ) . toHaveValue ( userList . title )
305+ await user . click ( inputs . title ( ) )
306+ await user . clear ( inputs . title ( ) )
307+ await user . paste ( patch . title )
308+
309+ // Description
310+ expect ( inputs . description ( ) ) . toHaveValue ( userList . description )
311+ await user . click ( inputs . description ( ) )
312+ await user . clear ( inputs . description ( ) )
313+ await user . paste ( patch . description )
314+
315+ // Privacy Level
316+ expect ( inputs . privacy_level ( PrivacyLevelEnum . Private ) . checked ) . toBe ( true )
317+ expect ( inputs . privacy_level ( PrivacyLevelEnum . Unlisted ) . checked ) . toBe ( false )
318+ await user . click ( inputs . privacy_level ( patch . privacy_level ) )
319+
320+ // Submit
321+ const patchUrl = urls . userLists . details ( { id : userList . id } )
322+ setMockResponse . patch ( patchUrl , { ...userList , ...patch } )
323+ await user . click ( inputs . submit ( ) )
324+
325+ expect ( makeRequest ) . toHaveBeenCalledWith (
326+ "patch" ,
327+ patchUrl ,
328+ expect . objectContaining ( { ...patch } ) ,
329+ )
330+ } )
331+
332+ test ( "Displays overall error if form validates but API call fails" , async ( ) => {
333+ allowConsoleErrors ( )
334+ const userList = factories . userLists . userList ( )
335+ await setup ( { userList : userList } )
336+
337+ const patchUrl = urls . userLists . details ( { id : userList . id } )
338+ setMockResponse . patch ( patchUrl , { } , { code : 408 } )
339+ await user . click ( inputs . submit ( ) )
340+
341+ expect ( makeRequest ) . toHaveBeenCalledWith (
342+ "patch" ,
343+ patchUrl ,
344+ expect . anything ( ) ,
345+ )
346+ const alertMessage = await screen . findByRole ( "alert" )
347+
348+ expect ( alertMessage ) . toHaveTextContent (
349+ "There was a problem saving your list." ,
350+ )
351+ } )
352+ } )
353+
354+ describe ( "manageListDialogs.destroyLearningPath" , ( ) => {
215355 const setup = ( ) => {
216356 const resource = factories . learningResources . learningPath ( )
217357 renderWithProviders ( null )
218358 act ( ( ) => {
219- manageLearningPathDialogs . destroy ( resource )
359+ manageListDialogs . destroyLearningPath ( resource )
220360 } )
221361 return { resource }
222362 }
@@ -249,3 +389,42 @@ describe("manageListDialogs.destroy", () => {
249389 await waitForElementToBeRemoved ( dialog )
250390 } )
251391} )
392+
393+ describe ( "manageListDialogs.destroyUserList" , ( ) => {
394+ const setup = ( ) => {
395+ const userList = factories . userLists . userList ( )
396+ renderWithProviders ( null )
397+ act ( ( ) => {
398+ manageListDialogs . destroyUserList ( userList )
399+ } )
400+ return { userList : userList }
401+ }
402+
403+ test ( "Dialog title is 'Delete list'" , async ( ) => {
404+ setup ( )
405+ const dialog = screen . getByRole ( "heading" , { name : "Delete User List" } )
406+ expect ( dialog ) . toBeVisible ( )
407+ } )
408+
409+ test ( "Deleting a $label calls correct API" , async ( ) => {
410+ const { userList } = setup ( )
411+
412+ const dialog = screen . getByRole ( "dialog" )
413+ const url = urls . userLists . details ( { id : userList . id } )
414+ setMockResponse . delete ( url , undefined )
415+ await user . click ( inputs . delete ( ) )
416+
417+ expect ( makeRequest ) . toHaveBeenCalledWith ( "delete" , url , undefined )
418+ await waitForElementToBeRemoved ( dialog )
419+ } )
420+
421+ test ( "Clicking cancel does not delete list" , async ( ) => {
422+ setup ( )
423+
424+ const dialog = screen . getByRole ( "dialog" )
425+ await user . click ( inputs . cancel ( ) )
426+
427+ expect ( makeRequest ) . not . toHaveBeenCalled ( )
428+ await waitForElementToBeRemoved ( dialog )
429+ } )
430+ } )
0 commit comments