Skip to content

Commit

Permalink
Mes 9554 journal rehydration (#1592)
Browse files Browse the repository at this point in the history
* initial commit for rehydration

* add call for completed tests into incompleted test function

* rebase

* WIP

* WIP

* begin to add unit tests

* update unit tests

* update to allow partially completed tests to be included with the journal restore
  • Loading branch information
andrewsetterfield authored Apr 29, 2024
1 parent 85b1b33 commit 1812ea1
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 25 deletions.
17 changes: 16 additions & 1 deletion src/app/pages/dashboard/__tests__/dashboard.page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { DateTime } from '@shared/helpers/date-time';
import { StoreModel } from '@shared/models/store.model';
import { BasePageComponent } from '@shared/classes/base-page';
import { ComponentsModule } from '@components/common/common-components.module';
import { LoadJournalSilent } from '@store/journal/journal.actions';
import { LoadCompletedTests, LoadJournalSilent } from '@store/journal/journal.actions';
import { CompletedTestPersistenceProvider } from '@providers/completed-test-persistence/completed-test-persistence';
import {
CompletedTestPersistenceProviderMock,
Expand Down Expand Up @@ -226,6 +226,21 @@ describe('DashboardPage', () => {
.toHaveBeenCalled();
expect(store$.dispatch)
.toHaveBeenCalledWith(LoadJournalSilent());
expect(store$.dispatch)
.toHaveBeenCalledWith(LoadCompletedTests(true));
});
it('should exclude various actions when delegated examiner', async () => {
spyOn(BasePageComponent.prototype, 'isIos')
.and
.returnValue(true);
spyOn(appConfigProvider, 'getAppConfig')
.and
.returnValue({ role: ExaminerRole.DLG } as AppConfig);
await component.ionViewDidEnter();
expect(store$.dispatch)
.not.toHaveBeenCalledWith(LoadJournalSilent());
expect(store$.dispatch)
.not.toHaveBeenCalledWith(LoadCompletedTests(true));
});
});
describe('showTestReportPracticeMode', () => {
Expand Down
13 changes: 11 additions & 2 deletions src/app/pages/dashboard/dashboard.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ import { ClearVehicleData } from '@pages/back-to-office/back-to-office.actions';
import { SlotProvider } from '@providers/slot/slot';
import { unsubmittedTestSlotsCount$ } from '@pages/unuploaded-tests/unuploaded-tests.selector';
import { sumFlatArray } from '@shared/helpers/sum-number-array';
import { StoreUnuploadedSlotsInTests } from '@pages/unuploaded-tests/unuploaded-tests.actions';
import {
StoreUnuploadedSlotsInTests,
} from '@pages/unuploaded-tests/unuploaded-tests.actions';
import {
UpdateAvailable,
UpdateAvailableModal,
Expand All @@ -41,6 +43,7 @@ import {
UpdateAvailablePopup,
} from '@store/app-info/app-info.actions';
import { DashboardViewDidEnter, PracticeTestReportCard } from './dashboard.actions';
import { CompletedTestPersistenceProvider } from '@providers/completed-test-persistence/completed-test-persistence';

interface DashboardPageState {
appVersion$: Observable<string>;
Expand Down Expand Up @@ -74,6 +77,7 @@ export class DashboardPage extends BasePageComponent implements OnInit, ViewDidE
private networkStateProvider: NetworkStateProvider,
private slotProvider: SlotProvider,
private modalController: ModalController,
private completedTestPersistenceProvider: CompletedTestPersistenceProvider,
injector: Injector,
) {
super(injector);
Expand Down Expand Up @@ -128,9 +132,14 @@ export class DashboardPage extends BasePageComponent implements OnInit, ViewDidE
this.store$.dispatch(ClearCandidateLicenceData());
this.store$.dispatch(ClearVehicleData());
this.store$.dispatch(StoreUnuploadedSlotsInTests());
//guard against calling journal if the user type is a delegated examiner

//guard against calling various services if the user type is a delegated examiner
if (!this.isDelegatedExaminer()) {
this.store$.dispatch(journalActions.LoadJournalSilent());

// acquire previously completed tests
await this.completedTestPersistenceProvider.loadCompletedPersistedTests();
this.store$.dispatch(journalActions.LoadCompletedTests(true));
}

await super.unlockDevice();
Expand Down
14 changes: 11 additions & 3 deletions src/app/pages/journal/__tests__/journal.page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe('JournalPage', () => {
let component: JournalPage;
let store$: Store<StoreModel>;
let loaderService: LoadingProvider;
let completedTestPersistenceProvider: CompletedTestPersistenceProvider;
const loadingOpts: LoadingOptions = {
id: 'journal_loading_spinner',
spinner: 'circles',
Expand Down Expand Up @@ -137,6 +138,7 @@ describe('JournalPage', () => {
provide: ActivatedRoute,
useClass: ActivatedRouteMock,
},

],
});

Expand All @@ -146,6 +148,7 @@ describe('JournalPage', () => {
component.subscription = new Subscription();
store$ = TestBed.inject(Store);
loaderService = TestBed.inject(LoadingProvider);
completedTestPersistenceProvider = TestBed.inject(CompletedTestPersistenceProvider);
spyOn(store$, 'dispatch');
spyOn(loaderService, 'handleUILoading');
spyOn(BasePageComponent.prototype, 'isIos')
Expand Down Expand Up @@ -288,18 +291,23 @@ describe('JournalPage', () => {
.toHaveBeenCalled();
expect(component.configurePlatformSubscriptions)
.toHaveBeenCalled();
expect(completedTestPersistenceProvider.loadCompletedPersistedTests)
.toHaveBeenCalled();
expect(store$.dispatch)
.toHaveBeenCalledWith(journalActions.LoadCompletedTests(true));
});
});

describe('refreshJournal', () => {
it('should run loadJournalManually', async () => {
spyOn(component, 'loadJournalManually')
.and
.callThrough();
spyOn(component, 'loadJournalManually').and.callThrough();
spyOn(component, 'loadCompletedTestsWithCallThrough').and.callThrough();

await component.refreshJournal();
expect(component.loadJournalManually)
.toHaveBeenCalled();
expect(component.loadCompletedTestsWithCallThrough)
.toHaveBeenCalled();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
<test-slot
*ngSwitchCase="'test-slot'"
[delegatedTest]="false"
[derivedActivityCode]="hasSlotBeenTested(slot.slotData, completedTests)"
[derivedPassCertificate]="didSlotPass(slot.slotData, completedTests)"
[derivedTestStatus]="derivedTestStatus(slot?.slotData, completedTests)"
[hasSeenCandidateDetails]="slot?.hasSeenCandidateDetails"
[hasSlotChanged]="slot?.hasSlotChanged"
[isPortrait]="isPortrait"
Expand Down
24 changes: 22 additions & 2 deletions src/app/pages/journal/journal.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Component, Injector, OnInit } from '@angular/core';
import { ModalController, RefresherEventDetail } from '@ionic/angular';
import { select } from '@ngrx/store';
import { IonRefresherCustomEvent, LoadingOptions } from '@ionic/core';
import { merge, Observable, of, Subscription } from 'rxjs';
import { merge, Observable, Subscription } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators';
import { SearchResultTestSchema } from '@dvsa/mes-search-schema';
import { ScreenOrientation } from '@capawesome/capacitor-screen-orientation';
Expand All @@ -18,6 +18,7 @@ import * as journalActions from '@store/journal/journal.actions';
import {
canNavigateToNextDay,
canNavigateToPreviousDay,
getCompletedTests,
getError,
getIsLoading,
getLastRefreshed,
Expand All @@ -31,6 +32,7 @@ import { LoadingProvider } from '@providers/loader/loader';
import { OrientationMonitorProvider } from '@providers/orientation-monitor/orientation-monitor.provider';
import { AccessibilityService } from '@providers/accessibility/accessibility.service';
import { ErrorPage } from '../error-page/error';
import { CompletedTestPersistenceProvider } from '@providers/completed-test-persistence/completed-test-persistence';

interface JournalPageState {
selectedDate$: Observable<string>;
Expand Down Expand Up @@ -72,6 +74,7 @@ export class JournalPage extends BasePageComponent implements OnInit {
private accessibilityService: AccessibilityService,
private networkStateProvider: NetworkStateProvider,
public loadingProvider: LoadingProvider,
private completedTestPersistenceProvider: CompletedTestPersistenceProvider,
injector: Injector,
) {
super(injector);
Expand Down Expand Up @@ -121,7 +124,10 @@ export class JournalPage extends BasePageComponent implements OnInit {
map((selectedDate) => selectedDate === this.dateTimeProvider.now()
.format('YYYY-MM-DD')),
),
completedTests$: of([]),
completedTests$: this.store$.pipe(
select(getJournalState),
select(getCompletedTests),
),
};

const {
Expand All @@ -148,6 +154,9 @@ export class JournalPage extends BasePageComponent implements OnInit {
this.setupPolling();
this.configurePlatformSubscriptions();

await this.completedTestPersistenceProvider.loadCompletedPersistedTests();
this.store$.dispatch(journalActions.LoadCompletedTests(true));

if (this.merged$) {
this.subscription = this.merged$.subscribe();
}
Expand Down Expand Up @@ -226,6 +235,7 @@ export class JournalPage extends BasePageComponent implements OnInit {

public refreshJournal = async () => {
await this.loadJournalManually();
this.loadCompletedTestsWithCallThrough();
};

onPreviousDayClick(): void {
Expand All @@ -235,4 +245,14 @@ export class JournalPage extends BasePageComponent implements OnInit {
onNextDayClick(): void {
this.store$.dispatch(journalActions.SelectNextDay());
}

/**
* Load the completed tests with the callThrough property set to true (default false)
* This will make the request to the backend to check if any of the tests have already been submitted
* by another device
* *
*/
loadCompletedTestsWithCallThrough = (): void => {
this.store$.dispatch(journalActions.LoadCompletedTests(true));
};
}
2 changes: 1 addition & 1 deletion src/app/providers/slot-selector/slot-selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class SlotSelectorProvider {
return completedTest.activityCode;
}

private getCompletedTest = (slotData: TestSlot, completedTests: SearchResultTestSchema[] = []) => {
private getCompletedTest = (slotData: TestSlot, completedTests: SearchResultTestSchema[]) => {
if (isEmpty(completedTests)) {
return null;
}
Expand Down
24 changes: 20 additions & 4 deletions src/components/test-slot/test-slot/test-slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ export class TestSlotComponent implements SlotComponent, OnInit {
@Input()
teamJournalCandidateResult: boolean = false;

@Input()
derivedTestStatus: TestStatus | null = null;

@Input()
derivedActivityCode: ActivityCode | null = null;

@Input()
derivedPassCertificate?: string;

@Input()
examinerName: string = null;

Expand Down Expand Up @@ -109,19 +118,26 @@ export class TestSlotComponent implements SlotComponent, OnInit {
this.componentState = {
testStatus$: this.store$.pipe(
select(getTests),
select((tests) => getTestStatus(tests, slotId)),
select((tests) => {
const testStatus = getTestStatus(tests, slotId);
return testStatus === TestStatus.Autosaved ? testStatus : this.derivedTestStatus || testStatus;
}),
),
testActivityCode$: this.store$.pipe(
select(getTests),
map((tests) => getActivityCodeBySlotId(tests, slotId)),
map((tests) => {
return this.derivedActivityCode || getActivityCodeBySlotId(tests, slotId);
}),
),
testPassCertificate$: this.store$.pipe(
select(getTests),
map((tests) => getPassCertificateBySlotId(tests, slotId)),
map((tests) => {
return this.derivedPassCertificate || getPassCertificateBySlotId(tests, slotId);
}),
),
isRekey$: this.store$.pipe(
select(getTests),
map((tests) => getTestById(tests, slotId?.toString())),
map((tests) => getTestById(tests, this.slot.slotDetail.slotId.toString())),
filter((test) => test !== undefined),
select(getRekeyIndicator),
select(isRekey),
Expand Down
27 changes: 15 additions & 12 deletions src/store/journal/journal.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import { AdvancedSearchParams } from '@providers/search/search.models';
import { removeLeadingZeros } from '@shared/helpers/formatters';
import { hasStartedTests } from '@store/tests/tests.selector';
import { SearchResultTestSchema } from '@dvsa/mes-search-schema';
import { getStaffNumber } from '@store/tests/journal-data/common/examiner/examiner.selector';
import { CompletedTestPersistenceProvider } from '@providers/completed-test-persistence/completed-test-persistence';
import { ExaminerSlotItems, ExaminerSlotItemsByDate } from './journal.model';
import { SaveLog } from '../logs/logs.actions';
Expand All @@ -51,6 +50,7 @@ import {
getSelectedDate,
getSlots,
} from './journal.selector';
import { selectEmployeeId } from '@store/app-info/app-info.selectors';

@Injectable()
export class JournalEffects {
Expand Down Expand Up @@ -171,7 +171,7 @@ export class JournalEffects {
pollingSetup$ = createEffect(() => this.actions$.pipe(
ofType(journalActions.SetupPolling),
switchMap(() => {
// Switch map the manual refreshes so they restart the timer.
// Switch map the manual refreshes, restarting the timer.
const manualRefreshes$ = this.actions$.pipe(
ofType(journalActions.LoadJournal),
// Initial emission so poll doesn't wait until the first manual refresh
Expand Down Expand Up @@ -204,9 +204,7 @@ export class JournalEffects {
.pipe(
withLatestFrom(
this.store$.pipe(
select(getJournalState),
select(getExaminer),
select(getStaffNumber),
select(selectEmployeeId)
),
this.store$.pipe(
select(getTests),
Expand All @@ -226,20 +224,25 @@ export class JournalEffects {
if ((environment as unknown as TestersEnvironmentFile)?.isTest) return false;
if (action.callThrough) return true;


return !hasStarted && completedTests && completedTests.length === 0;
}),
switchMap(([, staffNumber]) => {
const { numberOfDaysToView } = this.appConfig.getAppConfig().journal;
const dateTime = new DateTime();

const startDate = new DateTime()
.subtract(numberOfDaysToView, Duration.DAY)
.format('YYYY-MM-DD');

const endDate = new DateTime()
.format('YYYY-MM-DD');

const advancedSearchParams: AdvancedSearchParams = {
startDate: dateTime
.subtract(numberOfDaysToView, Duration.DAY)
.format('YYYY-MM-DD'),
endDate: dateTime
.format('YYYY-MM-DD'),
startDate,
endDate,
staffNumber: removeLeadingZeros(staffNumber),
costCode: '',
excludeAutoSavedTests: 'true',
excludeAutoSavedTests: 'false',
activityCode: '',
category: '',
};
Expand Down

0 comments on commit 1812ea1

Please sign in to comment.