Commits
Tim Donohue authored 64f968b2467
1 1 | import { CommonModule } from '@angular/common'; |
2 2 | import { NO_ERRORS_SCHEMA, DebugElement } from '@angular/core'; |
3 - | import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick, waitForAsync } from '@angular/core/testing'; |
3 + | import { ComponentFixture, fakeAsync, flush, inject, TestBed, waitForAsync } from '@angular/core/testing'; |
4 4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; |
5 5 | import { BrowserModule, By } from '@angular/platform-browser'; |
6 6 | import { Router } from '@angular/router'; |
7 7 | import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; |
8 8 | import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; |
9 - | import { Observable, of as observableOf, BehaviorSubject } from 'rxjs'; |
9 + | import { Observable, of as observableOf } from 'rxjs'; |
10 10 | import { RestResponse } from '../../../../core/cache/response.models'; |
11 11 | import { buildPaginatedList, PaginatedList } from '../../../../core/data/paginated-list.model'; |
12 12 | import { RemoteData } from '../../../../core/data/remote-data'; |
13 13 | import { GroupDataService } from '../../../../core/eperson/group-data.service'; |
14 14 | import { Group } from '../../../../core/eperson/models/group.model'; |
15 15 | import { PageInfo } from '../../../../core/shared/page-info.model'; |
16 16 | import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; |
17 17 | import { NotificationsService } from '../../../../shared/notifications/notifications.service'; |
18 18 | import { GroupMock, GroupMock2 } from '../../../../shared/testing/group-mock'; |
19 19 | import { SubgroupsListComponent } from './subgroups-list.component'; |
20 20 | import { |
21 - | createSuccessfulRemoteDataObject$, |
22 - | createSuccessfulRemoteDataObject |
21 + | createSuccessfulRemoteDataObject$ |
23 22 | } from '../../../../shared/remote-data.utils'; |
24 23 | import { RouterMock } from '../../../../shared/mocks/router.mock'; |
25 24 | import { getMockFormBuilderService } from '../../../../shared/mocks/form-builder-service.mock'; |
26 25 | import { getMockTranslateService } from '../../../../shared/mocks/translate.service.mock'; |
27 26 | import { TranslateLoaderMock } from '../../../../shared/testing/translate-loader.mock'; |
28 27 | import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub'; |
29 - | import { map } from 'rxjs/operators'; |
30 28 | import { PaginationService } from '../../../../core/pagination/pagination.service'; |
31 29 | import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; |
32 30 | import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; |
33 31 | import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; |
32 + | import { EPersonMock2 } from 'src/app/shared/testing/eperson.mock'; |
34 33 | |
35 34 | describe('SubgroupsListComponent', () => { |
36 35 | let component: SubgroupsListComponent; |
37 36 | let fixture: ComponentFixture<SubgroupsListComponent>; |
38 37 | let translateService: TranslateService; |
39 38 | let builderService: FormBuilderService; |
40 39 | let ePersonDataServiceStub: any; |
41 40 | let groupsDataServiceStub: any; |
42 - | let activeGroup; |
41 + | let activeGroup: Group; |
43 42 | let subgroups: Group[]; |
44 - | let allGroups: Group[]; |
43 + | let groupNonMembers: Group[]; |
45 44 | let routerStub; |
46 45 | let paginationService; |
46 + | // Define a new mock activegroup for all tests below |
47 + | let mockActiveGroup: Group = Object.assign(new Group(), { |
48 + | handle: null, |
49 + | subgroups: [GroupMock2], |
50 + | epersons: [EPersonMock2], |
51 + | selfRegistered: false, |
52 + | permanent: false, |
53 + | _links: { |
54 + | self: { |
55 + | href: 'https://rest.api/server/api/eperson/groups/activegroupid', |
56 + | }, |
57 + | subgroups: { href: 'https://rest.api/server/api/eperson/groups/activegroupid/subgroups' }, |
58 + | object: { href: 'https://rest.api/server/api/eperson/groups/activegroupid/object' }, |
59 + | epersons: { href: 'https://rest.api/server/api/eperson/groups/activegroupid/epersons' } |
60 + | }, |
61 + | _name: 'activegroupname', |
62 + | id: 'activegroupid', |
63 + | uuid: 'activegroupid', |
64 + | type: 'group', |
65 + | }); |
47 66 | |
48 67 | beforeEach(waitForAsync(() => { |
49 - | activeGroup = GroupMock; |
68 + | activeGroup = mockActiveGroup; |
50 69 | subgroups = [GroupMock2]; |
51 - | allGroups = [GroupMock, GroupMock2]; |
70 + | groupNonMembers = [GroupMock]; |
52 71 | ePersonDataServiceStub = {}; |
53 72 | groupsDataServiceStub = { |
54 73 | activeGroup: activeGroup, |
55 - | subgroups$: new BehaviorSubject(subgroups), |
74 + | subgroups: subgroups, |
75 + | groupNonMembers: groupNonMembers, |
56 76 | getActiveGroup(): Observable<Group> { |
57 77 | return observableOf(this.activeGroup); |
58 78 | }, |
59 79 | getSubgroups(): Group { |
60 - | return this.activeGroup; |
80 + | return this.subgroups; |
61 81 | }, |
82 + | // This method is used to get all the current subgroups |
62 83 | findListByHref(_href: string): Observable<RemoteData<PaginatedList<Group>>> { |
63 - | return this.subgroups$.pipe( |
64 - | map((currentGroups: Group[]) => { |
65 - | return createSuccessfulRemoteDataObject(buildPaginatedList<Group>(new PageInfo(), currentGroups)); |
66 - | }) |
67 - | ); |
84 + | return createSuccessfulRemoteDataObject$(buildPaginatedList<Group>(new PageInfo(), groupsDataServiceStub.getSubgroups())); |
68 85 | }, |
69 86 | getGroupEditPageRouterLink(group: Group): string { |
70 87 | return '/access-control/groups/' + group.id; |
71 88 | }, |
89 + | // This method is used to get all groups which are NOT currently a subgroup member |
72 90 | searchGroups(query: string): Observable<RemoteData<PaginatedList<Group>>> { |
73 91 | if (query === '') { |
74 - | return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), allGroups)); |
92 + | return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), groupNonMembers)); |
75 93 | } |
76 94 | return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); |
77 95 | }, |
78 - | addSubGroupToGroup(parentGroup, subgroup: Group): Observable<RestResponse> { |
79 - | this.subgroups$.next([this.subgroups$.getValue(), subgroup]); |
96 + | addSubGroupToGroup(parentGroup, subgroupToAdd: Group): Observable<RestResponse> { |
97 + | // Add group to list of subgroups |
98 + | this.subgroups = [this.subgroups, subgroupToAdd]; |
99 + | // Remove group from list of non-members |
100 + | this.groupNonMembers.forEach( (group: Group, index: number) => { |
101 + | if (group.id === subgroupToAdd.id) { |
102 + | this.groupNonMembers.splice(index, 1); |
103 + | } |
104 + | }); |
80 105 | return observableOf(new RestResponse(true, 200, 'Success')); |
81 106 | }, |
82 107 | clearGroupsRequests() { |
83 108 | // empty |
84 109 | }, |
85 110 | clearGroupLinkRequests() { |
86 111 | // empty |
87 112 | }, |
88 - | deleteSubGroupFromGroup(parentGroup, subgroup: Group): Observable<RestResponse> { |
89 - | this.subgroups$.next(this.subgroups$.getValue().filter((group: Group) => { |
90 - | if (group.id !== subgroup.id) { |
91 - | return group; |
113 + | deleteSubGroupFromGroup(parentGroup, subgroupToDelete: Group): Observable<RestResponse> { |
114 + | // Remove group from list of subgroups |
115 + | this.subgroups.forEach( (group: Group, index: number) => { |
116 + | if (group.id === subgroupToDelete.id) { |
117 + | this.subgroups.splice(index, 1); |
92 118 | } |
93 - | })); |
119 + | }); |
120 + | // Add group to list of non-members |
121 + | this.groupNonMembers = [this.groupNonMembers, subgroupToDelete]; |
94 122 | return observableOf(new RestResponse(true, 200, 'Success')); |
95 123 | } |
96 124 | }; |
97 125 | routerStub = new RouterMock(); |
98 126 | builderService = getMockFormBuilderService(); |
99 127 | translateService = getMockTranslateService(); |
100 128 | |
101 129 | paginationService = new PaginationServiceStub(); |
102 - | TestBed.configureTestingModule({ |
130 + | return TestBed.configureTestingModule({ |
103 131 | imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, |
104 132 | TranslateModule.forRoot({ |
105 133 | loader: { |
106 134 | provide: TranslateLoader, |
107 135 | useClass: TranslateLoaderMock |
108 136 | } |
109 137 | }), |
110 138 | ], |
111 139 | declarations: [SubgroupsListComponent], |
112 140 | providers: [SubgroupsListComponent, |
130 158 | fixture.destroy(); |
131 159 | fixture.debugElement.nativeElement.remove(); |
132 160 | flush(); |
133 161 | component = null; |
134 162 | })); |
135 163 | |
136 164 | it('should create SubgroupsListComponent', inject([SubgroupsListComponent], (comp: SubgroupsListComponent) => { |
137 165 | expect(comp).toBeDefined(); |
138 166 | })); |
139 167 | |
140 - | it('should show list of subgroups of current active group', () => { |
141 - | const groupIdsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tr td:first-child')); |
142 - | expect(groupIdsFound.length).toEqual(1); |
143 - | activeGroup.subgroups.map((group: Group) => { |
144 - | expect(groupIdsFound.find((foundEl) => { |
145 - | return (foundEl.nativeElement.textContent.trim() === group.uuid); |
146 - | })).toBeTruthy(); |
168 + | describe('current subgroup list', () => { |
169 + | it('should show list of subgroups of current active group', () => { |
170 + | const groupIdsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tr td:first-child')); |
171 + | expect(groupIdsFound.length).toEqual(1); |
172 + | subgroups.map((group: Group) => { |
173 + | expect(groupIdsFound.find((foundEl) => { |
174 + | return (foundEl.nativeElement.textContent.trim() === group.uuid); |
175 + | })).toBeTruthy(); |
176 + | }); |
147 177 | }); |
148 - | }); |
149 178 | |
150 - | describe('if first group delete button is pressed', () => { |
151 - | let groupsFound: DebugElement[]; |
152 - | beforeEach(fakeAsync(() => { |
153 - | const addButton = fixture.debugElement.query(By.css('#subgroupsOfGroup tbody .deleteButton')); |
154 - | addButton.triggerEventHandler('click', { |
155 - | preventDefault: () => {/**/ |
156 - | } |
179 + | it('should show a delete button next to each subgroup', () => { |
180 + | const subgroupsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tbody tr')); |
181 + | subgroupsFound.map((foundGroupRowElement: DebugElement) => { |
182 + | const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus')); |
183 + | const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt')); |
184 + | expect(addButton).toBeNull(); |
185 + | expect(deleteButton).not.toBeNull(); |
186 + | }); |
187 + | }); |
188 + | |
189 + | describe('if first group delete button is pressed', () => { |
190 + | let groupsFound: DebugElement[]; |
191 + | beforeEach(() => { |
192 + | const deleteButton = fixture.debugElement.query(By.css('#subgroupsOfGroup tbody .deleteButton')); |
193 + | deleteButton.nativeElement.click(); |
194 + | fixture.detectChanges(); |
195 + | }); |
196 + | it('then no subgroup remains as a member of the active group', () => { |
197 + | groupsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tbody tr')); |
198 + | expect(groupsFound.length).toEqual(0); |
157 199 | }); |
158 - | tick(); |
159 - | fixture.detectChanges(); |
160 - | })); |
161 - | it('one less subgroup in list from 1 to 0 (of 2 total groups)', () => { |
162 - | groupsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tbody tr')); |
163 - | expect(groupsFound.length).toEqual(0); |
164 200 | }); |
165 201 | }); |
166 202 | |
167 203 | describe('search', () => { |
168 204 | describe('when searching with empty query', () => { |
169 205 | let groupsFound: DebugElement[]; |
170 206 | beforeEach(fakeAsync(() => { |
171 207 | component.search({ query: '' }); |
208 + | fixture.detectChanges(); |
172 209 | groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr')); |
173 210 | })); |
174 211 | |
175 - | it('should display all groups', () => { |
176 - | fixture.detectChanges(); |
177 - | groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr')); |
178 - | expect(groupsFound.length).toEqual(2); |
179 - | groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr')); |
212 + | it('should display only non-member groups (i.e. groups that are not a subgroup)', () => { |
180 213 | const groupIdsFound: DebugElement[] = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr td:first-child')); |
181 - | allGroups.map((group: Group) => { |
214 + | expect(groupIdsFound.length).toEqual(1); |
215 + | groupNonMembers.map((group: Group) => { |
182 216 | expect(groupIdsFound.find((foundEl: DebugElement) => { |
183 217 | return (foundEl.nativeElement.textContent.trim() === group.uuid); |
184 218 | })).toBeTruthy(); |
185 219 | }); |
186 220 | }); |
187 221 | |
188 - | describe('if group is already a subgroup', () => { |
189 - | it('should have delete button, else it should have add button', () => { |
222 + | it('should display an add button next to non-member groups, not a delete button', () => { |
223 + | groupsFound.map((foundGroupRowElement: DebugElement) => { |
224 + | const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus')); |
225 + | const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt')); |
226 + | expect(addButton).not.toBeNull(); |
227 + | expect(deleteButton).toBeNull(); |
228 + | }); |
229 + | }); |
230 + | |
231 + | describe('if first add button is pressed', () => { |
232 + | beforeEach(() => { |
233 + | const addButton: DebugElement = fixture.debugElement.query(By.css('#groupsSearch tbody .fa-plus')); |
234 + | addButton.nativeElement.click(); |
190 235 | fixture.detectChanges(); |
236 + | }); |
237 + | it('then all (two) Groups are subgroups of the active group. No non-members left', () => { |
191 238 | groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr')); |
192 - | const getSubgroups = groupsDataServiceStub.getSubgroups().subgroups; |
193 - | if (getSubgroups !== undefined && getSubgroups.length > 0) { |
194 - | groupsFound.map((foundGroupRowElement: DebugElement) => { |
195 - | const groupId: DebugElement = foundGroupRowElement.query(By.css('td:first-child')); |
196 - | const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus')); |
197 - | const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt')); |
198 - | expect(addButton).toBeNull(); |
199 - | if (activeGroup.id === groupId.nativeElement.textContent) { |
200 - | expect(deleteButton).toBeNull(); |
201 - | } else { |
202 - | expect(deleteButton).not.toBeNull(); |
203 - | } |
204 - | }); |
205 - | } else { |
206 - | const subgroupIds: string[] = activeGroup.subgroups.map((group: Group) => group.id); |
207 - | groupsFound.map((foundGroupRowElement: DebugElement) => { |
208 - | const groupId: DebugElement = foundGroupRowElement.query(By.css('td:first-child')); |
209 - | const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus')); |
210 - | const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt')); |
211 - | if (subgroupIds.includes(groupId.nativeElement.textContent)) { |
212 - | expect(addButton).toBeNull(); |
213 - | expect(deleteButton).not.toBeNull(); |
214 - | } else { |
215 - | expect(deleteButton).toBeNull(); |
216 - | expect(addButton).not.toBeNull(); |
217 - | } |
218 - | }); |
219 - | } |
239 + | expect(groupsFound.length).toEqual(0); |
220 240 | }); |
221 241 | }); |
222 242 | }); |
223 243 | }); |
224 244 | |
225 245 | }); |