@@ -9,40 +9,129 @@ import { provideHttpClient } from '@angular/common/http';
99import { provideHttpClientTesting } from '@angular/common/http/testing' ;
1010import { signal } from '@angular/core' ;
1111import { ComponentFixture , TestBed } from '@angular/core/testing' ;
12+ import { FormBuilder , ReactiveFormsModule } from '@angular/forms' ;
1213
13- import { MOCK_STORE } from '@shared/mocks' ;
14+ import { UserSettings } from '@osf/core/models' ;
15+ import { UserSelectors } from '@osf/core/store/user' ;
16+ import { LoaderService , ToastService } from '@osf/shared/services' ;
17+ import { SubscriptionEvent , SubscriptionFrequency } from '@shared/enums' ;
18+ import { MOCK_STORE , MOCK_USER } from '@shared/mocks' ;
1419
1520import { NotificationsComponent } from './notifications.component' ;
21+ import { NotificationSubscriptionSelectors } from './store' ;
1622
1723describe ( 'NotificationsComponent' , ( ) => {
1824 let component : NotificationsComponent ;
1925 let fixture : ComponentFixture < NotificationsComponent > ;
26+ let loaderService : LoaderService ;
27+
28+ const mockUserSettings : UserSettings = {
29+ subscribeOsfGeneralEmail : true ,
30+ subscribeOsfHelpEmail : false ,
31+ } ;
32+
33+ const mockNotificationSubscriptions = [
34+ { id : 'id1' , event : SubscriptionEvent . GlobalComments , frequency : SubscriptionFrequency . Daily } ,
35+ {
36+ id : 'id2' ,
37+ event : SubscriptionEvent . GlobalMentions ,
38+ frequency : SubscriptionFrequency . Instant ,
39+ } ,
40+ ] ;
2041
2142 beforeEach ( async ( ) => {
22- const store = MOCK_STORE ;
23- store . selectSignal . mockImplementation ( ( ) => {
24- return signal ( [ ] ) ;
25- } ) ;
26- store . dispatch . mockImplementation ( ( ) => {
27- return of ( ) ;
43+ const mockToastService = {
44+ showSuccess : jest . fn ( ) ,
45+ showError : jest . fn ( ) ,
46+ } ;
47+
48+ const mockLoaderService = {
49+ show : jest . fn ( ) ,
50+ hide : jest . fn ( ) ,
51+ } ;
52+
53+ MOCK_STORE . selectSignal . mockImplementation ( ( selector ) => {
54+ if ( selector === UserSelectors . getCurrentUser ) {
55+ return signal ( MOCK_USER ) ;
56+ }
57+ if ( selector === UserSelectors . getCurrentUserSettings ) {
58+ return signal ( mockUserSettings ) ;
59+ }
60+ if ( selector === NotificationSubscriptionSelectors . getAllGlobalNotificationSubscriptions ) {
61+ return signal ( mockNotificationSubscriptions ) ;
62+ }
63+ if ( selector === UserSelectors . isUserSettingsLoading ) {
64+ return signal ( false ) ;
65+ }
66+ if ( selector === UserSelectors . isUserSettingsSubmitting ) {
67+ return signal ( false ) ;
68+ }
69+ if ( selector === NotificationSubscriptionSelectors . isLoading ) {
70+ return signal ( false ) ;
71+ }
72+ return signal ( null ) ;
2873 } ) ;
2974
75+ MOCK_STORE . dispatch . mockImplementation ( ( ) => of ( ) ) ;
76+
3077 await TestBed . configureTestingModule ( {
31- imports : [ NotificationsComponent , MockPipe ( TranslatePipe ) ] ,
78+ imports : [ NotificationsComponent , MockPipe ( TranslatePipe ) , ReactiveFormsModule ] ,
3279 providers : [
3380 provideHttpClient ( ) ,
3481 provideHttpClientTesting ( ) ,
3582 MockProvider ( TranslatePipe ) ,
36- MockProvider ( Store , store ) ,
83+ MockProvider ( Store , MOCK_STORE ) ,
84+ MockProvider ( LoaderService , mockLoaderService ) ,
85+ MockProvider ( ToastService , mockToastService ) ,
86+ FormBuilder ,
3787 ] ,
3888 } ) . compileComponents ( ) ;
3989
4090 fixture = TestBed . createComponent ( NotificationsComponent ) ;
4191 component = fixture . componentInstance ;
92+
93+ loaderService = TestBed . inject ( LoaderService ) ;
94+
4295 fixture . detectChanges ( ) ;
4396 } ) ;
4497
4598 it ( 'should create' , ( ) => {
4699 expect ( component ) . toBeTruthy ( ) ;
47100 } ) ;
101+
102+ it ( 'should not call loader hide when no user exists' , ( ) => {
103+ MOCK_STORE . selectSignal . mockImplementation ( ( selector ) => {
104+ if ( selector === UserSelectors . getCurrentUser ) {
105+ return signal ( null ) ;
106+ }
107+
108+ return signal ( null ) ;
109+ } ) ;
110+ component . emailPreferencesFormSubmit ( ) ;
111+
112+ expect ( loaderService . hide ) . not . toHaveBeenCalled ( ) ;
113+ } ) ;
114+
115+ it ( 'should handle subscription completion correctly' , ( ) => {
116+ const mockDispatch = jest . fn ( ) . mockReturnValue ( of ( { } ) ) ;
117+ MOCK_STORE . dispatch . mockImplementation ( mockDispatch ) ;
118+
119+ component . emailPreferencesFormSubmit ( ) ;
120+
121+ const subscription = mockDispatch . mock . results [ 0 ] . value ;
122+ subscription . subscribe ( ( ) => {
123+ expect ( loaderService . hide ) . toHaveBeenCalledTimes ( 1 ) ;
124+ } ) ;
125+ } ) ;
126+
127+ it ( 'should call dispatch only once per subscription change' , ( ) => {
128+ const mockDispatch = jest . fn ( ) . mockReturnValue ( of ( { } ) ) ;
129+ MOCK_STORE . dispatch . mockImplementation ( mockDispatch ) ;
130+ const event = SubscriptionEvent . GlobalComments ;
131+ const frequency = SubscriptionFrequency . Daily ;
132+
133+ component . onSubscriptionChange ( event , frequency ) ;
134+
135+ expect ( mockDispatch ) . toHaveBeenCalledTimes ( 1 ) ;
136+ } ) ;
48137} ) ;
0 commit comments