Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#7375): service worker auto update #9417

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1355ca5
move moment-locales-webpack-plugin to webapp
dianabarsan Aug 29, 2024
95d9705
handle organic service worker updates
dianabarsan Aug 30, 2024
d0f57a1
Merge branch 'master' into 7375-service-worker-auto-update
dianabarsan Aug 30, 2024
6a0b335
change how we try to sync
dianabarsan Sep 5, 2024
ab6baf8
Merge remote-tracking branch 'origin/master' into 7375-service-worker…
dianabarsan Sep 6, 2024
878c1ae
close modal before hamburger menu
dianabarsan Sep 6, 2024
5f6f1bd
close modal before hamburger menu
dianabarsan Sep 6, 2024
119c976
close modal before hamburger menu
dianabarsan Sep 7, 2024
d0df488
close modal before hamburger menu
dianabarsan Sep 20, 2024
d30ecb0
Merge remote-tracking branch 'origin/master' into 7375-service-worker…
dianabarsan Sep 23, 2024
b532821
update sync retry
dianabarsan Sep 24, 2024
2d3660c
fix linting
dianabarsan Sep 24, 2024
77d8f9a
wait for a standard amount of time
dianabarsan Sep 24, 2024
727b5f8
try with longer pause
dianabarsan Sep 24, 2024
ae17825
try with longer pause
dianabarsan Sep 24, 2024
849aa42
try with longer pause
dianabarsan Sep 25, 2024
99757cb
try with longer pause
dianabarsan Sep 25, 2024
da63297
try with longer pause
dianabarsan Sep 25, 2024
5f1feca
try with longer pause
dianabarsan Sep 25, 2024
a2205d9
try with longer pause
dianabarsan Sep 25, 2024
7e0cc39
try with longer pause
dianabarsan Sep 25, 2024
344c1dd
try with longer pause
dianabarsan Sep 25, 2024
c4158fd
try with longer pause
dianabarsan Sep 25, 2024
b49caa8
try with longer pause
dianabarsan Sep 26, 2024
8984619
try with longer pause
dianabarsan Sep 26, 2024
7d70f7d
try with longer pause
dianabarsan Sep 26, 2024
595ef11
Merge remote-tracking branch 'origin/master' into 7375-service-worker…
dianabarsan Oct 14, 2024
4d2586b
delete feedback docs when sync failures are expected.
dianabarsan Oct 16, 2024
04cb549
What is this value?
dianabarsan Oct 16, 2024
4a61dce
Merge remote-tracking branch 'refs/remotes/origin/master' into 7375-s…
dianabarsan Oct 16, 2024
707b8ce
resolve conflicts
dianabarsan Oct 16, 2024
e22ddc5
fix sync on client side muting
dianabarsan Oct 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/e2e/default/db/initial-replication.wdio-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ describe('initial-replication', () => {
setTimeout(() => browser.refresh(), 3000);
setTimeout(() => browser.refresh(), 5000);

await browser.pause(5000);
await commonPage.waitForPageLoaded();
await validateReplication();
});
Expand Down
32 changes: 2 additions & 30 deletions tests/e2e/default/transitions/client-side-muting.wdio-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const personFactory = require('@factories/cht/contacts/person');

/* global window */

