1- import { MockComponents } from 'ng-mocks' ;
1+ import { Store } from '@ngxs/store' ;
2+
3+ import { MockComponents , MockProvider } from 'ng-mocks' ;
4+
5+ import { Mock } from 'vitest' ;
26
37import { signal } from '@angular/core' ;
48import { ComponentFixture , TestBed } from '@angular/core/testing' ;
5- import { provideRouter } from '@angular/router' ;
9+ import { Router } from '@angular/router' ;
610
7- import { RegistriesSelectors } from '@osf/features/registries/store' ;
11+ import { CreateSchemaResponse , FetchAllSchemaResponses , RegistriesSelectors } from '@osf/features/registries/store' ;
812import { RegistrationReviewStates } from '@osf/shared/enums/registration-review-states.enum' ;
913import { RevisionReviewStates } from '@osf/shared/enums/revision-review-states.enum' ;
14+ import { UserPermissions } from '@osf/shared/enums/user-permissions.enum' ;
1015import { RegistrationCard } from '@shared/models/registration/registration-card.model' ;
1116
1217import { MOCK_REGISTRATION } from '@testing/mocks/registration.mock' ;
1318import { provideOSFCore } from '@testing/osf.testing.provider' ;
14- import { provideMockStore } from '@testing/providers/store-provider.mock' ;
19+ import { RouterMockBuilder , RouterMockType } from '@testing/providers/router-provider.mock' ;
20+ import {
21+ BaseSetupOverrides ,
22+ mergeSignalOverrides ,
23+ provideMockStore ,
24+ SignalOverride ,
25+ } from '@testing/providers/store-provider.mock' ;
1526
1627import { ContributorsListComponent } from '../contributors-list/contributors-list.component' ;
1728import { DataResourcesComponent } from '../data-resources/data-resources.component' ;
@@ -23,144 +34,189 @@ import { RegistrationCardComponent } from './registration-card.component';
2334describe ( 'RegistrationCardComponent' , ( ) => {
2435 let component : RegistrationCardComponent ;
2536 let fixture : ComponentFixture < RegistrationCardComponent > ;
37+ let store : Store ;
38+ let routerMock : RouterMockType ;
2639
2740 const mockRegistrationData : RegistrationCard = MOCK_REGISTRATION ;
2841
29- beforeEach ( ( ) => {
42+ const defaultSignals : SignalOverride [ ] = [
43+ { selector : RegistriesSelectors . getSchemaResponse , value : signal ( { id : 'revision-id' } ) } ,
44+ ] ;
45+
46+ type SetupOverrides = BaseSetupOverrides & {
47+ registrationData ?: RegistrationCard ;
48+ isDraft ?: boolean ;
49+ } ;
50+
51+ function setup ( overrides : SetupOverrides = { } ) : void {
52+ routerMock = RouterMockBuilder . create ( ) . build ( ) ;
53+
3054 TestBed . configureTestingModule ( {
3155 imports : [
3256 RegistrationCardComponent ,
3357 ...MockComponents ( StatusBadgeComponent , DataResourcesComponent , IconComponent , ContributorsListComponent ) ,
3458 ] ,
3559 providers : [
3660 provideOSFCore ( ) ,
37- provideRouter ( [ ] ) ,
38- provideMockStore ( {
39- signals : [ { selector : RegistriesSelectors . getSchemaResponse , value : signal ( null ) } ] ,
40- } ) ,
61+ MockProvider ( Router , routerMock ) ,
62+ provideMockStore ( { signals : mergeSignalOverrides ( defaultSignals , overrides . selectorOverrides ) } ) ,
4163 ] ,
4264 } ) ;
4365
66+ store = TestBed . inject ( Store ) ;
4467 fixture = TestBed . createComponent ( RegistrationCardComponent ) ;
4568 component = fixture . componentInstance ;
46- } ) ;
69+ fixture . componentRef . setInput ( 'registrationData' , overrides . registrationData ?? mockRegistrationData ) ;
70+ fixture . componentRef . setInput ( 'isDraft' , overrides . isDraft ?? false ) ;
71+ fixture . detectChanges ( ) ;
72+ }
4773
4874 it ( 'should create' , ( ) => {
49- expect ( component ) . toBeTruthy ( ) ;
50- } ) ;
75+ setup ( ) ;
5176
52- it ( 'should have registrationData as required input' , ( ) => {
53- fixture . componentRef . setInput ( 'registrationData' , mockRegistrationData ) ;
54- expect ( component . registrationData ( ) ) . toEqual ( mockRegistrationData ) ;
77+ expect ( component ) . toBeTruthy ( ) ;
5578 } ) ;
5679
57- it ( 'should compute isAccepted correctly when reviewsState is Accepted' , ( ) => {
58- const testData = {
59- ...mockRegistrationData ,
60- reviewsState : RegistrationReviewStates . Accepted ,
61- } ;
62- fixture . componentRef . setInput ( 'registrationData' , testData ) ;
63- fixture . detectChanges ( ) ;
80+ it . each ( [
81+ [ [ UserPermissions . Read , UserPermissions . Write , UserPermissions . Admin ] , true , true ] ,
82+ [ [ UserPermissions . Write ] , false , true ] ,
83+ [ [ UserPermissions . Admin ] , true , false ] ,
84+ ] as [ UserPermissions [ ] , boolean , boolean ] [ ] ) (
85+ 'should identify user permissions' ,
86+ ( currentUserPermissions , hasAdminAccess , hasWriteAccess ) => {
87+ setup ( {
88+ registrationData : {
89+ ...mockRegistrationData ,
90+ currentUserPermissions,
91+ } ,
92+ } ) ;
93+
94+ expect ( component . hasAdminAccess ( ) ) . toBe ( hasAdminAccess ) ;
95+ expect ( component . hasWriteAccess ( ) ) . toBe ( hasWriteAccess ) ;
96+ }
97+ ) ;
98+
99+ it . each ( [
100+ [ RegistrationReviewStates . Accepted , true ] ,
101+ [ RegistrationReviewStates . Pending , true ] ,
102+ [ RegistrationReviewStates . Embargo , true ] ,
103+ [ RegistrationReviewStates . Withdrawn , false ] ,
104+ ] as [ RegistrationReviewStates , boolean ] [ ] ) (
105+ 'should identify update-eligible review states' ,
106+ ( reviewsState , eligible ) => {
107+ setup ( {
108+ registrationData : {
109+ ...mockRegistrationData ,
110+ reviewsState,
111+ } ,
112+ } ) ;
113+
114+ expect ( component . isAccepted ( ) ) . toBe ( reviewsState === RegistrationReviewStates . Accepted ) ;
115+ expect ( component . isPending ( ) ) . toBe ( reviewsState === RegistrationReviewStates . Pending ) ;
116+ expect ( component . isEmbargo ( ) ) . toBe ( reviewsState === RegistrationReviewStates . Embargo ) ;
117+ expect ( component . showButtons ( ) ) . toBe ( eligible ) ;
118+ }
119+ ) ;
120+
121+ it . each ( [
122+ [ RevisionReviewStates . Approved , true , false , false ] ,
123+ [ RevisionReviewStates . Unapproved , false , true , false ] ,
124+ [ RevisionReviewStates . RevisionInProgress , false , false , true ] ,
125+ ] as [ RevisionReviewStates , boolean , boolean , boolean ] [ ] ) (
126+ 'should identify revision states' ,
127+ ( revisionState , isApproved , isUnapproved , isInProgress ) => {
128+ setup ( {
129+ registrationData : {
130+ ...mockRegistrationData ,
131+ revisionState,
132+ } ,
133+ } ) ;
134+
135+ expect ( component . isApproved ( ) ) . toBe ( isApproved ) ;
136+ expect ( component . isUnapproved ( ) ) . toBe ( isUnapproved ) ;
137+ expect ( component . isInProgress ( ) ) . toBe ( isInProgress ) ;
138+ }
139+ ) ;
140+
141+ it . each ( [
142+ [ null , true ] ,
143+ [ mockRegistrationData . id , true ] ,
144+ [ 'different-root-id' , false ] ,
145+ ] as [ string | null , boolean ] [ ] ) ( 'should identify root registrations' , ( rootParentId , isRootRegistration ) => {
146+ setup ( {
147+ registrationData : {
148+ ...mockRegistrationData ,
149+ rootParentId,
150+ } ,
151+ } ) ;
64152
65- expect ( component . isAccepted ) . toBe ( true ) ;
153+ expect ( component . isRootRegistration ( ) ) . toBe ( isRootRegistration ) ;
66154 } ) ;
67155
68- it ( 'should compute isPending correctly when reviewsState is Pending' , ( ) => {
69- const testData = {
70- ...mockRegistrationData ,
71- reviewsState : RegistrationReviewStates . Pending ,
72- } ;
73- fixture . componentRef . setInput ( 'registrationData' , testData ) ;
74- fixture . detectChanges ( ) ;
156+ it . each ( [
157+ [ 'updates are disabled' , { allowUpdates : false } ] ,
158+ [ 'user lacks admin access' , { currentUserPermissions : [ UserPermissions . Write ] } ] ,
159+ [ 'registration is not root' , { rootParentId : 'different-root-id' } ] ,
160+ ] as [ string , Partial < RegistrationCard > ] [ ] ) ( 'should hide update buttons when %s' , ( _label , registrationData ) => {
161+ setup ( {
162+ registrationData : {
163+ ...mockRegistrationData ,
164+ ...registrationData ,
165+ } ,
166+ } ) ;
75167
76- expect ( component . isPending ) . toBe ( true ) ;
168+ expect ( component . showButtons ( ) ) . toBe ( false ) ;
77169 } ) ;
78170
79- it ( 'should compute isApproved correctly when revisionState is Approved' , ( ) => {
80- const testData = {
81- ...mockRegistrationData ,
82- revisionState : RevisionReviewStates . Approved ,
83- } ;
84- fixture . componentRef . setInput ( 'registrationData' , testData ) ;
85- fixture . detectChanges ( ) ;
171+ it ( 'should dispatch create schema response and navigate to justification page on updateRegistration' , ( ) => {
172+ setup ( ) ;
173+ ( store . dispatch as Mock ) . mockClear ( ) ;
86174
87- expect ( component . isApproved ) . toBe ( true ) ;
88- } ) ;
175+ component . updateRegistration ( mockRegistrationData . id ) ;
89176
90- it ( 'should compute isUnapproved correctly when revisionState is Unapproved' , ( ) => {
91- const testData = {
92- ...mockRegistrationData ,
93- revisionState : RevisionReviewStates . Unapproved ,
94- } ;
95- fixture . componentRef . setInput ( 'registrationData' , testData ) ;
96- fixture . detectChanges ( ) ;
97-
98- expect ( component . isUnapproved ) . toBe ( true ) ;
177+ expect ( store . dispatch ) . toHaveBeenCalledWith ( new CreateSchemaResponse ( mockRegistrationData . id ) ) ;
178+ expect ( routerMock . navigate ) . toHaveBeenCalledWith ( [ '/registries/revisions/revision-id/justification' ] ) ;
99179 } ) ;
100180
101- it ( 'should compute isInProgress correctly when revisionState is RevisionInProgress' , ( ) => {
102- const testData = {
103- ...mockRegistrationData ,
104- revisionState : RevisionReviewStates . RevisionInProgress ,
105- } ;
106- fixture . componentRef . setInput ( 'registrationData' , testData ) ;
107- fixture . detectChanges ( ) ;
108-
109- expect ( component . isInProgress ) . toBe ( true ) ;
110- } ) ;
181+ it ( 'should dispatch fetch schema responses and navigate to review page for unapproved revision' , ( ) => {
182+ setup ( {
183+ registrationData : {
184+ ...mockRegistrationData ,
185+ revisionState : RevisionReviewStates . Unapproved ,
186+ } ,
187+ } ) ;
188+ ( store . dispatch as Mock ) . mockClear ( ) ;
111189
112- it ( 'should compute isAccepted as false when reviewsState is not Accepted' , ( ) => {
113- const testData = {
114- ...mockRegistrationData ,
115- reviewsState : RegistrationReviewStates . Pending ,
116- } ;
117- fixture . componentRef . setInput ( 'registrationData' , testData ) ;
118- fixture . detectChanges ( ) ;
190+ component . continueUpdateRegistration ( mockRegistrationData . id ) ;
119191
120- expect ( component . isAccepted ) . toBe ( false ) ;
192+ expect ( store . dispatch ) . toHaveBeenCalledWith ( new FetchAllSchemaResponses ( mockRegistrationData . id ) ) ;
193+ expect ( routerMock . navigate ) . toHaveBeenCalledWith ( [ '/registries/revisions/revision-id/review' ] ) ;
121194 } ) ;
122195
123- it ( 'should compute isPending as false when reviewsState is not Pending' , ( ) => {
124- const testData = {
125- ...mockRegistrationData ,
126- reviewsState : RegistrationReviewStates . Accepted ,
127- } ;
128- fixture . componentRef . setInput ( 'registrationData' , testData ) ;
129- fixture . detectChanges ( ) ;
130-
131- expect ( component . isPending ) . toBe ( false ) ;
132- } ) ;
196+ it ( 'should dispatch fetch schema responses and navigate to justification page for non-unapproved revision' , ( ) => {
197+ setup ( {
198+ registrationData : {
199+ ...mockRegistrationData ,
200+ revisionState : RevisionReviewStates . RevisionInProgress ,
201+ } ,
202+ } ) ;
203+ ( store . dispatch as Mock ) . mockClear ( ) ;
133204
134- it ( 'should compute isApproved as false when revisionState is not Approved' , ( ) => {
135- const testData = {
136- ...mockRegistrationData ,
137- revisionState : RevisionReviewStates . Unapproved ,
138- } ;
139- fixture . componentRef . setInput ( 'registrationData' , testData ) ;
140- fixture . detectChanges ( ) ;
205+ component . continueUpdateRegistration ( mockRegistrationData . id ) ;
141206
142- expect ( component . isApproved ) . toBe ( false ) ;
207+ expect ( store . dispatch ) . toHaveBeenCalledWith ( new FetchAllSchemaResponses ( mockRegistrationData . id ) ) ;
208+ expect ( routerMock . navigate ) . toHaveBeenCalledWith ( [ '/registries/revisions/revision-id/justification' ] ) ;
143209 } ) ;
144210
145- it ( 'should compute isUnapproved as false when revisionState is not Unapproved' , ( ) => {
146- const testData = {
147- ...mockRegistrationData ,
148- revisionState : RevisionReviewStates . Approved ,
149- } ;
150- fixture . componentRef . setInput ( 'registrationData' , testData ) ;
151- fixture . detectChanges ( ) ;
152-
153- expect ( component . isUnapproved ) . toBe ( false ) ;
154- } ) ;
211+ it ( 'should not navigate when schema response is not present' , ( ) => {
212+ setup ( {
213+ selectorOverrides : [ { selector : RegistriesSelectors . getSchemaResponse , value : signal ( null ) } ] ,
214+ } ) ;
215+ ( store . dispatch as Mock ) . mockClear ( ) ;
155216
156- it ( 'should compute isInProgress as false when revisionState is not RevisionInProgress' , ( ) => {
157- const testData = {
158- ...mockRegistrationData ,
159- revisionState : RevisionReviewStates . Approved ,
160- } ;
161- fixture . componentRef . setInput ( 'registrationData' , testData ) ;
162- fixture . detectChanges ( ) ;
217+ component . updateRegistration ( mockRegistrationData . id ) ;
163218
164- expect ( component . isInProgress ) . toBe ( false ) ;
219+ expect ( store . dispatch ) . toHaveBeenCalledWith ( new CreateSchemaResponse ( mockRegistrationData . id ) ) ;
220+ expect ( routerMock . navigate ) . not . toHaveBeenCalled ( ) ;
165221 } ) ;
166222} ) ;
0 commit comments