Skip to content

Commit 7c638b1

Browse files
committed
added tests
1 parent 8d25189 commit 7c638b1

File tree

9 files changed

+267
-85
lines changed

9 files changed

+267
-85
lines changed

web-app/admin/src/app/admin/admin-teams/admin-dashboard/admin-teams.component.html

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,36 +34,29 @@
3434
</div>
3535
</nav>
3636

37-
<div class="row">
38-
<div class="col-md-12">
39-
<div class="mat-elevation-z1">
40-
<div class="table-container" [style.min-height.px]="pageSize * 48">
41-
<table mat-table [class.mat-table]="true" [dataSource]="dataSource"
42-
class="mat-table-even-columns">
43-
<ng-container matColumnDef="name">
44-
<th mat-header-cell *matHeaderCellDef>Name</th>
45-
<td mat-cell *matCellDef="let team">
46-
<div class="strong">{{ team.name }}</div>
47-
</td>
48-
</ng-container>
37+
<div class="col-md-12 mat-elevation-z1 w-100">
38+
<table mat-table [class.mat-table]="true" [dataSource]="dataSource" class="mat-table-even-columns">
39+
<ng-container matColumnDef="name">
40+
<th mat-header-cell *matHeaderCellDef>Name</th>
41+
<td mat-cell *matCellDef="let team">
42+
<div class="strong">{{ team.name }}</div>
43+
</td>
44+
</ng-container>
4945

50-
<ng-container matColumnDef="description">
51-
<th mat-header-cell *matHeaderCellDef>Description</th>
52-
<td mat-cell *matCellDef="let team">
53-
<div class="muted">{{ team.description }}</div>
54-
</td>
55-
</ng-container>
46+
<ng-container matColumnDef="description">
47+
<th mat-header-cell *matHeaderCellDef>Description</th>
48+
<td mat-cell *matCellDef="let team">
49+
<div class="muted description-cell" [title]="team.description">{{ team.description }}</div>
50+
</td>
51+
</ng-container>
5652

57-
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
58-
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="gotoTeam(row)"
59-
class="pointer"></tr>
60-
</table>
61-
</div>
62-
<mat-paginator [length]="totalTeams" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions"
63-
(page)="onPageChange($event)">
64-
</mat-paginator>
65-
</div>
66-
</div>
53+
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
54+
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="gotoTeam(row)"
55+
class="pointer"></tr>
56+
</table>
57+
<mat-paginator [length]="totalTeams" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions"
58+
(page)="onPageChange($event)">
59+
</mat-paginator>
6760
</div>
6861
</div>
6962
</div>

web-app/admin/src/app/admin/admin-teams/admin-dashboard/admin-teams.component.scss

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,37 @@
22
table-layout: fixed;
33
width: 100%;
44

5+
.mat-row {
6+
height: 3.75rem;
7+
}
8+
59
.mat-cell {
610
text-overflow: ellipsis;
7-
white-space: wrap;
11+
white-space: nowrap;
12+
overflow: hidden;
813
}
914

15+
.mat-cell,
1016
.mat-header-cell {
1117
font-size: 1em;
18+
padding: 1rem 0.75rem;
1219
}
1320

