1+ import { Store } from '@ngxs/store' ;
2+
13import { MockComponents , MockProvider } from 'ng-mocks' ;
24
35import { DynamicDialogConfig , DynamicDialogRef } from 'primeng/dynamicdialog' ;
6+ import { PaginatorState } from 'primeng/paginator' ;
47
5- import { of } from 'rxjs' ;
6-
7- import { ComponentFixture , TestBed } from '@angular/core/testing' ;
8+ import { signal } from '@angular/core' ;
9+ import { ComponentFixture , fakeAsync , TestBed , tick } from '@angular/core/testing' ;
810
911import { CustomPaginatorComponent } from '@osf/shared/components/custom-paginator/custom-paginator.component' ;
1012import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component' ;
1113import { SearchInputComponent } from '@osf/shared/components/search-input/search-input.component' ;
1214
15+ import { AddModeratorType } from '../../enums' ;
1316import { ModeratorAddModel } from '../../models' ;
1417import { ModeratorsSelectors } from '../../store/moderators' ;
1518
@@ -23,17 +26,18 @@ import { provideMockStore } from '@testing/providers/store-provider.mock';
2326describe ( 'AddModeratorDialogComponent' , ( ) => {
2427 let component : AddModeratorDialogComponent ;
2528 let fixture : ComponentFixture < AddModeratorDialogComponent > ;
26- let mockDialogRef : jest . Mocked < DynamicDialogRef > ;
27- let mockDialogConfig : jest . Mocked < DynamicDialogConfig > ;
29+ let dialogRef : jest . Mocked < DynamicDialogRef > ;
30+ let dialogConfig : DynamicDialogConfig ;
31+ let store : Store ;
2832
2933 const mockUsers = [ MOCK_USER ] ;
3034
3135 beforeEach ( async ( ) => {
32- mockDialogRef = DynamicDialogRefMock . useValue as unknown as jest . Mocked < DynamicDialogRef > ;
36+ dialogRef = DynamicDialogRefMock . useValue as unknown as jest . Mocked < DynamicDialogRef > ;
3337
34- mockDialogConfig = {
38+ dialogConfig = {
3539 data : [ ] ,
36- } as jest . Mocked < DynamicDialogConfig > ;
40+ } as DynamicDialogConfig ;
3741
3842 await TestBed . configureTestingModule ( {
3943 imports : [
@@ -43,34 +47,30 @@ describe('AddModeratorDialogComponent', () => {
4347 ] ,
4448 providers : [
4549 DynamicDialogRefMock ,
46- MockProvider ( DynamicDialogConfig , mockDialogConfig ) ,
50+ MockProvider ( DynamicDialogConfig , dialogConfig ) ,
4751 provideMockStore ( {
4852 signals : [
49- { selector : ModeratorsSelectors . getUsers , value : mockUsers } ,
53+ { selector : ModeratorsSelectors . getUsers , value : signal ( mockUsers ) } ,
5054 { selector : ModeratorsSelectors . isUsersLoading , value : false } ,
5155 { selector : ModeratorsSelectors . getUsersTotalCount , value : 2 } ,
56+ { selector : ModeratorsSelectors . getUsersNextLink , value : signal ( null ) } ,
57+ { selector : ModeratorsSelectors . getUsersPreviousLink , value : signal ( null ) } ,
5258 ] ,
5359 } ) ,
5460 ] ,
5561 } ) . compileComponents ( ) ;
5662
63+ store = TestBed . inject ( Store ) ;
5764 fixture = TestBed . createComponent ( AddModeratorDialogComponent ) ;
5865 component = fixture . componentInstance ;
59- } ) ;
60-
61- afterEach ( ( ) => {
62- jest . clearAllMocks ( ) ;
63- jest . useRealTimers ( ) ;
66+ fixture . detectChanges ( ) ;
6467 } ) ;
6568
6669 it ( 'should create' , ( ) => {
67- fixture . detectChanges ( ) ;
6870 expect ( component ) . toBeTruthy ( ) ;
6971 } ) ;
7072
7173 it ( 'should initialize with default values' , ( ) => {
72- fixture . detectChanges ( ) ;
73-
7474 expect ( component . isInitialState ( ) ) . toBe ( true ) ;
7575 expect ( component . currentPage ( ) ) . toBe ( 1 ) ;
7676 expect ( component . first ( ) ) . toBe ( 0 ) ;
@@ -79,15 +79,13 @@ describe('AddModeratorDialogComponent', () => {
7979 expect ( component . searchControl . value ) . toBe ( '' ) ;
8080 } ) ;
8181
82- it ( 'should load users on initialization' , ( ) => {
83- fixture . detectChanges ( ) ;
84-
82+ it ( 'should load users from store' , ( ) => {
8583 expect ( component . users ( ) ) . toEqual ( mockUsers ) ;
8684 expect ( component . isLoading ( ) ) . toBe ( false ) ;
8785 expect ( component . totalUsersCount ( ) ) . toBe ( 2 ) ;
8886 } ) ;
8987
90- it ( 'should add moderator ' , ( ) => {
88+ it ( 'should close dialog with correct data for addModerator ' , ( ) => {
9189 const mockSelectedUsers : ModeratorAddModel [ ] = [
9290 {
9391 id : '1' ,
@@ -100,100 +98,86 @@ describe('AddModeratorDialogComponent', () => {
10098
10199 component . addModerator ( ) ;
102100
103- expect ( mockDialogRef . close ) . toHaveBeenCalledWith ( {
101+ expect ( dialogRef . close ) . toHaveBeenCalledWith ( {
104102 data : mockSelectedUsers ,
105- type : 1 ,
103+ type : AddModeratorType . Search ,
106104 } ) ;
107105 } ) ;
108106
109- it ( 'should invite moderator ' , ( ) => {
107+ it ( 'should close dialog with correct data for inviteModerator ' , ( ) => {
110108 component . inviteModerator ( ) ;
111109
112- expect ( mockDialogRef . close ) . toHaveBeenCalledWith ( {
110+ expect ( dialogRef . close ) . toHaveBeenCalledWith ( {
113111 data : [ ] ,
114- type : 2 ,
112+ type : AddModeratorType . Invite ,
115113 } ) ;
116114 } ) ;
117115
118- it ( 'should handle page change correctly' , ( ) => {
119- const mockEvent = { page : 1 , first : 10 , rows : 10 } ;
120- const searchUsersSpy = jest . fn ( ) ;
121- component . actions = {
122- ...component . actions ,
123- searchUsers : searchUsersSpy ,
124- } ;
125-
126- component . pageChanged ( mockEvent ) ;
127-
128- expect ( component . currentPage ( ) ) . toBe ( 2 ) ;
129- expect ( component . first ( ) ) . toBe ( 10 ) ;
130- expect ( searchUsersSpy ) . toHaveBeenCalledWith ( '' , 2 ) ;
131- } ) ;
132-
133- it ( 'should handle page change when page is null' , ( ) => {
134- const mockEvent = { page : undefined , first : 0 , rows : 10 } ;
135- const searchUsersSpy = jest . fn ( ) ;
136- component . actions = {
137- ...component . actions ,
138- searchUsers : searchUsersSpy ,
139- } ;
116+ it ( 'should handle pagination correctly' , ( ) => {
117+ const dispatchSpy = jest . spyOn ( store , 'dispatch' ) ;
140118
141- component . pageChanged ( mockEvent ) ;
119+ component . pageChanged ( { first : 0 } as PaginatorState ) ;
120+ expect ( dispatchSpy ) . not . toHaveBeenCalled ( ) ;
142121
122+ component . searchControl . setValue ( 'test' ) ;
123+ component . pageChanged ( { page : 0 , first : 0 , rows : 10 } as PaginatorState ) ;
124+ expect ( dispatchSpy ) . toHaveBeenCalled ( ) ;
143125 expect ( component . currentPage ( ) ) . toBe ( 1 ) ;
144126 expect ( component . first ( ) ) . toBe ( 0 ) ;
145- expect ( searchUsersSpy ) . toHaveBeenCalledWith ( '' , 1 ) ;
146127 } ) ;
147128
148- it ( 'should clear users on destroy' , ( ) => {
149- const clearUsersSpy = jest . fn ( ) ;
150- component . actions = {
151- ...component . actions ,
152- clearUsers : clearUsersSpy ,
153- } ;
129+ it ( 'should navigate to next page when link is available' , ( ) => {
130+ const nextLink = 'http://api.example.com/users?page=3' ;
131+ const originalSelect = store . select . bind ( store ) ;
132+ ( store . select as jest . Mock ) = jest . fn ( ( selector ) => {
133+ if ( selector === ModeratorsSelectors . getUsersNextLink ) {
134+ return signal ( nextLink ) ;
135+ }
136+ return originalSelect ( selector ) ;
137+ } ) ;
154138
155- component . ngOnDestroy ( ) ;
139+ Object . defineProperty ( component , 'usersNextLink' , {
140+ get : ( ) => signal ( nextLink ) ,
141+ configurable : true ,
142+ } ) ;
156143
157- expect ( clearUsersSpy ) . toHaveBeenCalled ( ) ;
158- } ) ;
144+ const dispatchSpy = jest . spyOn ( store , 'dispatch' ) ;
145+ component . currentPage . set ( 2 ) ;
146+ component . pageChanged ( { page : 2 , first : 20 , rows : 10 } as PaginatorState ) ;
159147
160- it ( 'should have actions defined' , ( ) => {
161- expect ( component . actions ) . toBeDefined ( ) ;
162- expect ( component . actions . searchUsers ) . toBeDefined ( ) ;
163- expect ( component . actions . clearUsers ) . toBeDefined ( ) ;
148+ expect ( dispatchSpy ) . toHaveBeenCalled ( ) ;
149+ expect ( component . currentPage ( ) ) . toBe ( 3 ) ;
150+ expect ( component . first ( ) ) . toBe ( 20 ) ;
164151 } ) ;
165152
166- it ( 'should handle search control value changes' , ( ) => {
167- jest . useFakeTimers ( ) ;
168- fixture . detectChanges ( ) ;
169- const searchUsersSpy = jest . fn ( ) . mockReturnValue ( of ( { } ) ) ;
170- component . actions = {
171- ...component . actions ,
172- searchUsers : searchUsersSpy ,
173- } ;
174-
175- component . searchControl . setValue ( 'test search' ) ;
153+ it ( 'should debounce and filter search input' , fakeAsync ( ( ) => {
154+ const dispatchSpy = jest . spyOn ( store , 'dispatch' ) ;
176155
177- jest . advanceTimersByTime ( 600 ) ;
156+ component . searchControl . setValue ( 't' ) ;
157+ tick ( 200 ) ;
158+ component . searchControl . setValue ( 'test' ) ;
159+ tick ( 500 ) ;
178160
179- expect ( searchUsersSpy ) . toHaveBeenCalledWith ( 'test search' , 1 ) ;
161+ expect ( dispatchSpy ) . toHaveBeenCalledTimes ( 1 ) ;
180162 expect ( component . isInitialState ( ) ) . toBe ( false ) ;
181163 expect ( component . selectedUsers ( ) ) . toEqual ( [ ] ) ;
164+ } ) ) ;
182165
183- jest . useRealTimers ( ) ;
184- } ) ;
185-
186- it ( 'should not search when search term is empty' , ( ) => {
187- fixture . detectChanges ( ) ;
188- const searchUsersSpy = jest . fn ( ) ;
189- component . actions = {
190- ...component . actions ,
191- searchUsers : searchUsersSpy ,
192- } ;
166+ it ( 'should not search empty or whitespace values' , fakeAsync ( ( ) => {
167+ const dispatchSpy = jest . spyOn ( store , 'dispatch' ) ;
193168
194169 component . searchControl . setValue ( '' ) ;
170+ tick ( 500 ) ;
171+ expect ( dispatchSpy ) . not . toHaveBeenCalled ( ) ;
172+
195173 component . searchControl . setValue ( ' ' ) ;
174+ tick ( 500 ) ;
175+ expect ( dispatchSpy ) . not . toHaveBeenCalled ( ) ;
176+ } ) ) ;
196177
197- expect ( searchUsersSpy ) . not . toHaveBeenCalled ( ) ;
178+ it ( 'should clear users on destroy' , ( ) => {
179+ const dispatchSpy = jest . spyOn ( store , 'dispatch' ) ;
180+ component . ngOnDestroy ( ) ;
181+ expect ( dispatchSpy ) . toHaveBeenCalled ( ) ;
198182 } ) ;
199183} ) ;
0 commit comments