diff --git a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.html b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.html index 2c814fc98a5..15b51047dd3 100644 --- a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.html +++ b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.html @@ -13,7 +13,12 @@

Students without a tea

Team {{ team.workgroupId }}

- Change Period No students @@ -39,7 +44,11 @@

Team {{ team.workgroupId } (cdkDragExited)="dragExit($event)" [cdkDragPreviewContainer]="'parent'" > - + diff --git a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.spec.ts b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.spec.ts index d04a992952e..a3614cd02b5 100644 --- a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.spec.ts +++ b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.spec.ts @@ -1,75 +1,108 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatCardModule } from '@angular/material/card'; -import { MatDialog } from '@angular/material/dialog'; +import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { By } from '@angular/platform-browser'; import { UpdateWorkgroupService } from '../../../../../../app/services/updateWorkgroupService'; import { ConfigService } from '../../../../services/configService'; import { ManageTeamComponent } from './manage-team.component'; import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { ManageTeamHarness } from './manage-team.harness'; +import { ManageStudentsModule } from '../manage-students.module'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of } from 'rxjs'; +import { RemoveUserConfirmDialogComponent } from '../remove-user-confirm-dialog/remove-user-confirm-dialog.component'; +import { HttpClient } from '@angular/common/http'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -class ConfigServiceStub { - getPermissions() {} - retrieveConfig() { - return {}; - } -} - -class UpdateWorkgroupServiceStub {} - +let component: ManageTeamComponent; let configService: ConfigService; +let dialog: MatDialog; let fixture: ComponentFixture; -let component: ManageTeamComponent; +let getPermissionsSpy: jasmine.Spy; +let http: HttpClient; +let manageTeamHarness: ManageTeamHarness; +const studentName = 'a a'; +const studentUsername = 'aa0101'; describe('ManageTeamComponent', () => { - beforeEach(() => { + beforeEach(async () => { TestBed.configureTestingModule({ declarations: [ManageTeamComponent], - imports: [MatSnackBarModule, MatCardModule], - providers: [ - { provide: ConfigService, useClass: ConfigServiceStub }, - { provide: UpdateWorkgroupService, useClass: UpdateWorkgroupServiceStub }, - { provide: MatDialog, useValue: {} } + imports: [ + BrowserAnimationsModule, + HttpClientTestingModule, + ManageStudentsModule, + MatCardModule, + MatDialogModule, + MatSnackBarModule ], + providers: [ConfigService, UpdateWorkgroupService], schemas: [NO_ERRORS_SCHEMA] }); configService = TestBed.inject(ConfigService); + dialog = TestBed.inject(MatDialog); fixture = TestBed.createComponent(ManageTeamComponent); + http = TestBed.inject(HttpClient); + getPermissionsSpy = spyOn(configService, 'getPermissions'); + spyOn(configService, 'getRunId').and.returnValue(1); + spyOnCanGradeStudentWork(true); component = fixture.componentInstance; - component.team = { workgroupId: 3, users: [{ id: 1 }] }; + component.team = { + workgroupId: 10, + users: [{ id: 1, name: studentName, username: studentUsername }] + }; + manageTeamHarness = await TestbedHarnessEnvironment.harnessForFixture( + fixture, + ManageTeamHarness + ); }); - changePeriodLinkVisible(); + changePeriodLinkVisibility(); + removeStudent(); }); -function changePeriodLinkVisible() { +function spyOnCanGradeStudentWork(canGrade: boolean) { + getPermissionsSpy.and.returnValue({ + canGradeStudentWork: canGrade, + canViewStudentNames: true, + canAuthorProject: true + }); +} + +function changePeriodLinkVisibility() { describe('change period link', () => { - it('should appear when user has GradeStudentWork permission', () => { - spyOnCanGradeStudentWork(true); - fixture.detectChanges(); - expect(getChangePeriodLink()).toBeTruthy(); + describe('teacher has GradeStudentWork permission', () => { + it('makes change period link visible', async () => { + expect(await manageTeamHarness.isChangePeriodLinkVisible()).toBeTrue(); + }); }); - it('should not appear when user does not have GradeStudentWork permission', () => { - spyOnCanGradeStudentWork(false); - fixture.detectChanges(); - expect(getChangePeriodLink()).toBeFalsy(); + describe('teacher does not have GradeStudentWork permission', () => { + it('makes change period link not visible', async () => { + spyOnCanGradeStudentWork(false); + component.ngOnInit(); + expect(await manageTeamHarness.isChangePeriodLinkVisible()).toBeFalse(); + }); }); - it('should not appear when there are no members', () => { - component.team.users = []; - spyOnCanGradeStudentWork(true); - fixture.detectChanges(); - expect(getChangePeriodLink()).toBeFalsy(); + describe('team has no members', () => { + it('makes change period link not visible', async () => { + component.team.users = []; + expect(await manageTeamHarness.isChangePeriodLinkVisible()).toBeFalse(); + }); }); }); } -function spyOnCanGradeStudentWork(canGrade: boolean) { - spyOn(configService, 'getPermissions').and.returnValue({ - canGradeStudentWork: canGrade, - canViewStudentNames: true, - canAuthorProject: true +function removeStudent() { + describe('removeStudent()', () => { + describe('click remove student button on a student', () => { + it('removes student from the team', async () => { + spyOn(dialog, 'open').and.returnValue({ + afterClosed: () => of(true) + } as MatDialogRef); + spyOn(http, 'delete').and.returnValue(of({})); + await manageTeamHarness.clickRemoveUser(`${studentName} (${studentUsername})`); + expect(await manageTeamHarness.getMemberCount()).toBe(0); + }); + }); }); } - -function getChangePeriodLink() { - return fixture.debugElement.query(By.css('.change-period')); -} diff --git a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.ts b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.ts index 74230f4ecff..8065c646db9 100644 --- a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.ts +++ b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.ts @@ -22,7 +22,7 @@ import { getAvatarColorForWorkgroupId } from '../../../../common/workgroup/workg }) export class ManageTeamComponent { avatarColor: string; - canChangePeriod: boolean; + canGradeStudentWork: boolean; isUnassigned: boolean; @Input() team: any; @@ -35,11 +35,8 @@ export class ManageTeamComponent { ngOnInit() { this.avatarColor = getAvatarColorForWorkgroupId(this.team.workgroupId); + this.canGradeStudentWork = this.configService.getPermissions().canGradeStudentWork; this.isUnassigned = this.team.workgroupId == null; - this.canChangePeriod = - this.configService.getPermissions().canGradeStudentWork && - this.team.users.length > 0 && - !this.isUnassigned; } changePeriod(event: Event) { @@ -110,4 +107,8 @@ export class ManageTeamComponent { } }); } + + protected removeUser(user: any): void { + this.team.users.splice(this.team.users.indexOf(user), 1); + } } diff --git a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.harness.ts b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.harness.ts new file mode 100644 index 00000000000..923c97450c4 --- /dev/null +++ b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.harness.ts @@ -0,0 +1,30 @@ +import { ComponentHarness } from '@angular/cdk/testing'; +import { ManageUserHarness } from '../manage-user/manage-user.harness'; + +export class ManageTeamHarness extends ComponentHarness { + static hostSelector = 'manage-team'; + protected getChangePeriodLink = this.locatorForOptional('.change-period'); + protected getMembers = this.locatorForAll(ManageUserHarness); + + async isChangePeriodLinkVisible(): Promise { + return (await this.getChangePeriodLink()) != null; + } + + async getMember(username: string): Promise { + for (const member of await this.getMembers()) { + if ((await member.getUsername()) === username) { + return member; + } + } + return null; + } + + async clickRemoveUser(username: string): Promise { + const member = await this.getMember(username); + await member.clickRemoveUserButton(); + } + + async getMemberCount(): Promise { + return (await this.getMembers()).length; + } +} diff --git a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.html b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.html index ba07492efc9..5db862850d0 100644 --- a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.html +++ b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.html @@ -38,6 +38,8 @@ matTooltip="Remove student" i18n-matTooltip matTooltipPosition="above" + aria-label="Remove student" + i18n-aria-label (click)="removeUser($event)" > clear diff --git a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.ts b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.ts index d87e1af4bc6..c8141a4f7d0 100644 --- a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.ts +++ b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.ts @@ -1,5 +1,5 @@ import { HttpClient } from '@angular/common/http'; -import { Component, Input, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; import { ConfigService } from '../../../../services/configService'; @@ -15,6 +15,7 @@ import { RemoveUserConfirmDialogComponent } from '../remove-user-confirm-dialog/ }) export class ManageUserComponent { @Input() user: any; + @Output() removeUserEvent: EventEmitter = new EventEmitter(); constructor( private dialog: MatDialog, @@ -49,9 +50,17 @@ export class ManageUserComponent { performRemoveUser() { const runId = this.configService.getRunId(); const studentId = this.user.id; - this.http.delete(`/api/teacher/run/${runId}/student/${studentId}/remove`).subscribe(() => { - this.snackBar.open($localize`Removed ${this.user.name} (${this.user.username}) from unit.`); - this.configService.retrieveConfig(`/api/config/classroomMonitor/${runId}`); + this.http.delete(`/api/teacher/run/${runId}/student/${studentId}/remove`).subscribe({ + next: () => { + this.removeUserEvent.emit(this.user); + this.snackBar.open($localize`Removed ${this.user.name} (${this.user.username}) from unit.`); + this.configService.retrieveConfig(`/api/config/classroomMonitor/${runId}`); + }, + error: () => { + this.snackBar.open( + $localize`Error: Could not remove ${this.user.name} (${this.user.username}) from unit.` + ); + } }); } diff --git a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.harness.ts b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.harness.ts new file mode 100644 index 00000000000..31ed4d2036a --- /dev/null +++ b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.harness.ts @@ -0,0 +1,19 @@ +import { ComponentHarness } from '@angular/cdk/testing'; +import { MatButtonHarness } from '@angular/material/button/testing'; +import { ShowStudentInfoHarness } from '../show-student-info/show-student-info.harness'; + +export class ManageUserHarness extends ComponentHarness { + static hostSelector = 'manage-user'; + protected getStudentInfo = this.locatorForOptional(ShowStudentInfoHarness); + protected getRemoveUserButton = this.locatorFor( + MatButtonHarness.with({ selector: '[aria-label="Remove student"]' }) + ); + + async clickRemoveUserButton(): Promise { + (await this.getRemoveUserButton()).click(); + } + + async getUsername(): Promise { + return await (await this.getStudentInfo()).getUsernameText(); + } +} diff --git a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/show-student-info/show-student-info.harness.ts b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/show-student-info/show-student-info.harness.ts new file mode 100644 index 00000000000..eaeee710e28 --- /dev/null +++ b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/show-student-info/show-student-info.harness.ts @@ -0,0 +1,10 @@ +import { ComponentHarness } from '@angular/cdk/testing'; + +export class ShowStudentInfoHarness extends ComponentHarness { + static hostSelector = 'show-student-info'; + protected getUsername = this.locatorFor('.username'); + + async getUsernameText(): Promise { + return (await this.getUsername()).text(); + } +} diff --git a/src/messages.xlf b/src/messages.xlf index aa83700b406..0acec0acd95 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -12891,7 +12891,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.html - 17 + 22 @@ -12987,21 +12987,21 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.No students src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.html - 19 + 24 Moved student to Team . src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.ts - 106 + 103 Error: Could not move student. src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-team/manage-team.component.ts - 109 + 106 @@ -13031,12 +13031,23 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.html 38 + + src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.html + 41 + Removed () from unit. src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.ts - 53 + 56 + + + + Error: Could not remove () from unit. + + src/assets/wise5/classroomMonitor/classroomMonitorComponents/manageStudents/manage-user/manage-user.component.ts + 61