Skip to content

Commit

Permalink
[SKOOP-93] Mark skills as favorites (#120)
Browse files Browse the repository at this point in the history
* button to mark a skill as favorite/ to remove from favorites

* The field `favorite` renamed to `favourite`.

* The test for `MySkillsEditComponent#saveUserSkill`.
  • Loading branch information
svetivanova authored and dgswan committed Jul 8, 2019
1 parent fb3aee3 commit 9ae454d
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 50 deletions.
46 changes: 31 additions & 15 deletions src/app/my-skills/my-skills-edit.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,7 @@ import { ExternalAssetsService } from '../shared/external-assets.service';
import { StepDescription } from './step-description';
import { SelectedValueTitleDirective } from './selected-value-title.directive';
import { By } from '@angular/platform-browser';

const mySkillsServiceStub: Partial<MySkillsService> = {
updateCurrentUserSkill(skillId: string, currentLevel: number, desiredLevel: number, priority: number):
Observable<UserSkill> { return null; }
};

const bottomSheetStub: Partial<MatBottomSheetRef> = {
dismiss(result?: any): void { }
};

import { UpdateUserSkillRequest } from './update-user-skill-request';

const externalAssetsServiceStub: Partial<ExternalAssetsService> = {
getJSON<T>(filePath: string): Observable<T> {
Expand All @@ -41,7 +32,8 @@ const userSkillTestData: UserSkillView = {
},
currentLevel: 2,
desiredLevel: 3,
priority: 4
priority: 4,
favourite: true
};

const levelDescription: StepDescription = {
Expand All @@ -58,8 +50,6 @@ describe('MySkillsEditComponent', () => {
let externalAssetsServiceSpy;

beforeEach(async(() => {
spyOn(mySkillsServiceStub, 'updateCurrentUserSkill');
spyOn(bottomSheetStub, 'dismiss');
TestBed.configureTestingModule({
imports: [
BrowserAnimationsModule,
Expand All @@ -71,8 +61,18 @@ describe('MySkillsEditComponent', () => {
declarations: [MySkillsEditComponent, SelectedValueTitleDirective],
providers: [
GlobalErrorHandlerService, SelectedValueTitleDirective,
{ provide: MySkillsService, useValue: mySkillsServiceStub },
{ provide: MatBottomSheetRef, useValue: bottomSheetStub },
{ provide: MySkillsService, useValue: jasmine.createSpyObj('mySkillsService', {
'updateCurrentUserSkill': of<UserSkill>(
{
currentLevel: userSkillTestData.currentLevel,
desiredLevel: userSkillTestData.desiredLevel,
priority: 3,
favourite: userSkillTestData.favourite,
skill: userSkillTestData.skill
}
)
}) },
{ provide: MatBottomSheetRef, useValue: jasmine.createSpyObj('matBottomSheetRef', ['dismiss'] ) },
{ provide: MAT_BOTTOM_SHEET_DATA, useValue: userSkillTestData },
{ provide: ExternalAssetsService, useValue: externalAssetsServiceStub }
]
Expand Down Expand Up @@ -127,4 +127,20 @@ describe('MySkillsEditComponent', () => {
expect(label.nativeElement.title).toBe('');
}));

it('should edit user skill', async(() => {
component.priority.setValue(3);
const matBottomSheetRef: MatBottomSheetRef = TestBed.get(MatBottomSheetRef);
component.saveUserSkill();
const mySkillsService: MySkillsService = TestBed.get(MySkillsService);
expect(mySkillsService.updateCurrentUserSkill).toHaveBeenCalledWith(userSkillTestData.skill.id, {
currentLevel: userSkillTestData.currentLevel,
desiredLevel: userSkillTestData.desiredLevel,
priority: 3,
favourite: userSkillTestData.favourite
} as UpdateUserSkillRequest);
fixture.whenStable().then(() => {
expect(matBottomSheetRef.dismiss).toHaveBeenCalledWith(true);
});
}));

});
9 changes: 8 additions & 1 deletion src/app/my-skills/my-skills-edit.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { UserSkillView } from '../shared/skill-card/user-skill-view';
import { StepDescription } from './step-description';
import { ExternalAssetsService } from '../shared/external-assets.service';
import { MySkillsDialogComponentTrait } from './my-skills-dialog-component-trait';
import { UpdateUserSkillRequest } from './update-user-skill-request';

@Component({
selector: 'app-my-skills-edit',
Expand Down Expand Up @@ -49,8 +50,14 @@ export class MySkillsEditComponent extends MySkillsDialogComponentTrait implemen
saveUserSkill(): void {
this.operationInProgress = true;
this.errorMessage = null;
const requestData: UpdateUserSkillRequest = {
currentLevel: this.currentLevel.value,
desiredLevel: this.desiredLevel.value,
priority: this.priority.value,
favourite: this.userSkill.favourite
};
this.mySkillsService.updateCurrentUserSkill(
this.userSkill.skill.id, this.currentLevel.value, this.desiredLevel.value, this.priority.value)
this.userSkill.skill.id, requestData)
.subscribe(() => {
// Return 'true' to indicate that the user skill was changed.
this.bottomSheet.dismiss(true);
Expand Down
6 changes: 4 additions & 2 deletions src/app/my-skills/my-skills-new.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ describe('MySkillsNewComponent', () => {
},
currentLevel: 2,
desiredLevel: 3,
priority: 4
priority: 4,
favourite: false
};
// Spy on service methods used during interaction with the component.
const mySkillsService = TestBed.get(MySkillsService) as MySkillsService;
Expand Down Expand Up @@ -148,7 +149,8 @@ describe('MySkillsNewComponent', () => {
},
currentLevel: 2,
desiredLevel: 3,
priority: 4
priority: 4,
favourite: false
};
// Spy on service methods used during interaction with the component.
const mySkillsService = TestBed.get(MySkillsService) as MySkillsService;
Expand Down
10 changes: 10 additions & 0 deletions src/app/my-skills/my-skills.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ <h1>My skill profile</h1>

<app-skill-card *ngFor="let userSkill of userSkills" [userSkill]="userSkill">
<div fxFlex="none" buttonContent>
<button *ngIf="userSkill.favourite; else mark_as_favorite_content" mat-icon-button
(click)="removeFromFavorites(userSkill)" title="Remove from favorites" aria-label="Remove from favorites">
<mat-icon>star</mat-icon>
</button>
<ng-template #mark_as_favorite_content>
<button mat-icon-button (click)="markAsFavorite(userSkill)" title="Mark as favorite"
aria-label="Mark as favorite">
<mat-icon>star_border</mat-icon>
</button>
</ng-template>
<button mat-icon-button (click)="getCoaches(userSkill)" title="Find coaches" aria-label="Find coaches"
class="mySkillsCard__findCoachesButton">
<mat-icon>school</mat-icon>
Expand Down
41 changes: 38 additions & 3 deletions src/app/my-skills/my-skills.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ import { User } from '../users/user';
import { MySkillsComponent } from './my-skills.component';
import { MySkillsService } from './my-skills.service';
import { SkillCardComponent } from '../shared/skill-card/skill-card.component';
import { UpdateUserSkillRequest } from './update-user-skill-request';

// Stub only those methods of the service which are used by the component.
const mySkillsServiceStub: Partial<MySkillsService> = {
getCurrentUserSkills(): Observable<UserSkill[]> { return null; },
getCurrentUserSkillCoaches(skillId: string): Observable<User[]> { return null; },
deleteCurrentUserSkill(skillId: string): Observable<void> { return null; }
deleteCurrentUserSkill(skillId: string): Observable<void> { return null; },
updateCurrentUserSkill(skillId: string, requestData: UpdateUserSkillRequest):
Observable<UserSkill> { return null; }
};

const initialUserSkills: UserSkill[] = [
Expand All @@ -28,7 +31,8 @@ const initialUserSkills: UserSkill[] = [
},
currentLevel: 2,
desiredLevel: 3,
priority: 4
priority: 4,
favourite: false
},
{
skill: {
Expand All @@ -38,7 +42,8 @@ const initialUserSkills: UserSkill[] = [
},
currentLevel: 1,
desiredLevel: 2,
priority: 3
priority: 3,
favourite: false
}
];

Expand Down Expand Up @@ -138,4 +143,34 @@ describe('MySkillsComponent', () => {
expect(coaches.nativeElement.textContent).toContain('Toni Tester (tester)');
expect(coaches.nativeElement.textContent).toContain('Tina Testing (testing)');
}));

it('should mark the skill as favourite', fakeAsync(() => {
const spy = spyOn(TestBed.get(MySkillsService) as MySkillsService, 'updateCurrentUserSkill')
.and.returnValue(of<UserSkill[]>(initialUserSkills));
const expectedRequest: UpdateUserSkillRequest = {
currentLevel: component.userSkills[0].currentLevel,
desiredLevel: component.userSkills[0].desiredLevel,
priority: component.userSkills[0].priority,
favourite: true
};
component.markAsFavorite(component.userSkills[0]);
fixture.detectChanges();

expect(spy).toHaveBeenCalledWith(component.userSkills[0].skill.id, expectedRequest);
}));

it('should remove the skill from the favourites', fakeAsync(() => {
const spy = spyOn(TestBed.get(MySkillsService) as MySkillsService, 'updateCurrentUserSkill')
.and.returnValue(of<UserSkill[]>(initialUserSkills));
const expectedRequest: UpdateUserSkillRequest = {
currentLevel: component.userSkills[0].currentLevel,
desiredLevel: component.userSkills[0].desiredLevel,
priority: component.userSkills[0].priority,
favourite: false
};
component.removeFromFavorites(component.userSkills[0]);
fixture.detectChanges();

expect(spy).toHaveBeenCalledWith(component.userSkills[0].skill.id, expectedRequest);
}));
});
48 changes: 40 additions & 8 deletions src/app/my-skills/my-skills.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { HttpErrorResponse } from '@angular/common/http';
import { GlobalErrorHandlerService } from '../error/global-error-handler.service';
import { DeleteConfirmationDialogComponent } from '../shared/delete-confirmation-dialog/delete-confirmation-dialog.component';
import { MySkillsService } from './my-skills.service';
import { UpdateUserSkillRequest } from './update-user-skill-request';

@Component({
selector: 'app-my-skills',
Expand Down Expand Up @@ -61,7 +62,8 @@ export class MySkillsComponent implements OnInit {
},
currentLevel: userSkill.currentLevel,
desiredLevel: userSkill.desiredLevel,
priority: userSkill.priority
priority: userSkill.priority,
favourite: userSkill.favourite
}
}).afterDismissed().pipe(filter(Boolean)).subscribe(() => this.loadUserSkills());
}
Expand All @@ -77,14 +79,22 @@ export class MySkillsComponent implements OnInit {
.subscribe(() => {
this.loadUserSkills();
}, (errorResponse: HttpErrorResponse) => {
this.errorMessage = this.globalErrorHandlerService.createFullMessage(errorResponse);
// Dirty fix because of: https://github.com/angular/angular/issues/17772
this.changeDetector.markForCheck();
this.handleErrorResponse(errorResponse);
});
}
});
}

markAsFavorite(userSkill: UserSkillView) {
userSkill.favourite = true;
this.editUserSkill(userSkill);
}

removeFromFavorites(userSkill: UserSkillView) {
userSkill.favourite = false;
this.editUserSkill(userSkill);
}

private loadUserSkills(): void {
this.mySkillsService.getCurrentUserSkills()
.pipe(map(userSkills => userSkills.map<UserSkillView>(userSkill => ({
Expand All @@ -94,7 +104,8 @@ export class MySkillsComponent implements OnInit {
},
currentLevel: userSkill.currentLevel,
desiredLevel: userSkill.desiredLevel,
priority: userSkill.priority
priority: userSkill.priority,
favourite: userSkill.favourite
}))))
.subscribe(userSkills => {
this.userSkills = userSkills.sort((a, b) => {
Expand All @@ -104,18 +115,39 @@ export class MySkillsComponent implements OnInit {
return a.skill.name.toLocaleLowerCase().localeCompare(b.skill.name.toLocaleLowerCase());
});
}, (errorResponse: HttpErrorResponse) => {
this.errorMessage = this.globalErrorHandlerService.createFullMessage(errorResponse);
// Dirty fix because of: https://github.com/angular/angular/issues/17772
this.changeDetector.markForCheck();
this.handleErrorResponse(errorResponse);
});
}

private editUserSkill(userSkill: UserSkillView) {
const requestData: UpdateUserSkillRequest = {
currentLevel: userSkill.currentLevel,
desiredLevel: userSkill.desiredLevel,
priority: userSkill.priority,
favourite: userSkill.favourite
};
this.mySkillsService.updateCurrentUserSkill(
userSkill.skill.id, requestData)
.subscribe(() => {
this.loadUserSkills();
}, (errorResponse: HttpErrorResponse) => {
this.handleErrorResponse(errorResponse);
});
}

private handleErrorResponse(errorResponse: HttpErrorResponse) {
this.errorMessage = this.globalErrorHandlerService.createFullMessage(errorResponse);
// Dirty fix because of: https://github.com/angular/angular/issues/17772
this.changeDetector.markForCheck();
}
}

interface UserSkillView {
skill: SkillView;
currentLevel: number;
desiredLevel: number;
priority: number;
favourite: boolean;
coaches?: UserView[];
}

Expand Down
5 changes: 3 additions & 2 deletions src/app/my-skills/my-skills.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { UserSkill } from '../user-skills/user-skill';
import { UserSkillsService } from '../user-skills/user-skills.service';
import { UserIdentityService } from '../shared/user-identity.service';
import { User } from '../users/user';
import { UpdateUserSkillRequest } from './update-user-skill-request';

@Injectable({
providedIn: 'root'
Expand Down Expand Up @@ -36,10 +37,10 @@ export class MySkillsService {
userIdentity.userId, skillName, currentLevel, desiredLevel, priority)));
}

updateCurrentUserSkill(skillId: string, currentLevel: number, desiredLevel: number, priority: number): Observable<UserSkill> {
updateCurrentUserSkill(skillId: string, requestData: UpdateUserSkillRequest): Observable<UserSkill> {
return this.userIdentityService.getUserIdentity()
.pipe(switchMap(userIdentity => this.userSkillsService.updateUserSkill(
userIdentity.userId, skillId, currentLevel, desiredLevel, priority)));
userIdentity.userId, skillId, requestData)));
}

deleteCurrentUserSkill(skillId: string): Observable<void> {
Expand Down
6 changes: 6 additions & 0 deletions src/app/my-skills/update-user-skill-request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface UpdateUserSkillRequest {
currentLevel: number;
desiredLevel: number;
priority: number;
favourite: boolean;
}
1 change: 1 addition & 0 deletions src/app/shared/skill-card/user-skill-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export interface UserSkillView {
currentLevel: number;
desiredLevel: number;
priority: number;
favourite?: boolean;
}
1 change: 1 addition & 0 deletions src/app/user-skills/user-skill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export interface UserSkill {
currentLevel: number;
desiredLevel: number;
priority: number;
favourite?: boolean;
}
Loading

0 comments on commit 9ae454d

Please sign in to comment.