diff --git a/components/Grid/Item.test.tsx b/components/Grid/Item.test.tsx index ebe6bd41..cddc838d 100644 --- a/components/Grid/Item.test.tsx +++ b/components/Grid/Item.test.tsx @@ -1,6 +1,9 @@ import { render, screen } from "@/test-utils"; import GridItem from "./Item"; +import React from "react"; import { SearchShape } from "@/types/api/response"; +import { UserContext } from "@/context/user-context"; +import { UserContext as UserContextType } from "@/types/context/user"; const mockItem = { api_model: "Work", @@ -21,6 +24,28 @@ const mockItem = { work_type: "Image", }; +const defaultUserContext: UserContextType = { + user: { + isInstitution: false, + isLoggedIn: false, + isReadingRoom: false, + scopes: ["read:Public", "read:Published"], + }, + isLoading: false, + isSignInModalOpen: false, + openSignInModal: jest.fn(), + closeSignInModal: jest.fn(), +}; + +const withUserProvider = ( + Component: React.ReactNode, + userContext: UserContextType = defaultUserContext, +) => { + return ( + {Component} + ); +}; + describe("GridItem component", () => { it("renders the item, link and image", () => { render(); @@ -50,4 +75,51 @@ describe("GridItem component", () => { "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/b92874a0-72b7-4479-979e-38860c412a13/square/512,/0/default.jpg", ); }); + + it("does not render a lock for public items when auth finishes without a user", () => { + render( + withUserProvider(, { + ...defaultUserContext, + user: null, + }), + ); + + expect(screen.queryByTitle("Restricted Item")).not.toBeInTheDocument(); + }); + + it("renders a lock for non-public items while auth is loading", () => { + const institutionItem = { + ...mockItem, + visibility: "Institution", + }; + + render( + withUserProvider(, { + ...defaultUserContext, + isLoading: true, + user: null, + }), + ); + + expect(screen.getByTitle("Restricted Item")).toBeInTheDocument(); + }); + + it("does not render a lock for non-public items when the user has the matching scope", () => { + const institutionItem = { + ...mockItem, + visibility: "Institution", + }; + + render( + withUserProvider(, { + ...defaultUserContext, + user: { + ...defaultUserContext.user!, + scopes: ["read:Public", "read:Published", "read:Institution"], + }, + }), + ); + + expect(screen.queryByTitle("Restricted Item")).not.toBeInTheDocument(); + }); }); diff --git a/components/Grid/Item.tsx b/components/Grid/Item.tsx index cf12f1b2..14090d2d 100644 --- a/components/Grid/Item.tsx +++ b/components/Grid/Item.tsx @@ -16,7 +16,9 @@ const GridItem: React.FC = ({ item, isFeatured }) => { const userContext = useContext(UserContext); const isRestricted = (item: SearchShape): boolean => { - if (userContext?.isLoading) return item?.visibility !== "Public"; + if (item?.visibility === "Public") return false; + if (userContext?.isLoading) return true; + return !( userContext?.user?.scopes?.includes(`read:${item?.visibility}`) ?? false ); diff --git a/components/Search/TranscriptionResults.test.tsx b/components/Search/TranscriptionResults.test.tsx new file mode 100644 index 00000000..a8305b40 --- /dev/null +++ b/components/Search/TranscriptionResults.test.tsx @@ -0,0 +1,136 @@ +import { render, screen } from "@/test-utils"; +import React from "react"; +import TranscriptionResults from "@/components/Search/TranscriptionResults"; +import { FileSetSearchResult, Pagination } from "@/types/api/response"; +import { UserContext } from "@/context/user-context"; +import { UserContext as UserContextType } from "@/types/context/user"; + +jest.mock("@/lib/constants/endpoints", () => ({ + DCAPI_ENDPOINT: "https://dcapi.rdc-staging.library.northwestern.edu/api/v2", +})); + +const defaultUserContext: UserContextType = { + user: { + isInstitution: false, + isLoggedIn: false, + isReadingRoom: false, + scopes: ["read:Public", "read:Published"], + }, + isLoading: false, + isSignInModalOpen: false, + openSignInModal: jest.fn(), + closeSignInModal: jest.fn(), +}; + +const pagination: Pagination = { + query_url: + "https://dcapi.rdc-staging.library.northwestern.edu/api/v2/search/file-sets?searchToken=N4IgRg9gJgniBcoDOBLAXgUwQFgAwBoQB9JCAVwC&page=1", + current_page: 1, + limit: 10, + offset: 0, + total_hits: 1, + total_pages: 1, + collapsed_by: { + field: "work_id", + total_hits: 1, + }, +}; + +const publicResult: FileSetSearchResult = { + id: "file-set-public", + label: "Page 1", + work_id: "work-public", + work_title: "Public Work", + collection: { id: "collection-1", title: "Collection" }, + rank: 1, + annotations: [ + { + id: "annotation-1", + type: "transcription", + content: "This transcript contains a public match.", + language: ["eng"], + model: "Transcription", + }, + ], + representative_image_url: + "https://iiif.stack.rdc-staging.library.northwestern.edu/iiif/2/file-set-public", + accession_number: "abc", + visibility: "Public", +}; + +const withUserProvider = ( + Component: React.ReactNode, + userContext: UserContextType = defaultUserContext, +) => { + return ( + {Component} + ); +}; + +const renderTranscriptionResults = ( + results: FileSetSearchResult[], + userContext: UserContextType = defaultUserContext, +) => { + return render( + withUserProvider( + , + userContext, + ), + ); +}; + +describe("TranscriptionResults component", () => { + it("does not render locks for public file sets when auth finishes without a user", () => { + renderTranscriptionResults([publicResult], { + ...defaultUserContext, + user: null, + }); + + expect(screen.queryByTitle("Restricted Item")).not.toBeInTheDocument(); + }); + + it("renders locks for non-public file sets while auth is loading", () => { + renderTranscriptionResults( + [ + { + ...publicResult, + id: "file-set-institution", + visibility: "Institution", + }, + ], + { + ...defaultUserContext, + isLoading: true, + user: null, + }, + ); + + expect(screen.getAllByTitle("Restricted Item")).toHaveLength(2); + }); + + it("does not render locks for non-public file sets when the user has the matching scope", () => { + renderTranscriptionResults( + [ + { + ...publicResult, + id: "file-set-institution", + visibility: "Institution", + }, + ], + { + ...defaultUserContext, + user: { + ...defaultUserContext.user!, + scopes: ["read:Public", "read:Published", "read:Institution"], + }, + }, + ); + + expect(screen.queryByTitle("Restricted Item")).not.toBeInTheDocument(); + }); +}); diff --git a/components/Search/TranscriptionResults.tsx b/components/Search/TranscriptionResults.tsx index 5df61160..299ea1d2 100644 --- a/components/Search/TranscriptionResults.tsx +++ b/components/Search/TranscriptionResults.tsx @@ -81,7 +81,9 @@ const TranscriptionResults: React.FC = ({ const groups = groupByWork(results); const { user, isLoading } = useContext(UserContext); const isRestricted = (visibility: string) => { - if (isLoading) return visibility !== "Public"; + if (visibility === "Public") return false; + if (isLoading) return true; + return !(user?.scopes?.includes(`read:${visibility}`) ?? false); };