Skip to content

Commit

Permalink
Merge pull request #6328 from NMDSdevopsServiceAdm/feat/1468-main-job…
Browse files Browse the repository at this point in the history
…-page

Feat/1468 main job page
  • Loading branch information
kapppa-joe authored Aug 22, 2024
2 parents 8364f81 + 5717dec commit df5ace4
Show file tree
Hide file tree
Showing 11 changed files with 383 additions and 237 deletions.
19 changes: 19 additions & 0 deletions frontend/src/app/core/services/worker.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Worker, WorkerEditResponse, WorkersResponse } from '@core/model/worker.
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { environment } from 'src/environments/environment';
import { Contracts } from '@core/model/contracts.enum';

export interface Reason {
id: number;
Expand All @@ -31,6 +32,11 @@ interface TotalStaffRecordsResponse {
total: number;
}

export interface NewWorkerMandatoryInfo {
nameOrId: string;
contract: Contracts;
}

@Injectable({
providedIn: 'root',
})
Expand All @@ -44,6 +50,7 @@ export class WorkerService {
public worker$ = this._worker$.asObservable();
public getRoute$: BehaviorSubject<any> = new BehaviorSubject<any>(null);
public createStaffResponse = null;
private _newWorkerMandatoryInfo: NewWorkerMandatoryInfo = null;

private _workers$: BehaviorSubject<Worker[]> = new BehaviorSubject<Worker[]>(null);
public workers$: Observable<Worker[]> = this._workers$.asObservable();
Expand Down Expand Up @@ -290,4 +297,16 @@ export class WorkerService {
public getLongTermAbsenceReasons(): Observable<Array<string>> {
return this.http.get<any>(`${environment.appRunnerEndpoint}/api/longTermAbsence`).pipe(map((res) => res.reasons));
}

public setNewWorkerMandatoryInfo(nameOrId: string, contract: Contracts): void {
this._newWorkerMandatoryInfo = { nameOrId, contract };
}

public get newWorkerMandatoryInfo(): NewWorkerMandatoryInfo {
return this._newWorkerMandatoryInfo;
}

public clearNewWorkerMandatoryInfo(): void {
this._newWorkerMandatoryInfo = null;
}
}
3 changes: 2 additions & 1 deletion frontend/src/app/features/wdf/wdf-data-change/wdf.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { WdfStaffRecordComponent } from './wdf-staff-record/wdf-staff-record.com
import { WdfStaffSummaryComponent } from './wdf-staff-summary/wdf-staff-summary.component';
import { WdfWorkplacesSummaryTableComponent } from './wdf-workplaces-summary-table/wdf-workplaces-summary-table.component';
import { WdfWorkplacesSummaryComponent } from './wdf-workplaces-summary/wdf-workplaces-summary.component';
import { JobsResolver } from '@core/resolvers/jobs.resolver';

@NgModule({
imports: [CommonModule, ReactiveFormsModule, SharedModule, OverlayModule, WdfRoutingModule],
Expand All @@ -34,6 +35,6 @@ import { WdfWorkplacesSummaryComponent } from './wdf-workplaces-summary/wdf-work
WdfParentStatusMessageComponent,
WdfOverviewComponent,
],
providers: [WorkerResolver, WorkplaceResolver],
providers: [WorkerResolver, WorkplaceResolver, JobsResolver],
})
export class WdfModule {}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<app-error-summary *ngIf="submitted && form.invalid" [formErrorsMap]="formErrorsMap" [form]="form"></app-error-summary>

<form #formEl (ngSubmit)="onSubmit()" [formGroup]="form">
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
Expand All @@ -16,8 +18,20 @@ <h1 class="govuk-fieldset__heading">{{ worker ? 'Update' : 'Select' }} their mai
></app-grouped-radio-button-accordion>
</fieldset>
</div>
<div class="govuk-grid-column-one-third" *ngIf="!editFlow">
<div class="govuk-!-margin-left-8">
<app-progress-bar
data-testid="progress-bar-1"
[sections]="staffRecordSections"
[currentSection]="'Mandatory information'"
></app-progress-bar>
</div>
</div>
</div>
<app-submit-button
[summaryContinue]="summaryContinue"
overrideCallToActionLabel="true"
[callToAction]="callToActionLabel"
[recordSummary]="false"
[canExit]="canExit"
[return]="editFlow"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms';
import { ProgressBarComponent } from '@shared/components/progress-bar/progress-bar.component';
import { WorkerService } from '@core/services/worker.service';
import { MockWorkerServiceWithUpdateWorker } from '@core/test-utils/MockWorkerService';
import { MockWorkerServiceWithUpdateWorker, workerBuilder } from '@core/test-utils/MockWorkerService';
import { WindowRef } from '@core/services/window.ref';
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { SharedModule } from '@shared/shared.module';
Expand All @@ -17,10 +17,13 @@ import { HttpClient } from '@angular/common/http';
import { UserService } from '@core/services/user.service';
import { MockUserService } from '@core/test-utils/MockUserService';
import { Roles } from '@core/model/roles.enum';
import { Contracts } from '@core/model/contracts.enum';
import { Worker } from '@core/model/worker.model';
import userEvent from '@testing-library/user-event';
import { AlertService } from '@core/services/alert.service';

describe('MainJobRoleComponent', () => {
async function setup(insideFlow = true, returnToMandatoryDetails = false) {
async function setup(insideFlow = true, returnToMandatoryDetails = false, addNewWorker = false) {
let path;
if (returnToMandatoryDetails) {
path = 'mandatory-details';
Expand All @@ -29,12 +32,13 @@ describe('MainJobRoleComponent', () => {
} else {
path = 'staff-record-summary';
}
const { fixture, getByText, getByTestId, getByLabelText } = await render(MainJobRoleComponent, {
const { fixture, getByText, getByTestId, getByLabelText, queryByTestId } = await render(MainJobRoleComponent, {
imports: [SharedModule, RouterModule, RouterTestingModule, HttpClientTestingModule, ReactiveFormsModule],
declarations: [ProgressBarComponent],
schemas: [NO_ERRORS_SCHEMA],
providers: [
UntypedFormBuilder,
AlertService,
WindowRef,
{
provide: PermissionsService,
Expand Down Expand Up @@ -76,6 +80,16 @@ describe('MainJobRoleComponent', () => {
jobRoleGroup: 'Care providing roles',
title: 'Care worker',
},
{
id: 23,
title: 'Registered nurse',
jobRoleGroup: 'Professional and related roles',
},
{
id: 27,
title: 'Social worker',
jobRoleGroup: 'Professional and related roles',
},
],
},
},
Expand All @@ -88,20 +102,41 @@ describe('MainJobRoleComponent', () => {
const injector = getTestBed();
const router = injector.inject(Router) as Router;
const workerService = injector.inject(WorkerService) as WorkerService;
const alertService = injector.inject(AlertService) as AlertService;

const routerSpy = spyOn(router, 'navigate').and.returnValue(Promise.resolve(true));
const updateWorkerSpy = spyOn(workerService, 'updateWorker').and.callThrough();
const submitSpy = spyOn(component, 'setSubmitAction').and.callThrough();
const alertSpy = spyOn(alertService, 'addAlert').and.callThrough();
const setAddStaffRecordInProgressSpy = spyOn(workerService, 'setAddStaffRecordInProgress');

if (addNewWorker) {
spyOn(workerService, 'setState').and.callFake(() => {
component.worker = workerBuilder() as Worker;
});
spyOnProperty(workerService, 'newWorkerMandatoryInfo').and.returnValue({
nameOrId: 'Someone',
contract: 'Permanent' as Contracts,
});
component.worker = null;
component.init();
fixture.detectChanges();
}

return {
component,
fixture,
getByTestId,
getByText,
getByLabelText,
router,
routerSpy,
updateWorkerSpy,
submitSpy,
workerService,
queryByTestId,
alertSpy,
setAddStaffRecordInProgressSpy,
};
}

Expand Down Expand Up @@ -173,7 +208,103 @@ describe('MainJobRoleComponent', () => {
expect(component.form.value).toEqual({ mainJob: 13 });
});

describe('progress bar', () => {
it('should render the progress bar when accessed from the flow', async () => {
const { getByTestId } = await setup();

expect(getByTestId('progress-bar-1')).toBeTruthy();
});

it('should not render the progress bar when accessed from outside the flow', async () => {
const { queryByTestId } = await setup(false);

expect(queryByTestId('progress-bar-1')).toBeFalsy();
});
});

describe('submit buttons', () => {
describe('adding new staff record', () => {
it(`should show 'Save this staff record' cta button and 'Cancel' link`, async () => {
const { getByText } = await setup(true, false, true);

expect(getByText('Save this staff record')).toBeTruthy();
expect(getByText('Cancel')).toBeTruthy();
});

it(`should call submit data and navigate to the the correct url when 'Save this staff record' is clicked`, async () => {
const { component, fixture, getByText, submitSpy, routerSpy, workerService } = await setup(true, false, true);
const createWorkerSpy = spyOn(workerService, 'createWorker').and.callThrough();

userEvent.click(getByText('Care providing roles'));
userEvent.click(getByText('Care worker'));
userEvent.click(getByText('Save this staff record'));

const updatedFormData = component.form.value;
expect(updatedFormData).toEqual({
mainJob: 10,
});

expect(submitSpy).toHaveBeenCalledWith({ action: 'continue', save: true });
expect(createWorkerSpy).toHaveBeenCalledWith(component.workplace.uid, {
nameOrId: 'Someone',
contract: 'Permanent',
mainJob: { jobId: 10 },
});
expect(routerSpy).toHaveBeenCalledWith([
'/workplace',
'mocked-uid',
'staff-record',
fixture.componentInstance.worker.uid,
'mandatory-details',
]);
});

it('should show a banner when a staff record has been successfully added', async () => {
const { getByText, alertSpy } = await setup(true, false, true);
userEvent.click(getByText('Care providing roles'));
userEvent.click(getByText('Care worker'));
userEvent.click(getByText('Save this staff record'));

expect(alertSpy).toHaveBeenCalledWith({
type: 'success',
message: 'Staff record saved',
});
});

it('should call setAddStaffRecordInProgress when clicking save this staff record', async () => {
const { getByText, workerService, setAddStaffRecordInProgressSpy } = await setup(true, false, true);

spyOn(workerService, 'createWorker').and.callThrough();

userEvent.click(getByText('Care providing roles'));
userEvent.click(getByText('Care worker'));
userEvent.click(getByText('Save this staff record'));

expect(setAddStaffRecordInProgressSpy).toHaveBeenCalledWith(true);
});

it('should return the user to the staff records tab when clicking cancel', async () => {
const { getByText, submitSpy, routerSpy, updateWorkerSpy } = await setup(true, false, true);

userEvent.click(getByText('Cancel'));
expect(submitSpy).toHaveBeenCalledWith({ action: 'exit', save: false });
expect(routerSpy).toHaveBeenCalledWith(['/dashboard'], {
fragment: 'staff-records',
});
expect(updateWorkerSpy).not.toHaveBeenCalled();
});

it('should return an error message if user clicked submit without selecting a job role', async () => {
const { fixture, getByText } = await setup(true, false, true);

userEvent.click(getByText('Save this staff record'));
fixture.detectChanges();

expect(getByText('There is a problem')).toBeTruthy();
expect(getByText('Select the job role')).toBeTruthy();
});
});

describe('editing from staff record', () => {
it(`should show 'Save and return' and 'Cancel' buttons when not in mandatory details flow or in the staff record flow`, async () => {
const { getByText } = await setup(false, false);
Expand Down Expand Up @@ -210,7 +341,7 @@ describe('MainJobRoleComponent', () => {
expect(updatedFormData).toEqual({
mainJob: 10,
});
expect(submitSpy).toHaveBeenCalledWith({ action: 'return', save: true });
expect(submitSpy).toHaveBeenCalledWith({ action: 'continue', save: true });
expect(updateWorkerSpy).toHaveBeenCalledWith(component.workplace.uid, component.worker.uid, {
mainJob: { jobId: 10 },
});
Expand All @@ -223,5 +354,80 @@ describe('MainJobRoleComponent', () => {
'staff-record-summary',
]);
});

it(`should navigate to the nursing-category page if the main job role is Registered nurse and outside of the flow`, async () => {
const { component, fixture, routerSpy, getByText, workerService } = await setup(false, false);

spyOn(workerService, 'hasJobRole').and.returnValues(false, true);
userEvent.click(getByText('Professional and related roles'));
userEvent.click(getByText('Registered nurse'));
userEvent.click(getByText('Save and return'));
fixture.detectChanges();

expect(routerSpy).toHaveBeenCalledWith([
'/workplace',
'mocked-uid',
'staff-record',
component.worker.uid,
'staff-record-summary',
'nursing-category',
]);
});

it(`should navigate to the nursing-category page if the main job role is Registered nurse and in the wdf edit version of the page`, async () => {
const { component, fixture, router, routerSpy, getByText, workerService } = await setup(false, false);
spyOnProperty(router, 'url').and.returnValue('/wdf/staff-record');
component.returnUrl = undefined;
component.ngOnInit();
fixture.detectChanges();

spyOn(workerService, 'hasJobRole').and.returnValues(false, true);
userEvent.click(getByText('Professional and related roles'));
userEvent.click(getByText('Registered nurse'));
userEvent.click(getByText('Save and return'));
fixture.detectChanges();

expect(routerSpy).toHaveBeenCalledWith(['/wdf', 'staff-record', component.worker.uid, 'nursing-category']);
});

it(`should navigate to the mental-health-professional page if the main job role is Social worker and outside of the flow`, async () => {
const { component, fixture, routerSpy, getByText, getByLabelText, workerService } = await setup(false, false);

spyOn(workerService, 'hasJobRole').and.returnValue(true);
userEvent.click(getByText('Professional and related roles'));
userEvent.click(getByText('Social worker'));
userEvent.click(getByText('Save and return'));
fixture.detectChanges();

expect(routerSpy).toHaveBeenCalledWith([
'/workplace',
'mocked-uid',
'staff-record',
component.worker.uid,
'staff-record-summary',
'mental-health-professional',
]);
});

it(`should navigate to the mental-health-professional page if the main job role is Social worker and in wdf version of page`, async () => {
const { component, fixture, routerSpy, router, getByText, workerService } = await setup(false, false);
spyOnProperty(router, 'url').and.returnValue('/wdf/staff-record');
component.returnUrl = undefined;
component.ngOnInit();
fixture.detectChanges();

spyOn(workerService, 'hasJobRole').and.returnValue(true);
userEvent.click(getByText('Professional and related roles'));
userEvent.click(getByText('Social worker'));
userEvent.click(getByText('Save and return'));
fixture.detectChanges();

expect(routerSpy).toHaveBeenCalledWith([
'/wdf',
'staff-record',
component.worker.uid,
'mental-health-professional',
]);
});
});
});
Loading

0 comments on commit df5ace4

Please sign in to comment.