14-
.mat-cell {
15-
font-size: 0.875em;
21+
.mat-header-cell {
22+
font-weight: 600;
23+
color: rgba(0, 0, 0, 0.87);
1624
}
1725

18-
.mat-header-cell,
19-
.mat-cell {
20-
padding: 0 0.75rem;
26+
.description-cell {
27+
display: -webkit-box;
28+
-webkit-line-clamp: 3;
29+
line-clamp: 3;
30+
-webkit-box-orient: vertical;
31+
overflow: hidden;
32+
text-overflow: ellipsis;
33+
white-space: normal;
34+
line-height: 1.2;
35+
max-height: calc(3 * 1.2em);
2136
}
2237
}
2338

Lines changed: 220 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,238 @@
1-
import { ComponentFixture, TestBed } from '@angular/core/testing';
1+
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
2+
import { MatDialog } from '@angular/material/dialog';
3+
import { MatTableModule } from '@angular/material/table';
4+
import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
5+
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
6+
import { FormsModule } from '@angular/forms';
7+
import { of } from 'rxjs';
28

39
import { AdminTeamsComponent } from './admin-teams.component';
10+
import { TeamsService } from '../teams-service';
11+
import { Team } from '../team';
12+
import { AdminTeamCreateComponent } from '../admin-team-create/admin-team-create.component';
413

514
describe('AdminTeamsComponent', () => {
615
let component: AdminTeamsComponent;
716
let fixture: ComponentFixture<AdminTeamsComponent>;
17+
let mockTeamsService: jasmine.SpyObj<TeamsService>;
18+
let mockDialog: jasmine.SpyObj<MatDialog>;
19+
20+
const mockTeams: Team[] = [
21+
{ id: '1', name: 'Team Alpha', description: 'First team description', teamEventId: '507f1f77bcf86cd799439011' },
22+
{ id: '2', name: 'Team Beta', description: 'Second team description with much longer text that might wrap', teamEventId: '507f191e810c19729de860ea' },
23+
{ id: '3', name: 'Team Gamma', description: 'Third team', teamEventId: '507f1f77bcf86cd799439012' }
24+
];
25+
26+
const mockTeamsResponse = [{
27+
items: mockTeams,
28+
totalCount: mockTeams.length
29+
}];
830

931
beforeEach(async () => {
32+
const teamsServiceSpy = jasmine.createSpyObj('TeamsService', ['getTeams']);
33+
const dialogSpy = jasmine.createSpyObj('MatDialog', ['open']);
34+
1035
await TestBed.configureTestingModule({
11-
declarations: [ AdminTeamsComponent ]
12-
})
13-
.compileComponents();
36+
declarations: [AdminTeamsComponent],
37+
imports: [
38+
MatTableModule,
39+
MatPaginatorModule,
40+
NoopAnimationsModule,
41+
FormsModule
42+
],
43+
providers: [
44+
{ provide: TeamsService, useValue: teamsServiceSpy },
45+
{ provide: MatDialog, useValue: dialogSpy }
46+
]
47+
}).compileComponents();
1448

1549
fixture = TestBed.createComponent(AdminTeamsComponent);
1650
component = fixture.componentInstance;
17-
fixture.detectChanges();
51+
mockTeamsService = TestBed.inject(TeamsService) as jasmine.SpyObj<TeamsService>;
52+
mockDialog = TestBed.inject(MatDialog) as jasmine.SpyObj<MatDialog>;
53+
54+
mockTeamsService.getTeams.and.returnValue(of(mockTeamsResponse));
1855
});
1956

2057
it('should create', () => {
2158
expect(component).toBeTruthy();
2259
});
60+
61+
it('should initialize with correct default values', () => {
62+
expect(component.teamSearch).toBe('');
63+
expect(component.pageSize).toBe(10);
64+
expect(component.pageIndex).toBe(0);
65+
expect(component.displayedColumns).toEqual(['name', 'description']);
66+
});
67+
68+
it('should fetch teams on init', () => {
69+
fixture.detectChanges();
70+
71+
expect(mockTeamsService.getTeams).toHaveBeenCalledWith({
72+
term: '',
73+
sort: { name: 1 },
74+
limit: 10,
75+
start: '0'
76+
});
77+
expect(component.teams).toEqual(mockTeams);
78+
expect(component.totalTeams).toBe(mockTeams.length);
79+
expect(component.dataSource.data).toEqual(mockTeams);
80+
});
81+
82+
it('should perform search with debouncing', fakeAsync(() => {
83+
fixture.detectChanges();
84+
mockTeamsService.getTeams.calls.reset();
85+
86+
component.search('test');
87+
tick(100); // Less than debounce time
88+
expect(mockTeamsService.getTeams).not.toHaveBeenCalled();
89+
90+
tick(200); // Complete debounce time (250ms total)
91+
expect(mockTeamsService.getTeams).toHaveBeenCalledWith({
92+
term: 'test',
93+
sort: { name: 1 },
94+
limit: 10,
95+
start: '0'
96+
});
97+
}));
98+
99+
it('should reset page index when searching', fakeAsync(() => {
100+
fixture.detectChanges();
101+
component.pageIndex = 2;
102+
103+
component.search('test');
104+
tick(250);
105+
106+
expect(component.pageIndex).toBe(0);
107+
}));
108+
109+
it('should handle page changes', () => {
110+
fixture.detectChanges();
111+
mockTeamsService.getTeams.calls.reset();
112+
113+
const pageEvent: PageEvent = {
114+
pageIndex: 1,
115+
pageSize: 25,
116+
length: 100
117+
};
118+
119+
component.onPageChange(pageEvent);
120+
121+
expect(component.pageSize).toBe(25);
122+
expect(component.pageIndex).toBe(1);
123+
expect(mockTeamsService.getTeams).toHaveBeenCalledWith({
124+
term: '',
125+
sort: { name: 1 },
126+
limit: 25,
127+
start: '25'
128+
});
129+
});
130+
131+
it('should reset search and pagination', () => {
132+
component.teamSearch = 'test';
133+
component.pageIndex = 2;
134+
fixture.detectChanges();
135+
mockTeamsService.getTeams.calls.reset();
136+
137+
component.reset();
138+
139+
expect(component.teamSearch).toBe('');
140+
expect(component.pageIndex).toBe(0);
141+
expect(mockTeamsService.getTeams).toHaveBeenCalledWith({
142+
term: '',
143+
sort: { name: 1 },
144+
limit: 10,
145+
start: '0'
146+
});
147+
});
148+
149+
it('should open new team dialog', () => {
150+
const dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['afterClosed']);
151+
dialogRefSpy.afterClosed.and.returnValue(of(null));
152+
mockDialog.open.and.returnValue(dialogRefSpy);
153+
154+
component.newTeam();
155+
156+
expect(mockDialog.open).toHaveBeenCalledWith(AdminTeamCreateComponent, {
157+
width: '50rem',
158+
height: '25rem',
159+
data: { team: {} }
160+
});
161+
});
162+
163+
it('should refresh teams after creating new team', () => {
164+
fixture.detectChanges();
165+
const newTeam = { id: '4', name: 'New Team', description: 'New team description' };
166+
const dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['afterClosed']);
167+
dialogRefSpy.afterClosed.and.returnValue(of(newTeam));
168+
mockDialog.open.and.returnValue(dialogRefSpy);
169+
mockTeamsService.getTeams.calls.reset();
170+
171+
component.newTeam();
172+
173+
expect(mockTeamsService.getTeams).toHaveBeenCalled();
174+
});
175+
176+
it('should not refresh teams if dialog is cancelled', () => {
177+
fixture.detectChanges();
178+
const dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['afterClosed']);
179+
dialogRefSpy.afterClosed.and.returnValue(of(null));
180+
mockDialog.open.and.returnValue(dialogRefSpy);
181+
mockTeamsService.getTeams.calls.reset();
182+
183+
component.newTeam();
184+
185+
expect(mockTeamsService.getTeams).not.toHaveBeenCalled();
186+
});
187+
188+
it('should handle empty teams response', () => {
189+
mockTeamsService.getTeams.and.returnValue(of([]));
190+
191+
fixture.detectChanges();
192+
193+
expect(component.teams).toEqual([]);
194+
expect(component.totalTeams).toBe(0);
195+
expect(component.dataSource.data).toEqual([]);
196+
});
197+
198+
it('should display team names and descriptions in table', () => {
199+
fixture.detectChanges();
200+
201+
const compiled = fixture.nativeElement;
202+
const tableRows = compiled.querySelectorAll('tr.mat-row');
203+
204+
expect(tableRows.length).toBe(mockTeams.length);
205+
expect(compiled.textContent).toContain('Team Alpha');
206+
expect(compiled.textContent).toContain('First team description');
207+
});
208+
209+
it('should show table headers', () => {
210+
fixture.detectChanges();
211+
212+
const compiled = fixture.nativeElement;
213+
const headers = compiled.querySelectorAll('th.mat-header-cell');
214+
expect(headers.length).toBe(2);
215+
expect(headers[0].textContent).toContain('Name');
216+
expect(headers[1].textContent).toContain('Description');
217+
});
218+
219+
it('should add title attribute for description tooltips', () => {
220+
fixture.detectChanges();
221+
222+
const compiled = fixture.nativeElement;
223+
const descriptionCells = compiled.querySelectorAll('.description-cell');
224+
225+
expect(descriptionCells[0].getAttribute('title')).toBe('First team description');
226+
expect(descriptionCells[1].getAttribute('title')).toBe('Second team description with much longer text that might wrap');
227+
});
228+
229+
it('should cleanup subscriptions on destroy', () => {
230+
spyOn(component['destroy$'], 'next');
231+
spyOn(component['destroy$'], 'complete');
232+
233+
component.ngOnDestroy();
234+
235+
expect(component['destroy$'].next).toHaveBeenCalled();
236+
expect(component['destroy$'].complete).toHaveBeenCalled();
237+
});
23238
});