describe.skip('Muting', () => {
describe('Muting', () => {
const places = placeFactory.generateHierarchy();
const district = places.get('district_hospital');
const healthCenter = places.get('health_center');
Expand Down Expand Up @@ -155,22 +155,6 @@ describe.skip('Muting', () => {
expect(doc.muted).to.be.ok;
expect(doc.muting_history).to.be.undefined;
};
const setBrowserOffline = async () => {
await browser.throttle({
offline: true,
downloadThroughput: 0,
uploadThroughput: 0,
latency: 0
});
};
const setBrowserOnline = async () => {
await browser.throttle({
offline: false,
downloadThroughput: 1000 * 1000,
uploadThroughput: 1000 * 1000,
latency: 0
});
};

before(async () => {
await utils.saveDocs([district, healthCenter]);
Expand Down Expand Up @@ -213,17 +197,7 @@ describe.skip('Muting', () => {

describe('for an offline user', () => {
const updateClientSideMutingSettings = async (settings) => {
await setBrowserOffline();
await utils.updateSettings(settings);
await setBrowserOnline();
try {
await commonPage.sync();
} catch (err) {
// sometimes sync happens by itself, on timeout
console.error('Error when trying to sync', err);
await commonPage.closeReloadModal(true);
await commonPage.sync();
}
await utils.updateSettings(settings, { sync: true, refresh: true, ignoreReload: true });
};

const unmuteContacts = () => {
Expand Down Expand Up @@ -264,10 +238,8 @@ describe.skip('Muting', () => {

afterEach(async () => {
await commonPage.sync();
await setBrowserOffline();
await utils.revertSettings(true);
await unmuteContacts();
await setBrowserOnline();
});

it( 'should not process muting client-side if not enabled', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,30 +87,6 @@ const loginAsUser = async (user) => {
await commonPage.waitForPageLoaded();
};

/**
* Ongoing replication can be interrupted by the user being edited on the server side.
* A 401 for a replication request will create a feedback doc, which will fail the test.
*/
const assertFeedbackDocs = async () => {
const feedbackDocs = await chtDbUtils.getFeedbackDocs();
if (!feedbackDocs.length) {
return;
}

const feedbackDocsToIgnore = [
'Http failure response',
'Server error'
];

const unknownMessages = feedbackDocs
.map(doc => doc.info.message)
.filter(message => !feedbackDocsToIgnore.find(toIgnore => message.includes(toIgnore)));

if (!unknownMessages.length) {
await chtDbUtils.clearFeedbackDocs();
}
};

const loginAsOfflineUser = () => loginAsUser(ORIGINAL_USER);

const loginAsOnlineUser = () => loginAsUser(ONLINE_USER);
Expand Down Expand Up @@ -206,6 +182,30 @@ const waitForConflicts = async (getDoc) => {
return utils.delayPromise(waitForConflicts(getDoc), 100);
};

/**
* Ongoing replication can be interrupted by the user being edited on the server side.
* A 401 for a replication request will create a feedback doc, which will fail the test.
*/
const assertFeedbackDocs = async () => {
const feedbackDocs = await chtDbUtils.getFeedbackDocs();
if (!feedbackDocs.length) {
return;
}

const feedbackDocsToIgnore = [
'Http failure response',
'Server error'
];

const unknownMessages = feedbackDocs
.map(doc => doc.info.message)
.filter(message => !feedbackDocsToIgnore.find(toIgnore => message.includes(toIgnore)));

if (!unknownMessages.length) {
await chtDbUtils.clearFeedbackDocs();
}
};

describe('Create user for contacts', () => {
before(async () => {
await utils.saveDocIfNotExists(BASIC_FORM_DOC);
Expand Down
36 changes: 23 additions & 13 deletions tests/page-objects/default/common/common.wdio.page.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ const FEEDBACK = '#feedback';
//About menu
const ABOUT_MENU = 'aria/About';
//Configuration App
const ELEMENT_DISPLAY_PAUSE = 500; // 500ms

const configurationAppMenuOption = () => $('aria/App Management');
const errorLog = () => $(`error-log`);
const sideBarMenuTitle = () => $('aria/Menu');
Expand All @@ -72,7 +74,7 @@ const waitForSnackbarToClose = async () => {

const clickFastActionById = async (id) => {
// Wait for the Angular Material's animation to complete.
await browser.pause(500);
await browser.pause(ELEMENT_DISPLAY_PAUSE);
await (await fastActionListContainer()).waitForDisplayed();
await (await fastActionById(id)).waitForClickable();
await (await fastActionById(id)).click();
Expand Down Expand Up @@ -107,7 +109,7 @@ const getFastActionItemsLabels = async () => {
await fab.waitForClickable();
await fab.click();

await browser.pause(500);
await browser.pause(ELEMENT_DISPLAY_PAUSE);
await (await fastActionListContainer()).waitForDisplayed();

const items = await fastActionItems();
Expand Down Expand Up @@ -345,16 +347,25 @@ const syncAndNotWaitForSuccess = async () => {
await (await syncButton()).click();
};

const syncAndWaitForSuccess = async (timeout = 20000) => {
await openHamburgerMenu();
await (await syncButton()).waitForClickable();
await (await syncButton()).click();
await closeReloadModal(false);
await openHamburgerMenu();
if (await (await syncInProgress()).isExisting()) {
await (await syncInProgress()).waitForDisplayed({ reverse: true, timeout });
const syncAndWaitForSuccess = async (timeout = 20000, retry = 10) => {
if (retry < 0) {
throw new Error('Failed to sync after 10 retries');
}
await closeReloadModal(false, 0);

try {
await openHamburgerMenu();
if (!await (await syncInProgress()).isExisting()) {
await (await syncButton()).click();
await openHamburgerMenu();
}

await (await syncInProgress()).waitForDisplayed({ timeout, reverse: true });
await (await syncSuccess()).waitForDisplayed({ timeout: ELEMENT_DISPLAY_PAUSE });
} catch (err) {
console.error(err);
await syncAndWaitForSuccess(timeout, retry - 1);
}
await (await syncSuccess()).waitForDisplayed({ timeout });
};

const hideModalOverlay = () => {
Expand Down Expand Up @@ -393,12 +404,11 @@ const syncAndWaitForFailure = async () => {

const closeReloadModal = async (shouldUpdate = false, timeout = 5000) => {
try {
await browser.waitUntil( async () => await modalPage.modal().isDisplayed(), { timeout: 10000, interval: 500 } );
shouldUpdate ? await modalPage.submit(timeout) : await modalPage.cancel(timeout);
shouldUpdate && await waitForAngularLoaded();
return true;
} catch (err) {
console.error('Reload modal not showed up');
timeout && console.error('Reload modal has not showed up');
return false;
}
};
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/ts/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ export class AppComponent implements OnInit, AfterViewInit {
}

private watchDDocChanges() {
this.updateServiceWorker.update(() => this.ngZone.run(() => this.showUpdateReady()));

this.changesService.subscribe({
key: 'ddoc',
filter: (change) => {
Expand Down
2 changes: 0 additions & 2 deletions webapp/src/ts/services/form.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import * as medicXpathExtensions from '../../js/enketo/medic-xpath-extensions';
import { DbService } from '@mm-services/db.service';
import { FileReaderService } from '@mm-services/file-reader.service';
import { LineageModelGeneratorService } from '@mm-services/lineage-model-generator.service';
import { SearchService } from '@mm-services/search.service';
import { SubmitFormBySmsService } from '@mm-services/submit-form-by-sms.service';
import { UserContactService } from '@mm-services/user-contact.service';
import { XmlFormsService } from '@mm-services/xml-forms.service';
Expand Down Expand Up @@ -46,7 +45,6 @@ export class FormService {
private dbService: DbService,
private fileReaderService: FileReaderService,
private lineageModelGeneratorService: LineageModelGeneratorService,
private searchService: SearchService,
private submitFormBySmsService: SubmitFormBySmsService,
private userContactService: UserContactService,
private userSettingsService:UserSettingsService,
Expand Down
24 changes: 22 additions & 2 deletions webapp/tests/karma/ts/app.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { UserSettingsService } from '@mm-services/user-settings.service';
import { FormService } from '@mm-services/form.service';
import { OLD_NAV_PERMISSION } from '@mm-components/header/header.component';
import { SidebarMenuComponent } from '@mm-components/sidebar-menu/sidebar-menu.component';
import { ReloadingComponent } from '@mm-modals/reloading/reloading.component';

describe('AppComponent', () => {
let component: AppComponent;
Expand Down Expand Up @@ -89,6 +90,7 @@ describe('AppComponent', () => {
let trainingCardsService;
let userSettingsService;
let formService;
let updateServiceWorkerService;
// End Services

let globalActions;
Expand Down Expand Up @@ -123,7 +125,11 @@ describe('AppComponent', () => {
unreadRecordsService = { init: sinon.stub() };
setLanguageService = { set: sinon.stub() };
translateService = { instant: sinon.stub().returnsArg(0) };
modalService = { show: sinon.stub().resolves() };
modalService = {
show: sinon.stub().returns({
afterClosed: sinon.stub().returns(of())
})
};
browserDetectorService = { isUsingOutdatedBrowser: sinon.stub().returns(false) };
chtDatasourceService = { isInitialized: sinon.stub() };
analyticsModulesService = { get: sinon.stub() };
Expand Down Expand Up @@ -175,6 +181,7 @@ describe('AppComponent', () => {
trainingCardsService = { initTrainingCards: sinon.stub() };
userSettingsService = { get: sinon.stub().resolves({ facility_id: ['facility'], contact_id: 'contact' }) };
formService = { setUserContext: sinon.stub() };
updateServiceWorkerService = { update: sinon.stub() };
consoleErrorStub = sinon.stub(console, 'error');

const mockedSelectors = [
Expand Down Expand Up @@ -203,7 +210,7 @@ describe('AppComponent', () => {
{ provide: AuthService, useValue: authService },
{ provide: ResourceIconsService, useValue: resourceIconsService },
{ provide: ChangesService, useValue: changesService },
{ provide: UpdateServiceWorkerService, useValue: {} },
{ provide: UpdateServiceWorkerService, useValue: updateServiceWorkerService },
{ provide: LocationService, useValue: locationService },
{ provide: ModalService, useValue: modalService },
{ provide: BrowserDetectorService, useValue: browserDetectorService},
Expand Down Expand Up @@ -282,6 +289,19 @@ describe('AppComponent', () => {
expect(userSettingsService.get.calledOnce).to.equal(true);
expect(globalActions.setUserFacilityIds.calledOnceWith(['facility'])).to.equal(true);
expect(globalActions.setUserContactId.calledOnceWith('contact')).to.equal(true);
expect(updateServiceWorkerService.update.callCount).to.equal(1);
});

it('should show reload popup when service worker is updated', async () => {
await getComponent();
await component.setupPromise;

expect(updateServiceWorkerService.update.callCount).to.equal(1);
const callback = updateServiceWorkerService.update.args[0][0];
callback();
expect(modalService.show.calledOnce).to.be.true;
expect(modalService.show.args[0]).to.have.deep.members([ReloadingComponent]);

});

it('should display browser compatibility modal if using outdated chrome browser', async () => {
Expand Down
Loading