1+ import { Store } from '@ngxs/store' ;
2+
13import { MockComponents , MockProvider } from 'ng-mocks' ;
24
35import { of } from 'rxjs' ;
@@ -7,7 +9,6 @@ import { ActivatedRoute, Router } from '@angular/router';
79
810import { StepperComponent } from '@osf/shared/components/stepper/stepper.component' ;
911import { IS_WEB } from '@osf/shared/helpers/breakpoints.tokens' ;
10- import { StepOption } from '@osf/shared/models/step-option.model' ;
1112import { BrandService } from '@osf/shared/services/brand.service' ;
1213import { BrowserTabService } from '@osf/shared/services/browser-tab.service' ;
1314import { HeaderStyleService } from '@osf/shared/services/header-style.service' ;
@@ -16,181 +17,180 @@ import { FileStepComponent, ReviewStepComponent } from '../../components';
1617import { createNewVersionStepsConst } from '../../constants' ;
1718import { PreprintSteps } from '../../enums' ;
1819import { PreprintProviderDetails } from '../../models' ;
19- import { PreprintProvidersSelectors } from '../../store/preprint-providers' ;
20- import { PreprintStepperSelectors } from '../../store/preprint-stepper' ;
20+ import { GetPreprintProviderById , PreprintProvidersSelectors } from '../../store/preprint-providers' ;
21+ import { FetchPreprintById , PreprintStepperSelectors , ResetPreprintStepperState } from '../../store/preprint-stepper' ;
2122
2223import { CreateNewVersionComponent } from './create-new-version.component' ;
2324
24- import { PREPRINT_MOCK } from '@testing/mocks/preprint.mock' ;
2525import { PREPRINT_PROVIDER_DETAILS_MOCK } from '@testing/mocks/preprint-provider-details' ;
26- import { TranslationServiceMock } from '@testing/mocks/translation.service.mock' ;
27- import { OSFTestingModule } from '@testing/osf.testing.module' ;
26+ import { provideOSFCore } from '@testing/osf.testing.provider' ;
27+ import { BrandServiceMock , BrandServiceMockType } from '@testing/providers/brand-service.mock' ;
28+ import { BrowserTabServiceMock , BrowserTabServiceMockType } from '@testing/providers/browser-tab-service.mock' ;
29+ import { HeaderStyleServiceMock , HeaderStyleServiceMockType } from '@testing/providers/header-style-service.mock' ;
2830import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock' ;
29- import { RouterMockBuilder } from '@testing/providers/router-provider.mock' ;
30- import { provideMockStore } from '@testing/providers/store-provider.mock' ;
31+ import { RouterMockBuilder , RouterMockType } from '@testing/providers/router-provider.mock' ;
32+ import { mergeSignalOverrides , provideMockStore , SignalOverride } from '@testing/providers/store-provider.mock' ;
3133
3234describe ( 'CreateNewVersionComponent' , ( ) => {
3335 let component : CreateNewVersionComponent ;
3436 let fixture : ComponentFixture < CreateNewVersionComponent > ;
35- let routerMock : ReturnType < RouterMockBuilder [ 'build' ] > ;
36- let routeMock : ReturnType < ActivatedRouteMockBuilder [ 'build' ] > ;
37+ let store : Store ;
38+ let routerMock : RouterMockType ;
39+ let brandServiceMock : BrandServiceMockType ;
40+ let headerStyleMock : HeaderStyleServiceMockType ;
41+ let browserTabMock : BrowserTabServiceMockType ;
3742
3843 const mockProvider : PreprintProviderDetails = PREPRINT_PROVIDER_DETAILS_MOCK ;
39- const mockPreprint = PREPRINT_MOCK ;
4044 const mockProviderId = 'osf' ;
4145 const mockPreprintId = 'test_preprint_123' ;
4246
43- beforeEach ( async ( ) => {
47+ const defaultSignals : SignalOverride [ ] = [
48+ { selector : PreprintProvidersSelectors . getPreprintProviderDetails ( mockProviderId ) , value : mockProvider } ,
49+ { selector : PreprintProvidersSelectors . isPreprintProviderDetailsLoading , value : false } ,
50+ { selector : PreprintStepperSelectors . hasBeenSubmitted , value : false } ,
51+ ] ;
52+
53+ function setup ( overrides ?: { selectorOverrides ?: SignalOverride [ ] } ) {
54+ const signals = mergeSignalOverrides ( defaultSignals , overrides ?. selectorOverrides ) ;
55+
4456 routerMock = RouterMockBuilder . create ( ) . withNavigate ( jest . fn ( ) . mockResolvedValue ( true ) ) . build ( ) ;
45- routeMock = ActivatedRouteMockBuilder . create ( )
57+ const routeMock = ActivatedRouteMockBuilder . create ( )
4658 . withParams ( { providerId : mockProviderId , preprintId : mockPreprintId } )
4759 . withQueryParams ( { } )
4860 . build ( ) ;
4961
50- await TestBed . configureTestingModule ( {
51- imports : [
52- CreateNewVersionComponent ,
53- OSFTestingModule ,
54- ... MockComponents ( StepperComponent , FileStepComponent , ReviewStepComponent ) ,
55- ] ,
62+ brandServiceMock = BrandServiceMock . simple ( ) ;
63+ headerStyleMock = HeaderStyleServiceMock . simple ( ) ;
64+ browserTabMock = BrowserTabServiceMock . simple ( ) ;
65+
66+ TestBed . configureTestingModule ( {
67+ imports : [ CreateNewVersionComponent , ... MockComponents ( StepperComponent , FileStepComponent , ReviewStepComponent ) ] ,
5668 providers : [
57- TranslationServiceMock ,
58- MockProvider ( BrandService ) ,
59- MockProvider ( BrowserTabService ) ,
60- MockProvider ( HeaderStyleService ) ,
61- MockProvider ( Router , routerMock ) ,
69+ provideOSFCore ( ) ,
6270 MockProvider ( ActivatedRoute , routeMock ) ,
71+ MockProvider ( Router , routerMock ) ,
72+ MockProvider ( BrandService , brandServiceMock ) ,
73+ MockProvider ( HeaderStyleService , headerStyleMock ) ,
74+ MockProvider ( BrowserTabService , browserTabMock ) ,
6375 MockProvider ( IS_WEB , of ( true ) ) ,
64- provideMockStore ( {
65- signals : [
66- {
67- selector : PreprintStepperSelectors . getPreprint ,
68- value : mockPreprint ,
69- } ,
70- {
71- selector : PreprintProvidersSelectors . getPreprintProviderDetails ( mockProviderId ) ,
72- value : mockProvider ,
73- } ,
74- {
75- selector : PreprintProvidersSelectors . isPreprintProviderDetailsLoading ,
76- value : false ,
77- } ,
78- {
79- selector : PreprintStepperSelectors . hasBeenSubmitted ,
80- value : false ,
81- } ,
82- ] ,
83- } ) ,
76+ provideMockStore ( { signals } ) ,
8477 ] ,
85- } ) . compileComponents ( ) ;
78+ } ) ;
8679
80+ store = TestBed . inject ( Store ) ;
8781 fixture = TestBed . createComponent ( CreateNewVersionComponent ) ;
8882 component = fixture . componentInstance ;
8983 fixture . detectChanges ( ) ;
90- } ) ;
91-
92- afterEach ( ( ) => {
93- if ( fixture ) {
94- fixture . destroy ( ) ;
95- }
96- jest . restoreAllMocks ( ) ;
97- } ) ;
84+ }
9885
9986 it ( 'should initialize with correct default values' , ( ) => {
87+ setup ( ) ;
88+
10089 expect ( component . PreprintSteps ) . toBe ( PreprintSteps ) ;
10190 expect ( component . newVersionSteps ) . toBe ( createNewVersionStepsConst ) ;
10291 expect ( component . currentStep ( ) ) . toEqual ( createNewVersionStepsConst [ 0 ] ) ;
10392 expect ( component . classes ) . toBe ( 'flex-1 flex flex-column w-full' ) ;
10493 } ) ;
10594
106- it ( 'should return preprint from store' , ( ) => {
107- const preprint = component . preprint ( ) ;
108- expect ( preprint ) . toBe ( mockPreprint ) ;
109- } ) ;
95+ it ( 'should dispatch initial actions on creation' , ( ) => {
96+ setup ( ) ;
11097
111- it ( 'should return preprint provider from store' , ( ) => {
112- const provider = component . preprintProvider ( ) ;
113- expect ( provider ) . toBe ( mockProvider ) ;
98+ expect ( store . dispatch ) . toHaveBeenCalledWith ( new GetPreprintProviderById ( mockProviderId ) ) ;
99+ expect ( store . dispatch ) . toHaveBeenCalledWith ( new FetchPreprintById ( mockPreprintId ) ) ;
114100 } ) ;
115101
116- it ( 'should return loading state from store' , ( ) => {
117- const loading = component . isPreprintProviderLoading ( ) ;
118- expect ( loading ) . toBe ( false ) ;
119- } ) ;
102+ it ( 'should apply branding when provider is available' , ( ) => {
103+ setup ( ) ;
120104
121- it ( 'should return submission state from store' , ( ) => {
122- const submitted = component . hasBeenSubmitted ( ) ;
123- expect ( submitted ) . toBe ( false ) ;
105+ expect ( brandServiceMock . applyBranding ) . toHaveBeenCalledWith ( mockProvider . brand ) ;
106+ expect ( headerStyleMock . applyHeaderStyles ) . toHaveBeenCalledWith (
107+ mockProvider . brand . primaryColor ,
108+ mockProvider . brand . secondaryColor ,
109+ mockProvider . brand . heroBackgroundImageUrl
110+ ) ;
111+ expect ( browserTabMock . updateTabStyles ) . toHaveBeenCalledWith ( mockProvider . faviconUrl , mockProvider . name ) ;
124112 } ) ;
125113
126- it ( 'should return web environment state' , ( ) => {
127- const isWeb = component . isWeb ( ) ;
128- expect ( typeof isWeb ) . toBe ( 'boolean' ) ;
129- } ) ;
114+ it ( 'should reset services on destroy' , ( ) => {
115+ setup ( ) ;
130116
131- it ( 'should initialize with first step as current step' , ( ) => {
132- expect ( component . currentStep ( ) ) . toEqual ( createNewVersionStepsConst [ 0 ] ) ;
117+ component . ngOnDestroy ( ) ;
118+
119+ expect ( headerStyleMock . resetToDefaults ) . toHaveBeenCalled ( ) ;
120+ expect ( brandServiceMock . resetBranding ) . toHaveBeenCalled ( ) ;
121+ expect ( browserTabMock . resetToDefaults ) . toHaveBeenCalled ( ) ;
122+ expect ( store . dispatch ) . toHaveBeenCalledWith ( new ResetPreprintStepperState ( ) ) ;
133123 } ) ;
134124
135- it ( 'should handle step change when moving to previous step' , ( ) => {
136- const previousStep = createNewVersionStepsConst [ 0 ] ;
125+ it ( 'should prevent beforeunload when not submitted' , ( ) => {
126+ setup ( ) ;
127+ const event = { preventDefault : jest . fn ( ) } as unknown as BeforeUnloadEvent ;
137128
138- component . stepChange ( previousStep ) ;
129+ component . onBeforeUnload ( event ) ;
139130
140- expect ( component . currentStep ( ) ) . toEqual ( previousStep ) ;
131+ expect ( event . preventDefault ) . toHaveBeenCalled ( ) ;
141132 } ) ;
142133
143- it ( 'should not change step when moving to next step ' , ( ) => {
144- const currentStep = component . currentStep ( ) ;
145- const nextStep = createNewVersionStepsConst [ 1 ] ;
134+ it ( 'should not prevent beforeunload when submitted ' , ( ) => {
135+ setup ( { selectorOverrides : [ { selector : PreprintStepperSelectors . hasBeenSubmitted , value : true } ] } ) ;
136+ const event = { preventDefault : jest . fn ( ) } as unknown as BeforeUnloadEvent ;
146137
147- component . stepChange ( nextStep ) ;
138+ component . onBeforeUnload ( event ) ;
148139
149- expect ( component . currentStep ( ) ) . toEqual ( currentStep ) ;
140+ expect ( event . preventDefault ) . not . toHaveBeenCalled ( ) ;
150141 } ) ;
151142
152- it ( 'should move to next step' , ( ) => {
153- const currentIndex = component . currentStep ( ) ?. index ?? 0 ;
154- const nextStep = createNewVersionStepsConst [ currentIndex + 1 ] ;
155-
156- component . moveToNextStep ( ) ;
143+ it ( 'should prevent deactivation when not submitted' , ( ) => {
144+ setup ( ) ;
157145
158- expect ( component . currentStep ( ) ) . toEqual ( nextStep ) ;
146+ expect ( component . canDeactivate ( ) ) . toBe ( false ) ;
159147 } ) ;
160148
161- it ( 'should navigate to previous step (preprint page) ' , ( ) => {
162- component . moveToPreviousStep ( ) ;
149+ it ( 'should allow deactivation when submitted ' , ( ) => {
150+ setup ( { selectorOverrides : [ { selector : PreprintStepperSelectors . hasBeenSubmitted , value : true } ] } ) ;
163151
164- expect ( routerMock . navigate ) . toHaveBeenCalledWith ( [ mockPreprintId . split ( '_' ) [ 0 ] ] ) ;
152+ expect ( component . canDeactivate ( ) ) . toBe ( true ) ;
165153 } ) ;
166154
167- it ( 'should return canDeactivate state' , ( ) => {
168- const canDeactivate = component . canDeactivate ( ) ;
169- expect ( canDeactivate ) . toBe ( false ) ;
155+ it ( 'should ignore stepping forward via stepper' , ( ) => {
156+ setup ( ) ;
157+
158+ component . stepChange ( createNewVersionStepsConst [ 1 ] ) ;
159+
160+ expect ( component . currentStep ( ) ) . toEqual ( createNewVersionStepsConst [ 0 ] ) ;
170161 } ) ;
171162
172- it ( 'should handle beforeunload event' , ( ) => {
173- const event = {
174- preventDefault : jest . fn ( ) ,
175- } as unknown as BeforeUnloadEvent ;
163+ it ( 'should allow stepping back via stepper' , ( ) => {
164+ setup ( ) ;
165+ component . moveToNextStep ( ) ;
176166
177- const result = component . onBeforeUnload ( event ) ;
167+ component . stepChange ( createNewVersionStepsConst [ 0 ] ) ;
178168
179- expect ( event . preventDefault ) . toHaveBeenCalled ( ) ;
180- expect ( result ) . toBe ( false ) ;
169+ expect ( component . currentStep ( ) ) . toEqual ( createNewVersionStepsConst [ 0 ] ) ;
181170 } ) ;
182171
183- it ( 'should handle step navigation correctly' , ( ) => {
172+ it ( 'should move to next step' , ( ) => {
173+ setup ( ) ;
174+
184175 component . moveToNextStep ( ) ;
176+
185177 expect ( component . currentStep ( ) ) . toEqual ( createNewVersionStepsConst [ 1 ] ) ;
178+ } ) ;
186179
187- component . stepChange ( createNewVersionStepsConst [ 0 ] ) ;
188- expect ( component . currentStep ( ) ) . toEqual ( createNewVersionStepsConst [ 0 ] ) ;
180+ it ( 'should not move past the last step' , ( ) => {
181+ setup ( ) ;
182+ component . currentStep . set ( createNewVersionStepsConst [ createNewVersionStepsConst . length - 1 ] ) ;
183+
184+ component . moveToNextStep ( ) ;
185+
186+ expect ( component . currentStep ( ) ) . toEqual ( createNewVersionStepsConst [ createNewVersionStepsConst . length - 1 ] ) ;
189187 } ) ;
190188
191- it ( 'should handle edge case when moving to next step with undefined current step ' , ( ) => {
192- component . currentStep . set ( { } as StepOption ) ;
189+ it ( 'should navigate back to preprint page ' , ( ) => {
190+ setup ( ) ;
193191
194- expect ( ( ) => component . moveToNextStep ( ) ) . not . toThrow ( ) ;
192+ component . navigateBack ( ) ;
193+
194+ expect ( routerMock . navigate ) . toHaveBeenCalledWith ( [ mockPreprintId . split ( '_' ) [ 0 ] ] ) ;
195195 } ) ;
196196} ) ;
0 commit comments