diff --git a/package-lock.json b/package-lock.json index 356e26aa3..2569c69ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@dnd-kit/sortable": "8.0.0", "@dnd-kit/utilities": "3.2.2", "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.0.0-alpha.42", + "@iqss/dataverse-client-javascript": "2.0.0-alpha.44", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", @@ -3560,9 +3560,9 @@ }, "node_modules/@iqss/dataverse-client-javascript": { "name": "@IQSS/dataverse-client-javascript", - "version": "2.0.0-alpha.42", - "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.0.0-alpha.42/9bb5781e9a747fbe2e07dc74e8550b42c0efb9e9", - "integrity": "sha512-qx2hLDTKx/7vnj7jkNpzguXaNzcmmtDvoQVJaCewrw1+ogH2G+pv9S40dDTJK7Wf8Zuxz6uxsUvA9O9WcurdNQ==", + "version": "2.0.0-alpha.44", + "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.0.0-alpha.44/17eec76da851b0b204674c74c66937b588425b8f", + "integrity": "sha512-zEcqwFbqB1rJQAoRzthURdnlnfGMfoctVGt1RPfEBDVWHC6esM9OeS210v2C+ASMJ0IrK4e+9d7775ZpaMrZRA==", "license": "MIT", "dependencies": { "@types/node": "^18.15.11", diff --git a/package.json b/package.json index 9307b8b98..42bc456ae 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@dnd-kit/sortable": "8.0.0", "@dnd-kit/utilities": "3.2.2", "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.0.0-alpha.42", + "@iqss/dataverse-client-javascript": "2.0.0-alpha.44", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", diff --git a/public/locales/en/signUp.json b/public/locales/en/signUp.json index e38cc425a..7857c3a23 100644 --- a/public/locales/en/signUp.json +++ b/public/locales/en/signUp.json @@ -64,8 +64,7 @@ "primaryLabel": "General Terms of Use", "label": "I have read and accept the Dataverse General Terms of Use as outlined above.", "description": "The terms and conditions for using the application and services.", - "required": "Please check the box to indicate your acceptance of the General Terms of Use.", - "noTerms": "There are no Terms of Use for this Dataverse installation." + "required": "Please check the box to indicate your acceptance of the General Terms of Use." } }, "submit": "Create Account", diff --git a/src/info/infrastructure/repositories/DataverseInfoJSDataverseRepository.ts b/src/info/infrastructure/repositories/DataverseInfoJSDataverseRepository.ts index 8fdc8adbe..83f1a3760 100644 --- a/src/info/infrastructure/repositories/DataverseInfoJSDataverseRepository.ts +++ b/src/info/infrastructure/repositories/DataverseInfoJSDataverseRepository.ts @@ -1,5 +1,8 @@ -import { getDataverseVersion, ReadError } from '@iqss/dataverse-client-javascript' -import { axiosInstance } from '@/axiosInstance' +import { + getApplicationTermsOfUse, + getDataverseVersion, + ReadError +} from '@iqss/dataverse-client-javascript' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' import { DataverseVersion } from '@/info/domain/models/DataverseVersion' import { TermsOfUse } from '@/info/domain/models/TermsOfUse' @@ -30,12 +33,9 @@ export class DataverseInfoJSDataverseRepository implements DataverseInfoReposito }) } - async getTermsOfUse() { - //TODO - This is not actually used and should be replaced with a js-dataverse use case when we have available the endpoint to get the installation terms of use not api terms of use. - const response = await axiosInstance.get<{ data: { message: TermsOfUse } }>( - '/api/v1/info/apiTermsOfUse', - { excludeToken: true } - ) - return JSTermsOfUseMapper.toSanitizedTermsOfUse(response.data.data.message) + getTermsOfUse(): Promise { + return getApplicationTermsOfUse + .execute() + .then((termsOfUse) => JSTermsOfUseMapper.toSanitizedTermsOfUse(termsOfUse)) } } diff --git a/src/sections/sign-up/SignUp.tsx b/src/sections/sign-up/SignUp.tsx index e70acff47..342f3989c 100644 --- a/src/sections/sign-up/SignUp.tsx +++ b/src/sections/sign-up/SignUp.tsx @@ -2,16 +2,22 @@ import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { Alert, Tabs } from '@iqss/dataverse-design-system' import { UserRepository } from '@/users/domain/repositories/UserRepository' +import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' import { useLoading } from '../loading/LoadingContext' import { ValidTokenNotLinkedAccountForm } from './valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm' import styles from './SignUp.module.scss' interface SignUpProps { userRepository: UserRepository + dataverseInfoRepository: DataverseInfoRepository hasValidTokenButNotLinkedAccount: boolean } -export const SignUp = ({ userRepository, hasValidTokenButNotLinkedAccount }: SignUpProps) => { +export const SignUp = ({ + userRepository, + dataverseInfoRepository, + hasValidTokenButNotLinkedAccount +}: SignUpProps) => { const { t } = useTranslation('signUp') const { setIsLoading } = useLoading() @@ -58,7 +64,10 @@ export const SignUp = ({ userRepository, hasValidTokenButNotLinkedAccount }: Sig
{hasValidTokenButNotLinkedAccount && ( - + )}
diff --git a/src/sections/sign-up/SignUpFactory.tsx b/src/sections/sign-up/SignUpFactory.tsx index 50f6a4e2c..9fa9879c3 100644 --- a/src/sections/sign-up/SignUpFactory.tsx +++ b/src/sections/sign-up/SignUpFactory.tsx @@ -2,10 +2,11 @@ import { ReactElement } from 'react' import { useSearchParams } from 'react-router-dom' import { SignUp } from './SignUp' import { QueryParamKey } from '../Route.enum' - import { UserJSDataverseRepository } from '@/users/infrastructure/repositories/UserJSDataverseRepository' +import { DataverseInfoJSDataverseRepository } from '@/info/infrastructure/repositories/DataverseInfoJSDataverseRepository' const userRepository = new UserJSDataverseRepository() +const dataverseInfoRepository = new DataverseInfoJSDataverseRepository() export class SignUpFactory { static create(): ReactElement { @@ -22,6 +23,7 @@ function SignUpWithSearchParams() { return ( ) diff --git a/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.module.scss b/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.module.scss index 54cb7a61b..4ed00fdb1 100644 --- a/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.module.scss +++ b/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.module.scss @@ -19,10 +19,12 @@ } .terms-of-use-wrapper { - max-height: 200px; + height: 100px; + min-height: 100px; padding: 12px; overflow-y: auto; background-color: #f5f5f5; border: solid 1px $dv-border-color; border-radius: 6px; + resize: vertical; } diff --git a/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx b/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx index 0341aea4c..84a3b22f7 100644 --- a/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx +++ b/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx @@ -318,7 +318,7 @@ export const FormFields = ({ userRepository, formDefaultValues, termsOfUse }: Fo
diff --git a/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx b/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx index 90e2d9680..960047ada 100644 --- a/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx +++ b/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx @@ -1,24 +1,30 @@ import { useContext } from 'react' import { AuthContext } from 'react-oauth2-code-pkce' +import { Alert } from '@iqss/dataverse-design-system' import { UserRepository } from '@/users/domain/repositories/UserRepository' -// import { useGetTermsOfUse } from '@/shared/hooks/useGetTermsOfUse' +import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' +import { useGetTermsOfUse } from '@/shared/hooks/useGetTermsOfUse' import { OIDC_STANDARD_CLAIMS, type ValidTokenNotLinkedAccountFormData } from './types' import { ValidTokenNotLinkedAccountFormHelper } from './ValidTokenNotLinkedAccountFormHelper' import { FormFields } from './FormFields' -// import { FormFieldsSkeleton } from './FormFieldsSkeleton' +import { FormFieldsSkeleton } from './FormFieldsSkeleton' interface ValidTokenNotLinkedAccountFormProps { userRepository: UserRepository + dataverseInfoRepository: DataverseInfoRepository } export const ValidTokenNotLinkedAccountForm = ({ - userRepository + userRepository, + dataverseInfoRepository }: ValidTokenNotLinkedAccountFormProps) => { const { tokenData } = useContext(AuthContext) - // TODO - Use actual terms of use when available in API 👇 - // const { termsOfUse, isLoading: isLoadingTermsOfUse } = - // useGetTermsOfUse(dataverseInfoRepository) + const { + termsOfUse, + isLoading: isLoadingTermsOfUse, + error: errorTermsOfUse + } = useGetTermsOfUse(dataverseInfoRepository) const defaultUserName = ValidTokenNotLinkedAccountFormHelper.getTokenDataValue( @@ -58,15 +64,19 @@ export const ValidTokenNotLinkedAccountForm = ({ termsAccepted: false } - // if (isLoadingTermsOfUse) { - // return - // } + if (isLoadingTermsOfUse) { + return + } + + if (errorTermsOfUse) { + return {errorTermsOfUse} + } return ( ) } diff --git a/src/sections/sign-up/valid-token-not-linked-account-form/useSubmitUser.ts b/src/sections/sign-up/valid-token-not-linked-account-form/useSubmitUser.ts index 9f57642a9..20cae4aa9 100644 --- a/src/sections/sign-up/valid-token-not-linked-account-form/useSubmitUser.ts +++ b/src/sections/sign-up/valid-token-not-linked-account-form/useSubmitUser.ts @@ -69,7 +69,8 @@ export const useSubmitUser = ( }) .catch((err: WriteError) => { const error = new JSDataverseWriteErrorHandler(err) - const formattedError = error.getReasonWithoutStatusCode() ?? error.getErrorMessage() + const formattedError = + error.getReasonWithoutStatusCode() ?? /* istanbul ignore next */ error.getErrorMessage() setSubmitError(formattedError) setSubmissionStatus(SubmissionStatus.Errored) diff --git a/src/shared/core/domain/models/PublicationStatus.ts b/src/shared/core/domain/models/PublicationStatus.ts index a56386f35..d2266979b 100644 --- a/src/shared/core/domain/models/PublicationStatus.ts +++ b/src/shared/core/domain/models/PublicationStatus.ts @@ -1,5 +1,7 @@ export enum PublicationStatus { Published = 'Published', Unpublished = 'Unpublished', - Draft = 'Draft' + Draft = 'Draft', + Deaccessioned = 'Deaccessioned', + InReview = 'In Review' } diff --git a/src/shared/hooks/useGetTermsOfUse.ts b/src/shared/hooks/useGetTermsOfUse.ts index fd4b5a46e..750fa5e6d 100644 --- a/src/shared/hooks/useGetTermsOfUse.ts +++ b/src/shared/hooks/useGetTermsOfUse.ts @@ -1,5 +1,7 @@ import { useEffect, useState } from 'react' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' +import { ReadError } from '@iqss/dataverse-client-javascript' +import { JSDataverseReadErrorHandler } from '../helpers/JSDataverseReadErrorHandler' interface UseGetTermsOfUseReturnType { termsOfUse: string @@ -22,11 +24,15 @@ export const useGetTermsOfUse = ( setTermsOfUse(termsOfUse) } catch (err) { - const errorMessage = - err instanceof Error && err.message - ? err.message - : 'Something went wrong getting the use of terms. Try again later.' - setError(errorMessage) + if (err instanceof ReadError) { + const error = new JSDataverseReadErrorHandler(err) + const formattedError = + error.getReasonWithoutStatusCode() ?? /* istanbul ignore next */ error.getErrorMessage() + + setError(formattedError) + } else { + setError('Something went wrong getting the use of terms. Try again later.') + } } finally { setIsLoading(false) } diff --git a/src/stories/shared-mock-repositories/info/DataverseInfoMockErrorRepository.ts b/src/stories/shared-mock-repositories/info/DataverseInfoMockErrorRepository.ts new file mode 100644 index 000000000..c9c56ce31 --- /dev/null +++ b/src/stories/shared-mock-repositories/info/DataverseInfoMockErrorRepository.ts @@ -0,0 +1,22 @@ +import { DataverseVersion } from '@/info/domain/models/DataverseVersion' +import { TermsOfUse } from '@/info/domain/models/TermsOfUse' +import { FakerHelper } from '@tests/component/shared/FakerHelper' +import { DataverseInfoMockRepository } from './DataverseInfoMockRepository' + +export class DataverseInfoMockErrorRepository implements DataverseInfoMockRepository { + getVersion(): Promise { + return new Promise((_resolve, reject) => { + setTimeout(() => { + reject() + }, FakerHelper.loadingTimout()) + }) + } + + getTermsOfUse(): Promise { + return new Promise((_resolve, reject) => { + setTimeout(() => { + reject() + }, FakerHelper.loadingTimout()) + }) + } +} diff --git a/src/stories/sign-up/SignUp.stories.tsx b/src/stories/sign-up/SignUp.stories.tsx index fce140863..b81684631 100644 --- a/src/stories/sign-up/SignUp.stories.tsx +++ b/src/stories/sign-up/SignUp.stories.tsx @@ -4,6 +4,9 @@ import { WithI18next } from '../WithI18next' import { SignUp } from '@/sections/sign-up/SignUp' import { WithOIDCAuthContext } from '../WithOIDCAuthContext' import { UserMockRepository } from '../shared-mock-repositories/user/UserMockRepository' +import { DataverseInfoMockRepository } from '../shared-mock-repositories/info/DataverseInfoMockRepository' +import { DataverseInfoMockLoadingRepository } from '../shared-mock-repositories/info/DataverseInfoMockLoadingkRepository' +import { DataverseInfoMockErrorRepository } from '../shared-mock-repositories/info/DataverseInfoMockErrorRepository' const meta: Meta = { title: 'Pages/Sign Up', @@ -19,6 +22,30 @@ type Story = StoryObj export const ValidTokenWithNotLinkedAccount: Story = { render: () => ( - + + ) +} + +export const LoadingTermsOfUse: Story = { + render: () => ( + + ) +} + +export const FailedToFetchTermsOfUse: Story = { + render: () => ( + ) } diff --git a/tests/component/info/models/TermsOfUseMother.ts b/tests/component/info/models/TermsOfUseMother.ts index 9db4628ee..f00799569 100644 --- a/tests/component/info/models/TermsOfUseMother.ts +++ b/tests/component/info/models/TermsOfUseMother.ts @@ -2,6 +2,10 @@ import { TermsOfUse } from '@/info/domain/models/TermsOfUse' export class TermsOfUseMother { static create(): TermsOfUse { + return 'Be nice to each other!' + } + + static createWithOnClickScript(): TermsOfUse { return '

Terms of Use SPA dev

Please see our full terms of use

Thanks for reading!

' } diff --git a/tests/component/sections/sign-up/SignUp.spec.tsx b/tests/component/sections/sign-up/SignUp.spec.tsx index 8546bd6a2..f359d17f8 100644 --- a/tests/component/sections/sign-up/SignUp.spec.tsx +++ b/tests/component/sections/sign-up/SignUp.spec.tsx @@ -26,7 +26,11 @@ describe('SignUp', () => { error: null, login: () => {} // 👈 deprecated }}> - + ) @@ -52,7 +56,11 @@ describe('SignUp', () => { error: null, login: () => {} // 👈 deprecated }}> - + ) diff --git a/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx b/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx index 099918a0f..b28c40a34 100644 --- a/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx +++ b/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx @@ -1,18 +1,18 @@ import { AuthContext } from 'react-oauth2-code-pkce' +import { ReadError } from '@iqss/dataverse-client-javascript' import { UserRepository } from '@/users/domain/repositories/UserRepository' +import { UserDTO } from '@/users/domain/useCases/DTOs/UserDTO' +import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' +import { JSTermsOfUseMapper } from '@/info/infrastructure/mappers/JSTermsOfUseMapper' import { ValidTokenNotLinkedAccountForm } from '@/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm' import { AuthContextMother } from '@tests/component/auth/AuthContextMother' -import { UserDTO } from '@/users/domain/useCases/DTOs/UserDTO' -// import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' -// import { TermsOfUseMother } from '@tests/component/info/models/TermsOfUseMother' -// import { JSTermsOfUseMapper } from '@/info/infrastructure/mappers/JSTermsOfUseMapper' +import { TermsOfUseMother } from '@tests/component/info/models/TermsOfUseMother' -// const dataverseInfoRepository: DataverseInfoRepository = {} as DataverseInfoRepository const userRepository: UserRepository = {} as UserRepository +const dataverseInfoRepository: DataverseInfoRepository = {} as DataverseInfoRepository -// TODO - Uncomment when application terms of use are available in API -// const termsOfUseMock = TermsOfUseMother.create() -// const sanitizedTermsOfUseMock = JSTermsOfUseMapper.toSanitizedTermsOfUse(termsOfUseMock) +const termsOfUseMock = TermsOfUseMother.createWithOnClickScript() +const sanitizedTermsOfUseMock = JSTermsOfUseMapper.toSanitizedTermsOfUse(termsOfUseMock) const mockUserName = 'mockUserName' const mockFirstName = 'mockFirstName' @@ -23,8 +23,7 @@ describe('ValidTokenNotLinkedAccountForm', () => { beforeEach(() => { cy.viewport(1280, 720) - // dataverseInfoRepository.getTermsOfUse = cy.stub().resolves(sanitizedTermsOfUseMock) - // dataverseInfoRepository.getTermsOfUse = cy.stub().resolves('') + dataverseInfoRepository.getTermsOfUse = cy.stub().resolves(sanitizedTermsOfUseMock) userRepository.register = cy.stub().as('registerUser').resolves() }) @@ -48,7 +47,10 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - + ) @@ -73,7 +75,10 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - + ) @@ -110,7 +115,10 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - + ) @@ -148,7 +156,10 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - + ) @@ -235,7 +246,10 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - + ) @@ -243,4 +257,109 @@ describe('ValidTokenNotLinkedAccountForm', () => { cy.get('@logOut').should('have.been.called') }) + + it('shows loading skeleton when loading terms of use', () => { + dataverseInfoRepository.getTermsOfUse = cy.stub().resolves(new Promise(() => {})) + + cy.customMount( + {}, + logOut: () => {}, + loginInProgress: false, + tokenData: AuthContextMother.createTokenData(), + idTokenData: AuthContextMother.createTokenData(), + error: null, + login: () => {} // 👈 deprecated + }}> + + + ) + + cy.findByTestId('form-fields-skeleton').should('exist') + }) + + it('shows error message alert when there is an error loading terms of use', () => { + dataverseInfoRepository.getTermsOfUse = cy + .stub() + .rejects(new ReadError('Error loading terms of use')) + + cy.customMount( + {}, + logOut: () => {}, + loginInProgress: false, + tokenData: AuthContextMother.createTokenData(), + idTokenData: AuthContextMother.createTokenData(), + error: null, + login: () => {} // 👈 deprecated + }}> + + + ) + + cy.findByText(/Error loading terms of use/).should('exist') + }) + + it('shows error message alert when there is an error registering the user', () => { + userRepository.register = cy + .stub() + .as('registerUser') + .rejects(new ReadError('Error registering user')) + + cy.customMount( + {}, + logOut: () => {}, + loginInProgress: false, + tokenData: AuthContextMother.createTokenData({ + preferred_username: mockUserName, + given_name: mockFirstName, + family_name: mockLastName, + email: mockEmail + }), + idTokenData: AuthContextMother.createTokenData({ + preferred_username: mockUserName, + given_name: mockFirstName, + family_name: mockLastName, + email: mockEmail + }), + error: null, + login: () => {} // 👈 deprecated + }}> + + + ) + + cy.wait(300) + + cy.findByTestId('termsAcceptedCheckbox').check({ force: true }) + + cy.wait(300) + + cy.findByRole('button', { name: 'Create Account' }).should('not.be.disabled') + + cy.findByRole('button', { name: 'Create Account' }).click() + + cy.findByText(/Error registering user/) + .should('exist') + .should('be.visible') + }) }) diff --git a/tests/component/shared/hooks/useGetTermsOfUse.spec.ts b/tests/component/shared/hooks/useGetTermsOfUse.spec.ts index d6acd8aa6..7defa18ad 100644 --- a/tests/component/shared/hooks/useGetTermsOfUse.spec.ts +++ b/tests/component/shared/hooks/useGetTermsOfUse.spec.ts @@ -2,6 +2,7 @@ import { act, renderHook } from '@testing-library/react' import { useGetTermsOfUse } from '@/shared/hooks/useGetTermsOfUse' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' import { TermsOfUseMother } from '@tests/component/info/models/TermsOfUseMother' +import { ReadError } from '@iqss/dataverse-client-javascript' const dataverseInfoRepository: DataverseInfoRepository = {} as DataverseInfoRepository const termsOfUseMock = TermsOfUseMother.create() @@ -25,8 +26,8 @@ describe('useGetTermsOfUse', () => { }) describe('Error handling', () => { - it('should return correct error message when there is an error type catched', async () => { - dataverseInfoRepository.getTermsOfUse = cy.stub().rejects(new Error('Error message')) + it('should return correct error message when it is a ReadError instance from js-dataverse', async () => { + dataverseInfoRepository.getTermsOfUse = cy.stub().rejects(new ReadError('Error message')) const { result } = renderHook(() => useGetTermsOfUse(dataverseInfoRepository)) @@ -41,7 +42,7 @@ describe('useGetTermsOfUse', () => { }) }) - it('should return correct error message when there is not an error type catched', async () => { + it('should return correct default error message when it is not a ReadError instance from js-dataverse', async () => { dataverseInfoRepository.getTermsOfUse = cy.stub().rejects('Error message') const { result } = renderHook(() => useGetTermsOfUse(dataverseInfoRepository))