Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f38d254

Browse files
Add filter in users list
1 parent 03b2904 commit f38d254

File tree

3 files changed

+359
-26
lines changed

3 files changed

+359
-26
lines changed

‎src/app/users/user-list/user-list.component.html‎

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,32 @@
22
<h2>User List</h2>
33
<mat-divider></mat-divider>
44

5+
<div class="filter-container">
6+
<button mat-fab extended color="primary" routerLink="add">
7+
<mat-icon>add</mat-icon>
8+
Add User
9+
</button>
10+
<mat-form-field>
11+
<input
12+
#filterInput
13+
matInput
14+
(input)="filterUsers($event)"
15+
placeholder="Filter by name"
16+
/>
17+
<button
18+
*ngIf="filterInput.value"
19+
matSuffix
20+
mat-icon-button
21+
aria-label="Clear"
22+
(click)="cleanFilterInput(filterInput)"
23+
>
24+
<mat-icon>close</mat-icon>
25+
</button>
26+
</mat-form-field>
27+
</div>
28+
529
<app-loading></app-loading>
6-
<div class="user-list"*ngIf="users">
30+
<div class="user-list">
731
<table mat-table [dataSource]="users" matSort class="mat-elevation-z8">
832
<ng-container matColumnDef="id">
933
<th
@@ -51,6 +75,7 @@ <h2>User List</h2>
5175
</table>
5276
</div>
5377
<mat-paginator
78+
*ngIf="!_hidePagination"
5479
[length]="totalUsers"
5580
[pageSize]="pageSize"
5681
[pageSizeOptions]="[5, 10, 20]"

‎src/app/users/user-list/user-list.component.spec.ts‎

Lines changed: 195 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
66
import { MaterialModule } from 'src/app/shared/material.module';
77
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
88
import { SharedModule } from 'src/app/shared/shared.module';
9+
import { UsersServiceMock } from 'src/app/testing/users-service.mock';
10+
import { LoadingService } from 'src/app/shared/services/loading.service';
11+
import { Subject, of } from 'rxjs';
12+
import { PageEvent } from '@angular/material/paginator';
913