web-app/admin/src/app/admin/admin-teams/team.d.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ export interface Team {
44
id: ObjectId;
55
name: string;
66
description: string;
7-
teamEventId: number;
8-
userIds: ObjectId[];
9-
acl: Records<ObjectId, string>;
10-
permissions: string[];
7+
teamEventId: number | string;
8+
userIds?: ObjectId[];
9+
acl?: Records<ObjectId, string>;
10+
permissions?: string[];
1111
}

web-app/admin/src/app/app.module.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ import { AdminEventFormPreviewComponent } from './admin/admin-event/admin-event-
134134
import { AdminEventFormPreviewDialogComponent } from './admin/admin-event/admin-event-form/admin-event-form-preview/admin-event-form-preview-dialog.component';
135135
import { AdminMapComponent } from './admin/admin-map/admin-map.component';
136136
import { AdminTeamsModule } from './admin/admin-teams/admin-teams.module';
137-
import { PaginationTableComponent } from './core/pagination-table/pagination-table.component';
138137

139138
@NgModule({
140139
declarations: [
@@ -189,8 +188,7 @@ import { PaginationTableComponent } from './core/pagination-table/pagination-tab
189188
AdminSettingsUnsavedComponent,
190189
AdminEventFormPreviewComponent,
191190
AdminEventFormPreviewDialogComponent,
192-
AdminMapComponent,
193-
PaginationTableComponent
191+
AdminMapComponent
194192
],
195193
imports: [
196194
CommonModule,

web-app/admin/src/app/core/pagination-table/pagination-table.component.html

Lines changed: 0 additions & 1 deletion
This file was deleted.

web-app/admin/src/app/core/pagination-table/pagination-table.component.scss

Whitespace-only changes.

0 commit comments

Comments
 (0)