@@ -9,40 +9,129 @@ import { provideHttpClient } from '@angular/common/http';
9
9
import { provideHttpClientTesting } from '@angular/common/http/testing' ;
10
10
import { signal } from '@angular/core' ;
11
11
import { ComponentFixture , TestBed } from '@angular/core/testing' ;
12
+ import { FormBuilder , ReactiveFormsModule } from '@angular/forms' ;
12
13
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' ;
14
19
15
20
import { NotificationsComponent } from './notifications.component' ;
21
+ import { NotificationSubscriptionSelectors } from './store' ;
16
22
17
23
describe ( 'NotificationsComponent' , ( ) => {
18
24
let component : NotificationsComponent ;
19
25
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
+ ] ;
20
41
21
42
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 ) ;
28
73
} ) ;
29
74
75
+ MOCK_STORE . dispatch . mockImplementation ( ( ) => of ( ) ) ;
76
+
30
77
await TestBed . configureTestingModule ( {
31
- imports : [ NotificationsComponent , MockPipe ( TranslatePipe ) ] ,
78
+ imports : [ NotificationsComponent , MockPipe ( TranslatePipe ) , ReactiveFormsModule ] ,
32
79
providers : [
33
80
provideHttpClient ( ) ,
34
81
provideHttpClientTesting ( ) ,
35
82
MockProvider ( TranslatePipe ) ,
36
- MockProvider ( Store , store ) ,
83
+ MockProvider ( Store , MOCK_STORE ) ,
84
+ MockProvider ( LoaderService , mockLoaderService ) ,
85
+ MockProvider ( ToastService , mockToastService ) ,
86
+ FormBuilder ,
37
87
] ,
38
88
} ) . compileComponents ( ) ;
39
89
40
90
fixture = TestBed . createComponent ( NotificationsComponent ) ;
41
91
component = fixture . componentInstance ;
92
+
93
+ loaderService = TestBed . inject ( LoaderService ) ;
94
+
42
95
fixture . detectChanges ( ) ;
43
96
} ) ;
44
97
45
98
it ( 'should create' , ( ) => {
46
99
expect ( component ) . toBeTruthy ( ) ;
47
100
} ) ;
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
+ } ) ;
48
137
} ) ;
0 commit comments