1014
describe('UserListComponent', () => {
1115
let component: UserListComponent;
1216
let fixture: ComponentFixture<UserListComponent>;
17+
let usersService: UsersService;
18+
let loadingService: LoadingService;
1319

1420
beforeEach(async () => {
1521
await TestBed.configureTestingModule({
@@ -20,15 +26,203 @@ describe('UserListComponent', () => {
2026
BrowserAnimationsModule,
2127
SharedModule,
2228
],
23-
providers: [UsersService],
29+
providers: [{provide: UsersService,useClass: UsersServiceMock}],
2430
}).compileComponents();
2531

2632
fixture = TestBed.createComponent(UserListComponent);
2733
component = fixture.componentInstance;
34+
usersService = TestBed.inject(UsersService);
35+
loadingService = TestBed.inject(LoadingService);
2836
fixture.detectChanges();
2937
});
3038

3139
it('should create', () => {
3240
expect(component).toBeTruthy();
3341
});
42+
43+
describe('ngOnInit', () => {
44+
it('should initialize pagination settings', () => {
45+
component.initializePagination();
46+
expect(component.pageSize).toBe(10);
47+
expect(component.pageIndex).toBe(0);
48+
expect(component.totalUsers).toBe(0);
49+
});
50+
51+
it('should call getUsers', () => {
52+
spyOn(component, 'getUsers').and.callThrough();
53+
spyOn(loadingService, 'loadingOn');
54+
55+
component.getUsers();
56+
expect(component.getUsers).toHaveBeenCalled();
57+
expect(loadingService.loadingOn).toHaveBeenCalled();
58+
});
59+
});
60+
61+
describe('getPaginationData', () => {
62+
it('should call getUsers from UsersService and set users in MatTableDataSource', (done) => {
63+
spyOn(usersService, 'getUsers').and.callThrough();
64+
spyOn(component, 'applySortingOnResponse');
65+
spyOn(loadingService, 'loadingOff');
66+
67+
const filterValue = 'John';
68+
component.getPaginationData(filterValue).subscribe((users) => {
69+
expect(usersService.getUsers).toHaveBeenCalledWith(
70+
component.pageIndex,
71+
component.pageSize,
72+
filterValue
73+
);
74+
expect(component.applySortingOnResponse).toHaveBeenCalledWith(users);
75+
done();
76+
});
77+
});
78+
});
79+
80+
describe('onPageChange', () => {
81+
it('should update pageIndex and pageSize properties', () => {
82+
const event: PageEvent = { pageIndex: 2, pageSize: 10, length: 50 };
83+
component.onPageChange(event);
84+
85+
expect(component.pageIndex).toBe(2);
86+
expect(component.pageSize).toBe(10);
87+
});
88+
89+
it('should call loadingOn method of loadingService', () => {
90+
spyOn(loadingService, 'loadingOn');
91+
const event: PageEvent = { pageIndex: 0, pageSize: 5, length: 20 };
92+
component.onPageChange(event);
93+
94+
expect(loadingService.loadingOn).toHaveBeenCalled();
95+
});
96+
});
97+
98+
describe('filterUsers', () => {
99+
beforeEach(() => {
100+
spyOn(loadingService, 'loadingOn');
101+
spyOn(component, 'getPaginationData').and.returnValue(of([]));
102+
});
103+
it('should do nothing when event or event.target is not provided', () => {
104+
const event: any = null;
105+
component.filterUsers(event);
106+
expect(component.getPaginationData).not.toHaveBeenCalled();
107+
expect(loadingService.loadingOn).not.toHaveBeenCalled();
108+
});
109+
it('should cancel previous API call and call loadingOn when a non-empty filter value is provided', () => {
110+
const inputValue = 'example filter';
111+
const event: any = { target: { value: inputValue } };
112+
113+
component.filterUsers(event);
114+
115+
expect(component.getPaginationData).toHaveBeenCalledWith(
116+
inputValue.toLowerCase()
117+
);
118+
expect(loadingService.loadingOn).toHaveBeenCalled();
119+
});
120+
121+
it('should debounce the API call by 300ms', (done: DoneFn) => {
122+
const debounceTimeValue = 300;
123+
124+
const event: any = { target: { value: 'sample filter' } };
125+
component.filterUsers(event);
126+
127+
expect(component.getPaginationData).toHaveBeenCalledWith('sample filter');
128+
129+
// Wait for the debounce time plus a little buffer (50ms) before checking the getPaginationData call count.
130+
setTimeout(() => {
131+
expect(component.getPaginationData).toHaveBeenCalledTimes(1);
132+
done();
133+
}, debounceTimeValue + 50);
134+
});
135+
it('should call clearFilterUsers when an empty filter value is provided', () => {
136+
const event: any = { target: { value: '' } };
137+
spyOn(component, 'clearFilterUsers');
138+
139+
component.filterUsers(event);
140+
141+
expect(component.clearFilterUsers).toHaveBeenCalled();
142+
});
143+
});
144+
describe('clearFilterUsers', () => {
145+
it('should call loadingOn method of loadingService', () => {
146+
spyOn(loadingService, 'loadingOn');
147+
component.clearFilterUsers();
148+
149+
expect(loadingService.loadingOn).toHaveBeenCalled();
150+
});
151+
it('should set pageIndex to 0', () => {
152+
component.pageIndex = 5; // Set pageIndex to a non-zero value.
153+
component.clearFilterUsers();
154+
155+
expect(component.pageIndex).toBe(0);
156+
});
157+
158+
it('should call displayPagination method', () => {
159+
spyOn(component, 'displayPagination');
160+
component.clearFilterUsers();
161+
162+
expect(component.displayPagination).toHaveBeenCalled();
163+
});
164+
165+
it('should call getPaginationData', () => {
166+
const mockObservable = of(/* mock API response */);
167+
spyOn(component, 'getPaginationData').and.returnValue(of([]));
168+
169+
component.clearFilterUsers();
170+
expect(component.getPaginationData).toHaveBeenCalled();
171+
});
172+
});
173+
174+
describe('cancelPreviousAPICall', () => {
175+
it('should call next and complete on ngUnsubscribe$', () => {
176+
const nextSpy = spyOn(component['ngUnsubscribe$'], 'next');
177+
const completeSpy = spyOn(component['ngUnsubscribe$'], 'complete');
178+
179+
component.cancelPreviousAPICall();
180+
181+
expect(nextSpy).toHaveBeenCalledWith('');
182+
expect(completeSpy).toHaveBeenCalled();
183+
});
184+
it('should assign a new instance of Subject to ngUnsubscribe$', () => {
185+
const previousSubject = component['ngUnsubscribe$'];
186+
187+
component.cancelPreviousAPICall();
188+
189+
expect(component['ngUnsubscribe$']).not.toBe(previousSubject);
190+
expect(component['ngUnsubscribe$'] instanceof Subject).toBeTrue();
191+
});
192+
});
193+
194+
it('should set _hidePagination to true in hidePagination()', () => {
195+
component.hidePagination();
196+
expect(component._hidePagination).toBeTrue();
197+
});
198+
199+
it('should set _hidePagination to false in displayPagination()', () => {
200+
component.displayPagination();
201+
expect(component._hidePagination).toBeFalse();
202+
});
203+
204+
describe('clearFilterUsers', () => {
205+
it('should clear the input field value and call clearFilterUsers', () => {
206+
const inputElement = document.createElement('input') as HTMLInputElement;
207+
inputElement.value = 'Sample Filter';
208+
209+
spyOn(component, 'clearFilterUsers');
210+
211+
component.cleanFilterInput(inputElement);
212+
213+
expect(inputElement.value).toBe('');
214+
215+
expect(component.clearFilterUsers).toHaveBeenCalled();
216+
});
217+
});
218+
219+
it('should call next and complete on ngUnsubscribe$', () => {
220+
spyOn(component['ngUnsubscribe$'], 'next');
221+
spyOn(component['ngUnsubscribe$'], 'complete');
222+
223+
component.ngOnDestroy();
224+
225+
expect(component['ngUnsubscribe$'].next).toHaveBeenCalledWith('');
226+
expect(component['ngUnsubscribe$'].complete).toHaveBeenCalled();
227+
});
34228
});

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /