Skip to content

Commit

Permalink
feat(#8119): add route guard to show training card confirm message (#…
Browse files Browse the repository at this point in the history
…9512)

Co-authored-by: Aniekan Eshiet <[email protected]>
  • Loading branch information
2 people authored and m5r committed Oct 17, 2024
1 parent be51c62 commit 49dcd91
Show file tree
Hide file tree
Showing 21 changed files with 268 additions and 69 deletions.
54 changes: 52 additions & 2 deletions tests/e2e/default/enketo/training-cards.wdio-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ const reportsPage = require('@page-objects/default/reports/reports.wdio.page');
const privacyPolicyFactory = require('@factories/cht/settings/privacy-policy');
const privacyPage = require('@page-objects/default/privacy-policy/privacy-policy.wdio.page');
const commonEnketoPage = require('@page-objects/default/enketo/common-enketo.wdio.page');
const modalPage = require('@page-objects/default/common/modal.wdio.page');

describe('Training Cards', () => {

const expectedConfirmModalHeader = 'Leave training?';
const expectedConfirmMessage = 'This training is not finished. ' +
'If you leave now, you will lose your progress and be prompted again later to complete it.';

Expand Down Expand Up @@ -48,13 +49,62 @@ describe('Training Cards', () => {
await trainingCardsPage.waitForTrainingCards();

const confirmMessage = await trainingCardsPage.quitTraining();
expect(confirmMessage.header).to.equal('Leave training?');
expect(confirmMessage.header).to.equal(expectedConfirmModalHeader);
expect(confirmMessage.body).to.contain(expectedConfirmMessage);
await trainingCardsPage.confirmQuitTraining();
await trainingCardsPage.checkTrainingCardIsNotDisplayed();

await commonPage.goToReports();
await commonElements.waitForPageLoaded();
expect(await reportsPage.leftPanelSelectors.allReports()).to.be.empty;
});

it('should display confirm message before navigating to different page', async () => {
await commonPage.goToMessages();
await commonElements.waitForPageLoaded();
await setLastViewedDateInThePast();
// Unfinished trainings should appear again after reload.
await browser.refresh();
await trainingCardsPage.waitForTrainingCards();
await commonPage.goToPeople();

const confirmMessage = await modalPage.getModalDetails();
expect(confirmMessage.header).to.contain(expectedConfirmModalHeader);
expect(confirmMessage.body).to.contain(expectedConfirmMessage);
expect(await browser.getUrl()).to.contain('/messages');

await trainingCardsPage.confirmQuitTraining();
await trainingCardsPage.checkTrainingCardIsNotDisplayed();
expect(await browser.getUrl()).to.contain('/contacts');

// Going to Reports to check training was not saved
await commonPage.goToReports();
expect(await reportsPage.leftPanelSelectors.allReports()).to.be.empty;
});

it('should display confirm message when browser back button is clicked', async () => {
// Creating navigation history to test back action
await commonPage.goToMessages();
await commonPage.goToPeople();

await commonElements.waitForPageLoaded();
await setLastViewedDateInThePast();
// Unfinished trainings should appear again after reload.
await browser.refresh();
await trainingCardsPage.waitForTrainingCards();
await browser.back();

const confirmMessage = await modalPage.getModalDetails();
expect(confirmMessage.header).to.contain(expectedConfirmModalHeader);
expect(confirmMessage.body).to.contain(expectedConfirmMessage);
expect(await browser.getUrl()).to.contain('/contacts');

await trainingCardsPage.confirmQuitTraining();
await trainingCardsPage.checkTrainingCardIsNotDisplayed();
expect(await browser.getUrl()).to.contain('/messages');

// Going to Reports to check training was not saved
await commonPage.goToReports();
expect(await reportsPage.leftPanelSelectors.allReports()).to.be.empty;
});

Expand Down
10 changes: 5 additions & 5 deletions webapp/src/ts/actions/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ export const Actions = {
setTranslationsLoaded: createAction('SET_TRANSLATIONS_LOADED'),
setUserFacilityIds: createSingleValueAction('SET_USER_FACILITY_IDS', 'userFacilityIds'),
setUserContactId: createSingleValueAction('SET_USER_CONTACT_ID', 'userContactId'),
setTrainingCardFormId: createSingleValueAction('SET_TRAINING_CARD_FORM_ID', 'trainingCardFormId'),
setSidebarMenu: createSingleValueAction('SET_SIDEBAR_MENU', 'sidebarMenu'),
closeSidebarMenu: createAction('CLOSE_SIDEBAR_MENU'),
openSidebarMenu: createAction('OPEN_SIDEBAR_MENU'),
setSearchBar: createSingleValueAction('SET_SEARCH_BAR', 'searchBar'),
setTrainingCard: createSingleValueAction('SET_TRAINING_CARD', 'trainingCard'),
};

export class GlobalActions {
Expand Down Expand Up @@ -96,6 +96,10 @@ export class GlobalActions {
return this.store.dispatch(Actions.setSearchBar(searchBar));
}

setTrainingCard(trainingCard) {
return this.store.dispatch(Actions.setTrainingCard(trainingCard));
}

clearSidebarFilter() {
return this.store.dispatch(Actions.clearSidebarFilter());
}
Expand Down Expand Up @@ -213,10 +217,6 @@ export class GlobalActions {
return this.store.dispatch(Actions.setUserContactId(userContactId));
}

setTrainingCardFormId(trainingCard) {
return this.store.dispatch(Actions.setTrainingCardFormId(trainingCard));
}

setSidebarMenu(sidebarMenu) {
return this.store.dispatch(Actions.setSidebarMenu(sidebarMenu));
}
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/ts/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ _.templateSettings.interpolate = /\{\{(.+?)\}\}/g;

import { AppRoutingModule } from './app-routing.module';
import { AppRouteGuardProvider } from './app-route.guard.provider';
import { TrainingCardDeactivationGuardProvider } from './training-card.guard.provider';
import { AppComponent } from './app.component';
import { ModulesModule } from '@mm-modules/modules.module';
import { environment } from '@mm-environments/environment';
Expand Down Expand Up @@ -93,6 +94,7 @@ export class MissingTranslationHandlerLog implements MissingTranslationHandler {
providers: [
{ provide: APP_BASE_HREF, useValue: '/' },
AppRouteGuardProvider,
TrainingCardDeactivationGuardProvider,
AnalyticsRouteGuardProvider,
CookieService,
ParseProvider,
Expand Down
16 changes: 15 additions & 1 deletion webapp/src/ts/modals/training-cards/training-cards.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { combineLatest, Subscription } from 'rxjs';
import { MatDialogRef } from '@angular/material/dialog';
import { Router } from '@angular/router';

import { XmlFormsService } from '@mm-services/xml-forms.service';
import { FormService } from '@mm-services/form.service';
Expand All @@ -21,6 +22,7 @@ export class TrainingCardsComponent implements OnInit, OnDestroy {
constructor(
private ngZone: NgZone,
private store: Store,
private readonly router: Router,
private xmlFormsService: XmlFormsService,
private formService: FormService,
private geolocationService: GeolocationService,
Expand Down Expand Up @@ -51,12 +53,14 @@ export class TrainingCardsComponent implements OnInit, OnDestroy {
enketoStatus;
enketoSaving;
showConfirmExit;
nextUrl;
subscription: Subscription = new Subscription();

ngOnInit() {
this.trackRender = this.performanceService.track();
this.reset();
this.subscribeToStore();
this.globalActions.setTrainingCard({ isOpen: true });
}

ngOnDestroy() {
Expand All @@ -71,6 +75,7 @@ export class TrainingCardsComponent implements OnInit, OnDestroy {
// see https://github.com/medic/cht-core/issues/2198#issuecomment-210202785 for AngularJS behavior
this.formService.unload(this.form);
this.globalActions.clearEnketoStatus();
this.globalActions.setTrainingCard({ formId: null, isOpen: false, showConfirmExit: false, nextUrl: null });
}

private loadForm() {
Expand Down Expand Up @@ -113,15 +118,19 @@ export class TrainingCardsComponent implements OnInit, OnDestroy {
this.store.select(Selectors.getEnketoSavingStatus),
this.store.select(Selectors.getEnketoError),
this.store.select(Selectors.getTrainingCardFormId),
this.store.select(Selectors.getTrainingCard),
]).subscribe(([
enketoStatus,
enketoSaving,
enketoError,
trainingCardFormId,
trainingCard,
]) => {
this.enketoStatus = enketoStatus;
this.enketoSaving = enketoSaving;
this.enketoError = enketoError;
this.showConfirmExit = trainingCard.showConfirmExit;
this.nextUrl = trainingCard.nextUrl;

if (trainingCardFormId && trainingCardFormId !== this.trainingCardFormId) {
this.trainingCardFormId = trainingCardFormId;
Expand Down Expand Up @@ -154,7 +163,7 @@ export class TrainingCardsComponent implements OnInit, OnDestroy {
}

close() {
this.globalActions.setTrainingCardFormId(null);
this.globalActions.setTrainingCard({ formId: null, isOpen: false, showConfirmExit: false, nextUrl: null });
this.matDialogRef.close();
}

Expand Down Expand Up @@ -224,6 +233,11 @@ export class TrainingCardsComponent implements OnInit, OnDestroy {

quitTraining() {
this.recordPerformanceQuitTraining();

if (this.nextUrl) {
this.router.navigateByUrl(this.nextUrl);
}

this.close();
}
}
8 changes: 7 additions & 1 deletion webapp/src/ts/modules/about/about.routes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Routes } from '@angular/router';

import { AboutComponent } from '@mm-modules/about/about.component';
import { TrainingCardDeactivationGuardProvider } from 'src/ts/training-card.guard.provider';

export const routes:Routes = [
{ path: 'about', component: AboutComponent, data: { tab: 'about' } },
{
path: 'about',
component: AboutComponent,
data: { tab: 'about' },
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
},
];
12 changes: 9 additions & 3 deletions webapp/src/ts/modules/analytics/analytics.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
AnalyticsTargetAggregatesDetailComponent
} from '@mm-modules/analytics/analytics-target-aggregates-detail.component';
import { AnalyticsRouteGuardProvider } from '@mm-modules/analytics/analytics-route.guard.provider';
import { TrainingCardDeactivationGuardProvider } from 'src/ts/training-card.guard.provider';
import { AGGREGATE_TARGETS_ID } from '@mm-services/analytics-modules.service';

export const routes:Routes = [
Expand All @@ -22,24 +23,29 @@ export const routes:Routes = [
{
path: '',
component: AnalyticsModulesComponent,
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
},
{
path: 'targets',
component: AnalyticsTargetsComponent,
data: { moduleId: 'targets' }
data: { moduleId: 'targets' },
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
},
{
path: 'target-aggregates',
component: AnalyticsTargetAggregatesComponent,
data: { moduleId: AGGREGATE_TARGETS_ID },
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
children: [
{
path: '',
component: AnalyticsTargetAggregatesDetailComponent
component: AnalyticsTargetAggregatesDetailComponent,
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
},
{
path: ':id',
component: AnalyticsTargetAggregatesDetailComponent
component: AnalyticsTargetAggregatesDetailComponent,
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
}
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Routes } from '@angular/router';

import { ConfigurationUserComponent } from '@mm-modules/configuration-user/configuration-user.component';
import { TrainingCardDeactivationGuardProvider } from 'src/ts/training-card.guard.provider';

export const routes:Routes = [
{
path: 'user',
component: ConfigurationUserComponent,
data: { tab: 'user'},
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
}
];
24 changes: 14 additions & 10 deletions webapp/src/ts/modules/contacts/contacts.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,61 @@ import { ContactsContentComponent } from '@mm-modules/contacts/contacts-content.
import { ContactsDeceasedComponent } from '@mm-modules/contacts/contacts-deceased.component';
import { ContactsEditComponent } from '@mm-modules/contacts/contacts-edit.component';
import { ContactRouteGuardProvider } from '@mm-modules/contacts/contact-route.guard.provider';
import { TrainingCardDeactivationGuardProvider } from 'src/ts/training-card.guard.provider';
import { ContactsReportComponent } from '@mm-modules/contacts/contacts-report.component';

export const routes: Routes = [
{
path: 'contacts',
component: ContactsComponent,
data: {permissions: ['can_view_contacts'], tab: 'contacts'},
canActivate: [AppRouteGuardProvider],
data: { permissions: ['can_view_contacts'], tab: 'contacts' },
canActivate: [ AppRouteGuardProvider ],
children: [
{
path: '',
component: ContactsContentComponent,
data: { name: 'contacts.detail' },
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
},
{
path: ':id',
component: ContactsContentComponent,
data: { name: 'contacts.detail' },
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
},
{
path: ':id/deceased',
component: ContactsDeceasedComponent,
data: { name: 'contacts.deceased' },
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
},
{
path: 'add/:type',
component: ContactsEditComponent,
data: { permissions: ['can_edit'], hideTraining: true },
canActivate: [AppRouteGuardProvider],
canDeactivate: [ContactRouteGuardProvider],
canActivate: [ AppRouteGuardProvider ],
canDeactivate: [ ContactRouteGuardProvider ],
},
{
path: ':parent_id/add/:type',
component: ContactsEditComponent,
data: { permissions: ['can_edit'], hideTraining: true },
canActivate: [AppRouteGuardProvider],
canDeactivate: [ContactRouteGuardProvider],
canActivate: [ AppRouteGuardProvider ],
canDeactivate: [ ContactRouteGuardProvider ],
},
{
path: ':id/edit',
component: ContactsEditComponent,
data: { permissions: ['can_edit'], hideTraining: true },
canActivate: [AppRouteGuardProvider],
canDeactivate: [ContactRouteGuardProvider],
canActivate: [ AppRouteGuardProvider ],
canDeactivate: [ ContactRouteGuardProvider ],
},
{
path: ':id/report/:formId',
component: ContactsReportComponent,
data: { permissions: ['can_edit'], hideTraining: true },
canActivate: [AppRouteGuardProvider],
canDeactivate: [ContactRouteGuardProvider],
canActivate: [ AppRouteGuardProvider ],
canDeactivate: [ ContactRouteGuardProvider ],
}
],
},
Expand Down
9 changes: 6 additions & 3 deletions webapp/src/ts/modules/messages/messages.routes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Routes, UrlSegment } from '@angular/router';

import { AppRouteGuardProvider } from '../../app-route.guard.provider';
import { TrainingCardDeactivationGuardProvider } from 'src/ts/training-card.guard.provider';
import { MessagesComponent } from './messages.component';
import { MessagesContentComponent } from './messages-content.component';

Expand All @@ -9,11 +10,12 @@ export const routes: Routes = [
path: 'messages',
component: MessagesComponent,
data: { permissions: ['can_view_messages'], tab: 'messages'},
canActivate: [AppRouteGuardProvider],
canActivate: [ AppRouteGuardProvider ],
children: [
{
path: '',
component: MessagesContentComponent
component: MessagesContentComponent,
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
},
/** This child route with matcher will redirect from /messages/[uuid] to /messages/contacts:[uuid]
* Ignoring any extra URL parameters because messages-content component is
Expand All @@ -37,7 +39,8 @@ export const routes: Routes = [
},
{
path: ':type_id',
component: MessagesContentComponent
component: MessagesContentComponent,
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
}
]
},
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/ts/modules/privacy-policy/privacy-policy.routes.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Routes } from '@angular/router';

import { PrivacyPolicyComponent } from './privacy-policy.component';
import { TrainingCardDeactivationGuardProvider } from 'src/ts/training-card.guard.provider';

export const routes: Routes = [
{
path: 'privacy-policy',
component: PrivacyPolicyComponent,
data: { tab: 'privacy-policy' },
canDeactivate: [ TrainingCardDeactivationGuardProvider ],
}
];
Loading

0 comments on commit 49dcd91

Please sign in to comment.