From b8750edfaeaf64a1c04e69457715fb1e37efe989 Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Mon, 11 Dec 2023 18:13:14 +0700
Subject: [PATCH 01/13] ugly code request to get place's children
---
.../search/src/generate-search-requests.js | 25 ++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/shared-libs/search/src/generate-search-requests.js b/shared-libs/search/src/generate-search-requests.js
index 894c70e002b..5a1469d95dc 100644
--- a/shared-libs/search/src/generate-search-requests.js
+++ b/shared-libs/search/src/generate-search-requests.js
@@ -121,6 +121,14 @@ const subjectRequest = function(filters) {
return getRequestWithMappedKeys('medic-client/reports_by_subject', subjectIds);
};
+const getContactParentRequest = function(filters) {
+ if (!filters.parent) {
+ return;
+ }
+
+ return getRequestWithMappedKeys('medic-client/contacts_by_parent', filters.parents, getKeysArray);
+};
+
const contactTypeRequest = function(filters, sortByLastVisitedDate) {
if (!filters.types) {
return;
@@ -196,12 +204,26 @@ const requestBuilders = {
contacts: function(filters, extensions) {
const shouldSortByLastVisitedDate = module.exports.shouldSortByLastVisitedDate(extensions);
+
+ if (filters) {
+ filters.parents = [ '17f1df8a-b096-4d1e-823d-2723bf7d24ee' ];
+ }
+
+
const typeRequest = contactTypeRequest(filters, shouldSortByLastVisitedDate);
const hasTypeRequest = typeRequest && typeRequest.params.keys.length;
const freetextRequests = freetextRequest(filters, 'medic-client/contacts_by_freetext');
const hasFreetextRequests = freetextRequests && freetextRequests.length;
+
+
+
+ const parentContactRequest = getContactParentRequest(filters);
+
+
+
+
if (hasTypeRequest && hasFreetextRequests) {
const makeCombinedParams = function(freetextRequest, typeKey) {
@@ -235,7 +257,7 @@ const requestBuilders = {
return freetextRequests.map(_.partial(makeCombinedRequest, typeRequest, _));
}
- let requests = [ freetextRequests, typeRequest ];
+ let requests = [ freetextRequests, typeRequest, parentContactRequest ];
requests = _.compact(_.flatten(requests));
if (!requests.length) {
@@ -247,6 +269,7 @@ const requestBuilders = {
// result and we'll need it later for sorting
requests.push(sortByLastVisitedDate());
}
+ console.warn('search requests -> ', requests);
return requests;
}
};
From 6255b30d97fbafe3eeca6da89be88468f6e7c772 Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Wed, 13 Dec 2023 23:30:47 +0700
Subject: [PATCH 02/13] ugly code request to get place's children
---
.../src/ts/services/select2-search.service.ts | 29 +++++++++++++++++--
1 file changed, 27 insertions(+), 2 deletions(-)
diff --git a/webapp/src/ts/services/select2-search.service.ts b/webapp/src/ts/services/select2-search.service.ts
index 80277f2d0a6..9163d5a9243 100644
--- a/webapp/src/ts/services/select2-search.service.ts
+++ b/webapp/src/ts/services/select2-search.service.ts
@@ -1,5 +1,7 @@
import { Injectable } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
import { sortBy as _sortBy } from 'lodash-es';
+import { Store } from '@ngrx/store';
import * as phoneNumber from '@medic/phone-number';
import { FormatProvider } from '@mm-providers/format.provider';
@@ -9,13 +11,17 @@ import { SessionService } from '@mm-services/session.service';
import { SettingsService } from '@mm-services/settings.service';
import { ContactMutedService } from '@mm-services/contact-muted.service';
import { TranslateService } from '@mm-services/translate.service';
+import { Selectors } from '@mm-selectors/index';
@Injectable({
providedIn: 'root'
})
export class Select2SearchService {
+ private currentTab;
constructor(
+ private store: Store,
+ private route: ActivatedRoute,
private formatProvider: FormatProvider,
private translateService: TranslateService,
private lineageModelGeneratorService: LineageModelGeneratorService,
@@ -23,7 +29,9 @@ export class Select2SearchService {
private sessionService: SessionService,
private settingsService: SettingsService,
private contactMutedService: ContactMutedService
- ) { }
+ ) {
+ this.subscribeToStore();
+ }
private defaultTemplateResult(row) {
if (!row.doc) {
@@ -56,7 +64,8 @@ export class Select2SearchService {
const skip = ((params.data.page || 1) - 1) * options.pageSize;
const filters = {
types: { selected: types },
- search: params.data.q
+ search: params.data.q,
+ ...this.getFilterContactByParent()
};
const searchOptions = {
limit: options.pageSize,
@@ -195,6 +204,22 @@ export class Select2SearchService {
}
}
+ private subscribeToStore() {
+ this.store
+ .select(Selectors.getCurrentTab)
+ .subscribe(currentTab => this.currentTab = currentTab);
+ }
+
+ private getFilterContactByParent() {
+ const contactId = this.route?.snapshot?.params?.id;
+
+ if (this.currentTab !== 'contacts' || !contactId) {
+ return;
+ }
+
+ return { parents: [ contactId ] };
+ }
+
async init(selectEl, _types, _options:any = {}) {
const settings = await this.settingsService.get();
const types = Array.isArray(_types) ? _types : [ _types ];
From 57560b5af3bc0f8a6b20d028c658d2dd19c6fd83 Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Thu, 14 Dec 2023 13:33:41 +0700
Subject: [PATCH 03/13] pretty code now
---
.../search/src/generate-search-requests.js | 106 +++++++++---------
.../src/js/enketo/widgets/db-object-widget.js | 3 +-
.../src/ts/services/select2-search.service.ts | 29 ++---
3 files changed, 63 insertions(+), 75 deletions(-)
diff --git a/shared-libs/search/src/generate-search-requests.js b/shared-libs/search/src/generate-search-requests.js
index 5a1469d95dc..1c9e00734a1 100644
--- a/shared-libs/search/src/generate-search-requests.js
+++ b/shared-libs/search/src/generate-search-requests.js
@@ -121,12 +121,18 @@ const subjectRequest = function(filters) {
return getRequestWithMappedKeys('medic-client/reports_by_subject', subjectIds);
};
-const getContactParentRequest = function(filters) {
+const getContactsByParentRequest = function(filters) {
if (!filters.parent) {
return;
}
- return getRequestWithMappedKeys('medic-client/contacts_by_parent', filters.parents, getKeysArray);
+ const types = filters?.types?.selected;
+ return {
+ view: 'medic-client/contacts_by_parent',
+ params: {
+ keys: types ? types.map(type => ([ filters.parent, type ])) : [ filters.parent ],
+ },
+ };
};
const contactTypeRequest = function(filters, sortByLastVisitedDate) {
@@ -183,6 +189,34 @@ const sortByLastVisitedDate = function() {
};
};
+const makeCombinedParams = function(freetextRequest, typeKey) {
+ const type = typeKey[0];
+ const params = {};
+ if (freetextRequest.key) {
+ params.key = [ type, freetextRequest.params.key[0] ];
+ } else {
+ params.startkey = [ type, freetextRequest.params.startkey[0] ];
+ params.endkey = [ type, freetextRequest.params.endkey[0] ];
+ }
+ return params;
+};
+
+const getContactsByTypeAndFreetextRequest = function(typeRequests, freetextRequest) {
+ const result = {
+ view: 'medic-client/contacts_by_type_freetext',
+ union: typeRequests.params.keys.length > 1
+ };
+
+ if (result.union) {
+ result.paramSets =
+ typeRequests.params.keys.map(_.partial(makeCombinedParams, freetextRequest, _));
+ return result;
+ }
+
+ result.params = makeCombinedParams(freetextRequest, typeRequests.params.keys[0]);
+ return result;
+};
+
const requestBuilders = {
reports: function(filters) {
let requests = [
@@ -204,72 +238,32 @@ const requestBuilders = {
contacts: function(filters, extensions) {
const shouldSortByLastVisitedDate = module.exports.shouldSortByLastVisitedDate(extensions);
-
- if (filters) {
- filters.parents = [ '17f1df8a-b096-4d1e-823d-2723bf7d24ee' ];
- }
-
-
- const typeRequest = contactTypeRequest(filters, shouldSortByLastVisitedDate);
- const hasTypeRequest = typeRequest && typeRequest.params.keys.length;
-
const freetextRequests = freetextRequest(filters, 'medic-client/contacts_by_freetext');
- const hasFreetextRequests = freetextRequests && freetextRequests.length;
-
-
-
-
- const parentContactRequest = getContactParentRequest(filters);
-
-
-
-
- if (hasTypeRequest && hasFreetextRequests) {
-
- const makeCombinedParams = function(freetextRequest, typeKey) {
- const type = typeKey[0];
- const params = {};
- if (freetextRequest.key) {
- params.key = [ type, freetextRequest.params.key[0] ];
- } else {
- params.startkey = [ type, freetextRequest.params.startkey[0] ];
- params.endkey = [ type, freetextRequest.params.endkey[0] ];
- }
- return params;
- };
-
- const makeCombinedRequest = function(typeRequests, freetextRequest) {
- const result = {
- view: 'medic-client/contacts_by_type_freetext',
- union: typeRequests.params.keys.length > 1
- };
-
- if (result.union) {
- result.paramSets =
- typeRequests.params.keys.map(_.partial(makeCombinedParams, freetextRequest, _));
- return result;
- }
-
- result.params = makeCombinedParams(freetextRequest, typeRequests.params.keys[0]);
- return result;
- };
+ const contactsByParentRequest = getContactsByParentRequest(filters);
+ const typeRequest = contactTypeRequest(filters, shouldSortByLastVisitedDate);
+ const hasTypeRequest = typeRequest?.params.keys.length;
- return freetextRequests.map(_.partial(makeCombinedRequest, typeRequest, _));
+ if (contactsByParentRequest && hasTypeRequest && !freetextRequests?.length) {
+ // The request's keys already have the type included.
+ return [ contactsByParentRequest ];
}
- let requests = [ freetextRequests, typeRequest, parentContactRequest ];
- requests = _.compact(_.flatten(requests));
+ if (hasTypeRequest && freetextRequests?.length) {
+ const combinedRequests = freetextRequests.map(_.partial(getContactsByTypeAndFreetextRequest, typeRequest, _));
+ contactsByParentRequest && combinedRequests.unshift(contactsByParentRequest);
+ return combinedRequests;
+ }
+ const requests = _.compact(_.flatten([ freetextRequests, typeRequest ]));
if (!requests.length) {
requests.push(defaultContactRequest());
}
if (shouldSortByLastVisitedDate) {
- // Always push this last, search:getIntersection uses the last request
- // result and we'll need it later for sorting
+ // Always push this last, search:getIntersection uses the last request's result, we'll need it later for sorting.
requests.push(sortByLastVisitedDate());
}
- console.warn('search requests -> ', requests);
+
return requests;
}
};
diff --git a/webapp/src/js/enketo/widgets/db-object-widget.js b/webapp/src/js/enketo/widgets/db-object-widget.js
index ad566b1e4a9..07a70acd1a7 100644
--- a/webapp/src/js/enketo/widgets/db-object-widget.js
+++ b/webapp/src/js/enketo/widgets/db-object-widget.js
@@ -55,7 +55,8 @@ const construct = ( element ) => {
}
const contactTypes = getContactTypes($question, $textInput);
const allowNew = $question.hasClass('or-appearance-allow-new');
- Select2Search.init($selectInput, contactTypes, { allowNew }).then(function() {
+ const filterByParent = $question.hasClass('or-appearance-with-same-parent');
+ Select2Search.init($selectInput, contactTypes, { allowNew, filterByParent }).then(function() {
// select2 doesn't understand readonly
$selectInput.prop('disabled', $textInput.prop('readonly'));
});
diff --git a/webapp/src/ts/services/select2-search.service.ts b/webapp/src/ts/services/select2-search.service.ts
index 9163d5a9243..3d2f2958a98 100644
--- a/webapp/src/ts/services/select2-search.service.ts
+++ b/webapp/src/ts/services/select2-search.service.ts
@@ -1,7 +1,6 @@
import { Injectable } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { sortBy as _sortBy } from 'lodash-es';
-import { Store } from '@ngrx/store';
import * as phoneNumber from '@medic/phone-number';
import { FormatProvider } from '@mm-providers/format.provider';
@@ -11,16 +10,13 @@ import { SessionService } from '@mm-services/session.service';
import { SettingsService } from '@mm-services/settings.service';
import { ContactMutedService } from '@mm-services/contact-muted.service';
import { TranslateService } from '@mm-services/translate.service';
-import { Selectors } from '@mm-selectors/index';
@Injectable({
providedIn: 'root'
})
export class Select2SearchService {
- private currentTab;
constructor(
- private store: Store,
private route: ActivatedRoute,
private formatProvider: FormatProvider,
private translateService: TranslateService,
@@ -29,9 +25,7 @@ export class Select2SearchService {
private sessionService: SessionService,
private settingsService: SettingsService,
private contactMutedService: ContactMutedService
- ) {
- this.subscribeToStore();
- }
+ ) { }
private defaultTemplateResult(row) {
if (!row.doc) {
@@ -65,7 +59,7 @@ export class Select2SearchService {
const filters = {
types: { selected: types },
search: params.data.q,
- ...this.getFilterContactByParent()
+ parent: options.filterByParent && this.getContactId(),
};
const searchOptions = {
limit: options.pageSize,
@@ -204,20 +198,18 @@ export class Select2SearchService {
}
}
- private subscribeToStore() {
- this.store
- .select(Selectors.getCurrentTab)
- .subscribe(currentTab => this.currentTab = currentTab);
- }
-
- private getFilterContactByParent() {
- const contactId = this.route?.snapshot?.params?.id;
+ private getContactId() {
+ let activeRoute = this.route.firstChild;
+ while (activeRoute?.firstChild) {
+ activeRoute = activeRoute.firstChild;
+ }
- if (this.currentTab !== 'contacts' || !contactId) {
+ const contactId = activeRoute?.snapshot?.params?.id;
+ if (!contactId) {
return;
}
- return { parents: [ contactId ] };
+ return contactId;
}
async init(selectEl, _types, _options:any = {}) {
@@ -229,6 +221,7 @@ export class Select2SearchService {
initialValue: _options.initialValue || selectEl.val(),
sendMessageExtras: _options.sendMessageExtras || this.defaultSendMessageExtras,
allowNew: _options.allowNew || false,
+ filterByParent: _options.filterByParent || false,
pageSize: _options.pageSize || 20,
tags: _options.tags || false,
templateResult: _options.templateResult || this.defaultTemplateResult.bind(this)
From 3367b8092e2b88ef8506ca9f8557f91115461f3c Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Thu, 14 Dec 2023 20:39:43 +0700
Subject: [PATCH 04/13] coverage
---
.../search/src/generate-search-requests.js | 2 +-
.../search/test/generate-search-requests.js | 57 +++++++++++++++
.../src/ts/services/select2-search.service.ts | 8 ++-
.../services/select2-search.service.spec.ts | 69 +++++++++++++++++--
4 files changed, 130 insertions(+), 6 deletions(-)
diff --git a/shared-libs/search/src/generate-search-requests.js b/shared-libs/search/src/generate-search-requests.js
index 1c9e00734a1..b67517f413f 100644
--- a/shared-libs/search/src/generate-search-requests.js
+++ b/shared-libs/search/src/generate-search-requests.js
@@ -254,7 +254,7 @@ const requestBuilders = {
return combinedRequests;
}
- const requests = _.compact(_.flatten([ freetextRequests, typeRequest ]));
+ const requests = _.compact(_.flatten([ freetextRequests, typeRequest, contactsByParentRequest ]));
if (!requests.length) {
requests.push(defaultContactRequest());
}
diff --git a/shared-libs/search/test/generate-search-requests.js b/shared-libs/search/test/generate-search-requests.js
index 792c96744f7..38a61fea8e1 100644
--- a/shared-libs/search/test/generate-search-requests.js
+++ b/shared-libs/search/test/generate-search-requests.js
@@ -168,6 +168,63 @@ describe('GenerateSearchRequests service', function() {
});
});
+ it('creates request to filter contacts by parent when contact ID and types are provided', function() {
+ const filters = {
+ types: {
+ selected: [ 'person' ],
+ },
+ parent: 'S-123',
+ };
+
+ const result = service('contacts', filters);
+
+ chai.expect(result.length).to.equal(1);
+ chai.expect(result[0]).to.deep.equal({
+ view: 'medic-client/contacts_by_parent',
+ params: {
+ keys: [ [ 'S-123', 'person' ] ],
+ },
+ });
+ });
+
+ it('creates request to filter contacts by parent and freetext', function() {
+ const filters = {
+ types: { selected: [ 'person' ] },
+ search: 'someth',
+ parent: 'S-123',
+ };
+
+ const result = service('contacts', filters);
+
+ chai.expect(result.length).to.equal(2);
+ chai.expect(result[0]).to.deep.equal({
+ view: 'medic-client/contacts_by_parent',
+ params: {
+ keys: [ [ 'S-123', 'person' ] ],
+ },
+ });
+ chai.expect(result[1]).to.deep.equal({
+ view: 'medic-client/contacts_by_type_freetext',
+ union: false,
+ params: {
+ endkey: [ 'person', 'someth\ufff0' ],
+ startkey: [ 'person', 'someth' ],
+ },
+ });
+ });
+
+ it('creates request to filter contacts by parent when types are not provided', function() {
+ const filters = { parent: 'S-123' };
+
+ const result = service('contacts', filters);
+
+ chai.expect(result.length).to.equal(1);
+ chai.expect(result[0]).to.deep.equal({
+ view: 'medic-client/contacts_by_parent',
+ params: { keys: [ 'S-123' ] },
+ });
+ });
+
it('creates unfiltered contacts request for types filter when all options are selected', function() {
const filters = {
types: {
diff --git a/webapp/src/ts/services/select2-search.service.ts b/webapp/src/ts/services/select2-search.service.ts
index 3d2f2958a98..2302a567ec6 100644
--- a/webapp/src/ts/services/select2-search.service.ts
+++ b/webapp/src/ts/services/select2-search.service.ts
@@ -56,10 +56,16 @@ export class Select2SearchService {
private query(params, successCb, failureCb, options, types) {
const currentQuery = params.data.q;
const skip = ((params.data.page || 1) - 1) * options.pageSize;
+
+ let parent;
+ if (options.filterByParent) {
+ parent = this.getContactId();
+ }
+
const filters = {
types: { selected: types },
search: params.data.q,
- parent: options.filterByParent && this.getContactId(),
+ parent,
};
const searchOptions = {
limit: options.pageSize,
diff --git a/webapp/tests/karma/ts/services/select2-search.service.spec.ts b/webapp/tests/karma/ts/services/select2-search.service.spec.ts
index ee487c273ef..85fa6292721 100644
--- a/webapp/tests/karma/ts/services/select2-search.service.spec.ts
+++ b/webapp/tests/karma/ts/services/select2-search.service.spec.ts
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import sinon from 'sinon';
-import { TestBed } from '@angular/core/testing';
+import { ActivatedRoute } from '@angular/router';
+import { TestBed, fakeAsync, flush } from '@angular/core/testing';
import { TranslateFakeLoader, TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { ContactMutedService } from '@mm-services/contact-muted.service';
@@ -17,7 +18,8 @@ describe('Select2SearchService', () => {
let lineageModelGeneratorService;
let sessionService;
let settingsService;
-
+ let searchService;
+ let activatedRoute;
let selectEl;
let val;
let select2Val;
@@ -35,7 +37,8 @@ describe('Select2SearchService', () => {
contactMutedService = {
getMuted: sinon.stub().returns(false)
};
-
+ searchService = { search: sinon.stub().resolves() };
+ activatedRoute = { firstChild: {} };
val = '';
select2Val = [{}];
selectEl = {
@@ -59,9 +62,10 @@ describe('Select2SearchService', () => {
TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateFakeLoader } }),
],
providers: [
+ { provide: ActivatedRoute, useValue: activatedRoute },
{ provide: ContactMutedService, useValue: contactMutedService },
{ provide: LineageModelGeneratorService, useValue: lineageModelGeneratorService },
- { provide: SearchService, useValue: { } },
+ { provide: SearchService, useValue: searchService },
{ provide: SessionService, useValue: sessionService },
{ provide: SettingsService, useValue: settingsService },
]
@@ -124,5 +128,62 @@ describe('Select2SearchService', () => {
expect(selectEl.trigger.callCount).to.equal(1);
expect(selectEl.trigger.args[0]).to.deep.equal([ 'change' ]); // the change is notified to the component
});
+
+ it('should set the filter by parent contact and search', fakeAsync(async () => {
+ activatedRoute.firstChild = { firstChild: { firstChild: { snapshot: { params: { id: 'A-123' } } } } };
+
+ await service.init(selectEl, [ 'person' ], { initialValue: '', filterByParent: true });
+
+ const selectConfig = selectEl.select2.args[0][0];
+ selectConfig.ajax.transport({ data: { q: 'Eric' } }, () => {}, () => {});
+ flush();
+
+ expect(searchService.search.calledOnce).to.be.true;
+ expect(searchService.search.args[0][0]).to.equal('contacts');
+ expect(searchService.search.args[0][1]).to.deep.equal({
+ types: { selected: [ 'person' ] },
+ search: 'Eric',
+ parent: 'A-123'
+ });
+ expect(searchService.search.args[0][2]).to.deep.equal({ limit: 20, skip: 0, hydrateContactNames: true });
+ }));
+
+ it('should not set the filter by parent contact when no contact ID', fakeAsync(async () => {
+ activatedRoute.firstChild = { firstChild: { snapshot: { params: { id: null } } } };
+
+ await service.init(selectEl, [ 'person' ], { initialValue: '', filterByParent: true });
+
+ const selectConfig = selectEl.select2.args[0][0];
+ selectConfig.ajax.transport({ data: { q: 'Eric' } }, () => {}, () => {});
+ flush();
+
+ expect(searchService.search.calledOnce).to.be.true;
+ expect(searchService.search.args[0][0]).to.equal('contacts');
+ expect(searchService.search.args[0][1]).to.deep.equal({
+ types: { selected: [ 'person' ] },
+ search: 'Eric',
+ parent: undefined
+ });
+ expect(searchService.search.args[0][2]).to.deep.equal({ limit: 20, skip: 0, hydrateContactNames: true });
+ }));
+
+ it('should not set the filter by parent contact when filterByParent turn off', fakeAsync(async () => {
+ activatedRoute.firstChild = { firstChild: { firstChild: { snapshot: { params: { id: 'A-123' } } } } };
+
+ await service.init(selectEl, [ 'person' ], { initialValue: '', filterByParent: false });
+
+ const selectConfig = selectEl.select2.args[0][0];
+ selectConfig.ajax.transport({ data: { q: 'Eric' } }, () => {}, () => {});
+ flush();
+
+ expect(searchService.search.calledOnce).to.be.true;
+ expect(searchService.search.args[0][0]).to.equal('contacts');
+ expect(searchService.search.args[0][1]).to.deep.equal({
+ types: { selected: [ 'person' ] },
+ search: 'Eric',
+ parent: undefined
+ });
+ expect(searchService.search.args[0][2]).to.deep.equal({ limit: 20, skip: 0, hydrateContactNames: true });
+ }));
});
});
From fb4a1a5f46a16ca28410458b9e7f7fce22e86c98 Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Fri, 15 Dec 2023 12:48:15 +0700
Subject: [PATCH 05/13] adding type and some clean up
---
.../ts/modules/contacts/contacts.component.ts | 6 ++--
.../ts/modules/reports/reports.component.ts | 4 +--
.../contact-view-model-generator.service.ts | 4 +--
webapp/src/ts/services/search.service.ts | 9 +++++-
.../src/ts/services/select2-search.service.ts | 30 +++++++------------
.../karma/ts/services/search.service.spec.ts | 8 ++---
.../services/select2-search.service.spec.ts | 3 +-
7 files changed, 31 insertions(+), 33 deletions(-)
diff --git a/webapp/src/ts/modules/contacts/contacts.component.ts b/webapp/src/ts/modules/contacts/contacts.component.ts
index 85dc4218bbc..66e5a347170 100644
--- a/webapp/src/ts/modules/contacts/contacts.component.ts
+++ b/webapp/src/ts/modules/contacts/contacts.component.ts
@@ -15,7 +15,7 @@ import { AuthService } from '@mm-services/auth.service';
import { SettingsService } from '@mm-services/settings.service';
import { UHCSettingsService } from '@mm-services/uhc-settings.service';
import { Selectors } from '@mm-selectors/index';
-import { SearchService } from '@mm-services/search.service';
+import { Filter, SearchService } from '@mm-services/search.service';
import { ContactTypesService } from '@mm-services/contact-types.service';
import { RelativeDateService } from '@mm-services/relative-date.service';
import { ScrollLoaderProvider } from '@mm-providers/scroll-loader.provider';
@@ -44,8 +44,8 @@ export class ContactsComponent implements OnInit, AfterViewInit, OnDestroy {
error;
appending: boolean;
hasContacts = true;
- filters:any = {};
- defaultFilters:any = {};
+ filters: Filter = {};
+ defaultFilters: Filter = {};
moreItems;
usersHomePlace;
contactTypes;
diff --git a/webapp/src/ts/modules/reports/reports.component.ts b/webapp/src/ts/modules/reports/reports.component.ts
index 68e804f9daf..4f51b3d63db 100644
--- a/webapp/src/ts/modules/reports/reports.component.ts
+++ b/webapp/src/ts/modules/reports/reports.component.ts
@@ -9,7 +9,7 @@ import { GlobalActions } from '@mm-actions/global';
import { ReportsActions } from '@mm-actions/reports';
import { ServicesActions } from '@mm-actions/services';
import { ChangesService } from '@mm-services/changes.service';
-import { SearchService } from '@mm-services/search.service';
+import { Filter, SearchService } from '@mm-services/search.service';
import { Selectors } from '@mm-selectors/index';
import { AddReadStatusService } from '@mm-services/add-read-status.service';
import { ExportService } from '@mm-services/export.service';
@@ -52,7 +52,7 @@ export class ReportsComponent implements OnInit, AfterViewInit, OnDestroy {
loading = true;
appending = false;
moreItems: boolean;
- filters:any = {};
+ filters: Filter = {};
hasReports: boolean;
selectMode = false;
selectModeAvailable = false;
diff --git a/webapp/src/ts/services/contact-view-model-generator.service.ts b/webapp/src/ts/services/contact-view-model-generator.service.ts
index f68eea09d4e..6021486569a 100644
--- a/webapp/src/ts/services/contact-view-model-generator.service.ts
+++ b/webapp/src/ts/services/contact-view-model-generator.service.ts
@@ -307,9 +307,9 @@ export class ContactViewModelGeneratorService {
contactDocs.forEach((doc) => {
subjectIds.push(registrationUtils.getSubjectIds(doc));
});
- const searchOptions = { subjectIds: _flattenDeep(subjectIds) };
+ const filter = { subjectIds: _flattenDeep(subjectIds) };
return this.searchService
- .search('reports', searchOptions, { include_docs: true })
+ .search('reports', filter, { include_docs: true })
.then((reports) => {
reports.forEach((report) => {
report.valid = !report.errors || !report.errors.length;
diff --git a/webapp/src/ts/services/search.service.ts b/webapp/src/ts/services/search.service.ts
index 93a86afa20b..900f3eed14c 100644
--- a/webapp/src/ts/services/search.service.ts
+++ b/webapp/src/ts/services/search.service.ts
@@ -122,7 +122,7 @@ export class SearchService {
});
}
- search(type, filters, options:any = {}, extensions:any = {}, docIds: any[] | undefined = undefined) {
+ search(type, filters: Filter, options:any = {}, extensions:any = {}, docIds: any[] | undefined = undefined) {
return this.ngZone.runOutsideAngular(() => this._search(type, filters, options, extensions, docIds));
}
@@ -201,3 +201,10 @@ export class SearchService {
});
}
}
+
+export interface Filter {
+ types?: { selected: string };
+ search?: string;
+ parent?: string;
+ subjectIds?: string[];
+}
diff --git a/webapp/src/ts/services/select2-search.service.ts b/webapp/src/ts/services/select2-search.service.ts
index 2302a567ec6..ad541a9af19 100644
--- a/webapp/src/ts/services/select2-search.service.ts
+++ b/webapp/src/ts/services/select2-search.service.ts
@@ -5,7 +5,7 @@ import * as phoneNumber from '@medic/phone-number';
import { FormatProvider } from '@mm-providers/format.provider';
import { LineageModelGeneratorService } from '@mm-services/lineage-model-generator.service';
-import { SearchService } from '@mm-services/search.service';
+import { Filter, SearchService } from '@mm-services/search.service';
import { SessionService } from '@mm-services/session.service';
import { SettingsService } from '@mm-services/settings.service';
import { ContactMutedService } from '@mm-services/contact-muted.service';
@@ -55,24 +55,21 @@ export class Select2SearchService {
private query(params, successCb, failureCb, options, types) {
const currentQuery = params.data.q;
- const skip = ((params.data.page || 1) - 1) * options.pageSize;
- let parent;
- if (options.filterByParent) {
- parent = this.getContactId();
- }
-
- const filters = {
- types: { selected: types },
- search: params.data.q,
- parent,
- };
const searchOptions = {
limit: options.pageSize,
- skip,
+ skip: ((params.data.page || 1) - 1) * options.pageSize,
hydrateContactNames: true,
};
+ const filters: Filter = {
+ types: { selected: types },
+ search: params.data.q,
+ };
+ if (options.filterByParent) {
+ filters.parent = this.getContactId();
+ }
+
this.searchService
.search('contacts', filters, searchOptions)
.then((documents) => {
@@ -210,12 +207,7 @@ export class Select2SearchService {
activeRoute = activeRoute.firstChild;
}
- const contactId = activeRoute?.snapshot?.params?.id;
- if (!contactId) {
- return;
- }
-
- return contactId;
+ return activeRoute?.snapshot?.params?.id;
}
async init(selectEl, _types, _options:any = {}) {
diff --git a/webapp/tests/karma/ts/services/search.service.spec.ts b/webapp/tests/karma/ts/services/search.service.spec.ts
index 6690a8939cd..884ed26e865 100644
--- a/webapp/tests/karma/ts/services/search.service.spec.ts
+++ b/webapp/tests/karma/ts/services/search.service.spec.ts
@@ -87,7 +87,7 @@ describe('Search service', () => {
.onSecondCall().resolves([ { id: 'b' } ]);
let firstReturned = false;
- const filters = { foo: 'bar' };
+ const filters = { search: 'bar' };
service
.search('reports', filters)
.then((actual) => {
@@ -96,7 +96,7 @@ describe('Search service', () => {
})
.catch(err => assert.fail(err));
- filters.foo = 'test';
+ filters.search = 'test';
service
.search('reports', filters)
.then((actual) => {
@@ -112,7 +112,7 @@ describe('Search service', () => {
.onSecondCall().resolves([ { id: 'b' } ]);
let firstReturned = false;
service
- .search('reports', { freetext: 'first' })
+ .search('reports', { search: 'first' })
.then((actual) => {
expect(actual).to.deep.equal([ { id: 'a' } ]);
firstReturned = true;
@@ -120,7 +120,7 @@ describe('Search service', () => {
.catch(err => assert.fail(err));
service
- .search('reports', { freetext: 'second' })
+ .search('reports', { search: 'second' })
.then((actual) => {
expect(actual).to.deep.equal([ { id: 'b' } ]);
expect(firstReturned).to.equal(true);
diff --git a/webapp/tests/karma/ts/services/select2-search.service.spec.ts b/webapp/tests/karma/ts/services/select2-search.service.spec.ts
index 85fa6292721..6bafe4d193e 100644
--- a/webapp/tests/karma/ts/services/select2-search.service.spec.ts
+++ b/webapp/tests/karma/ts/services/select2-search.service.spec.ts
@@ -149,7 +149,7 @@ describe('Select2SearchService', () => {
}));
it('should not set the filter by parent contact when no contact ID', fakeAsync(async () => {
- activatedRoute.firstChild = { firstChild: { snapshot: { params: { id: null } } } };
+ activatedRoute.firstChild = { firstChild: { snapshot: { params: undefined } } };
await service.init(selectEl, [ 'person' ], { initialValue: '', filterByParent: true });
@@ -181,7 +181,6 @@ describe('Select2SearchService', () => {
expect(searchService.search.args[0][1]).to.deep.equal({
types: { selected: [ 'person' ] },
search: 'Eric',
- parent: undefined
});
expect(searchService.search.args[0][2]).to.deep.equal({ limit: 20, skip: 0, hydrateContactNames: true });
}));
From fb5215bfbe5941cce52d1a215a0285ef9a6b15ca Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Fri, 15 Dec 2023 13:42:35 +0700
Subject: [PATCH 06/13] sonar
---
webapp/src/ts/services/select2-search.service.ts | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/webapp/src/ts/services/select2-search.service.ts b/webapp/src/ts/services/select2-search.service.ts
index ad541a9af19..ae712910b0a 100644
--- a/webapp/src/ts/services/select2-search.service.ts
+++ b/webapp/src/ts/services/select2-search.service.ts
@@ -53,12 +53,16 @@ export class Select2SearchService {
.map(doc => ({ id: doc._id, doc: doc }));
}
+ private calculateSkip(page: number, pageSize: number): number {
+ return ((page || 1) - 1) * pageSize;
+ }
+
private query(params, successCb, failureCb, options, types) {
const currentQuery = params.data.q;
const searchOptions = {
limit: options.pageSize,
- skip: ((params.data.page || 1) - 1) * options.pageSize,
+ skip: this.calculateSkip(params.data.page, options.pageSize),
hydrateContactNames: true,
};
From 8ae2c1c345a682d94123af7cea6991b3c0b9d4b4 Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Fri, 15 Dec 2023 16:00:25 +0700
Subject: [PATCH 07/13] e2e setup
---
.../enketo/db-object-widget.wdio-spec.js | 58 +++++++++++++++++++
.../default/enketo/forms/db-object-widget.xml | 32 ++++++++++
2 files changed, 90 insertions(+)
create mode 100644 tests/e2e/default/enketo/db-object-widget.wdio-spec.js
create mode 100644 tests/e2e/default/enketo/forms/db-object-widget.xml
diff --git a/tests/e2e/default/enketo/db-object-widget.wdio-spec.js b/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
new file mode 100644
index 00000000000..d1e8d2cef3c
--- /dev/null
+++ b/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
@@ -0,0 +1,58 @@
+const moment = require('moment');
+const fs = require('fs');
+
+const utils = require('@utils');
+const commonPage = require('@page-objects/default/common/common.wdio.page');
+const loginPage = require('@page-objects/default/login/login.wdio.page');
+const userFactory = require('@factories/cht/users/users');
+const placeFactory = require('@factories/cht/contacts/place');
+const personFactory = require('@factories/cht/contacts/person');
+const contactPage = require('@page-objects/default/contacts/contacts.wdio.page');
+const genericForm = require('@page-objects/default/enketo/generic-form.wdio.page');
+
+describe('DB Object Widget', () => {
+ const formId = 'db-object-widget';
+ const form = fs.readFileSync(`${__dirname}/forms/${formId}.xml`, 'utf8');
+ const formDocument = {
+ _id: `form:${formId}`,
+ internalId: formId,
+ title: `Form ${formId}`,
+ type: 'form',
+ context: { person: true, place: true },
+ _attachments: {
+ xml: {
+ content_type: 'application/octet-stream',
+ data: Buffer.from(form).toString('base64')
+ }
+ }
+ };
+
+ const places = placeFactory.generateHierarchy();
+ const districtHospital = places.get('district_hospital');
+ const area1 = places.get('health_center');
+ const area2 = placeFactory.place().build({
+ _id: 'area2',
+ name: 'area 2',
+ type: 'health_center',
+ parent: { _id: districtHospital._id }
+ });
+
+ const offlineUser = userFactory.build({ place: districtHospital._id, roles: [ 'chw' ] });
+ const personArea1 = personFactory.build({ parent: {_id: area1._id, parent: area1.parent} });
+ const personArea2 = personFactory.build({name: 'Patricio', parent: {_id: area2._id, parent: area2.parent} });
+
+ before(async () => {
+ await utils.saveDocs([ ...places.values(), area2, personArea1, personArea2, formDocument ]);
+ await utils.createUsers([ offlineUser ]);
+ await loginPage.login(offlineUser);
+ });
+
+ it('should display only the contacts from the parent contact', async () => {
+ await commonPage.goToPeople(area1._id);
+ await commonPage.openFastActionReport(formId);
+ await browser.debug();
+
+ expect('hola').to.equal('Health facility');
+ });
+
+});
diff --git a/tests/e2e/default/enketo/forms/db-object-widget.xml b/tests/e2e/default/enketo/forms/db-object-widget.xml
new file mode 100644
index 00000000000..8b8a23212a2
--- /dev/null
+++ b/tests/e2e/default/enketo/forms/db-object-widget.xml
@@ -0,0 +1,32 @@
+
+
+
+ DB Object Form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 4af3b055f35b3242d34a448633ec263ad9f41404 Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Mon, 18 Dec 2023 14:24:09 +0700
Subject: [PATCH 08/13] e2e no flaky
---
.../enketo/db-object-widget.wdio-spec.js | 33 ++++++++++++++-----
.../default/enketo/generic-form.wdio.page.js | 23 +++++++++++++
2 files changed, 48 insertions(+), 8 deletions(-)
diff --git a/tests/e2e/default/enketo/db-object-widget.wdio-spec.js b/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
index d1e8d2cef3c..42060bead1e 100644
--- a/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
+++ b/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
@@ -1,14 +1,13 @@
-const moment = require('moment');
const fs = require('fs');
const utils = require('@utils');
-const commonPage = require('@page-objects/default/common/common.wdio.page');
-const loginPage = require('@page-objects/default/login/login.wdio.page');
const userFactory = require('@factories/cht/users/users');
const placeFactory = require('@factories/cht/contacts/place');
const personFactory = require('@factories/cht/contacts/person');
-const contactPage = require('@page-objects/default/contacts/contacts.wdio.page');
+const commonPage = require('@page-objects/default/common/common.wdio.page');
+const loginPage = require('@page-objects/default/login/login.wdio.page');
const genericForm = require('@page-objects/default/enketo/generic-form.wdio.page');
+const reportsPage = require('@page-objects/default/reports/reports.wdio.page');
describe('DB Object Widget', () => {
const formId = 'db-object-widget';
@@ -38,8 +37,8 @@ describe('DB Object Widget', () => {
});
const offlineUser = userFactory.build({ place: districtHospital._id, roles: [ 'chw' ] });
- const personArea1 = personFactory.build({ parent: {_id: area1._id, parent: area1.parent} });
- const personArea2 = personFactory.build({name: 'Patricio', parent: {_id: area2._id, parent: area2.parent} });
+ const personArea1 = personFactory.build({ parent: { _id: area1._id, parent: area1.parent } });
+ const personArea2 = personFactory.build({ name: 'Patricio', parent: { _id: area2._id, parent: area2.parent } });
before(async () => {
await utils.saveDocs([ ...places.values(), area2, personArea1, personArea2, formDocument ]);
@@ -50,9 +49,27 @@ describe('DB Object Widget', () => {
it('should display only the contacts from the parent contact', async () => {
await commonPage.goToPeople(area1._id);
await commonPage.openFastActionReport(formId);
- await browser.debug();
- expect('hola').to.equal('Health facility');
+ const sameParent = await genericForm.getDBObjectWidgetValues('/db_object_form/people/person_test_same_parent');
+ expect(sameParent.length).to.equal(1);
+ expect(sameParent[0].name).to.equal('Mary Smith');
+ await sameParent[0].click();
+
+ const allContacts = await genericForm.getDBObjectWidgetValues('/db_object_form/people/person_test_all');
+ expect(allContacts.length).to.equal(3);
+ expect(allContacts[0].name).to.equal('Mary Smith');
+ expect(allContacts[1].name).to.equal('OfflineUser');
+ expect(allContacts[2].name).to.equal('Patricio');
+ await allContacts[1].click();
+
+ await genericForm.submitForm();
+ await commonPage.waitForPageLoaded();
+ await commonPage.goToReports();
+
+ const firstReport = await reportsPage.getListReportInfo(await reportsPage.firstReport());
+
+ expect(firstReport.heading).to.equal('OfflineUser');
+ expect(firstReport.form).to.equal('Form db-object-widget');
});
});
diff --git a/tests/page-objects/default/enketo/generic-form.wdio.page.js b/tests/page-objects/default/enketo/generic-form.wdio.page.js
index 1b61e776d37..9905c242f44 100644
--- a/tests/page-objects/default/enketo/generic-form.wdio.page.js
+++ b/tests/page-objects/default/enketo/generic-form.wdio.page.js
@@ -107,6 +107,28 @@ const selectYesNoOption = async (selector, value = 'yes') => {
return value === 'yes';
};
+const getDBObjectWidgetValues = async (field) => {
+ const widget = $(`[data-contains-ref-target="${field}"] .selection`);
+ await (await widget).waitForClickable();
+ await (await widget).click();
+
+ const dropdown = $('.select2-dropdown--below');
+ await (await dropdown).waitForDisplayed();
+ const firstElement = $('.select2-results__options > li');
+ await (await firstElement).waitForClickable();
+
+ const list = await $$('.select2-results__options > li');
+ const contacts = [];
+ for (const item of list) {
+ contacts.push({
+ name: await (item.$('.name').getText()),
+ click: () => item.click(),
+ });
+ }
+
+ return contacts;
+};
+
module.exports = {
getFormTitle,
getErrorMessage,
@@ -125,4 +147,5 @@ module.exports = {
currentFormView,
formTitle,
selectYesNoOption,
+ getDBObjectWidgetValues,
};
From 2bfcd45856a3f1c72bb2bf7e6b84ca370ba2abbe Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Mon, 18 Dec 2023 17:35:09 +0700
Subject: [PATCH 09/13] e2e no flaky take 2
---
.../enketo/db-object-widget.wdio-spec.js | 23 ++++++++++++-------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/tests/e2e/default/enketo/db-object-widget.wdio-spec.js b/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
index 42060bead1e..36f82321a4f 100644
--- a/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
+++ b/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
@@ -51,25 +51,32 @@ describe('DB Object Widget', () => {
await commonPage.openFastActionReport(formId);
const sameParent = await genericForm.getDBObjectWidgetValues('/db_object_form/people/person_test_same_parent');
- expect(sameParent.length).to.equal(1);
- expect(sameParent[0].name).to.equal('Mary Smith');
await sameParent[0].click();
+ expect(sameParent.length).to.equal(1);
+ expect(sameParent[0].name).to.equal(personArea1.name);
const allContacts = await genericForm.getDBObjectWidgetValues('/db_object_form/people/person_test_all');
+ await allContacts[2].click();
expect(allContacts.length).to.equal(3);
- expect(allContacts[0].name).to.equal('Mary Smith');
- expect(allContacts[1].name).to.equal('OfflineUser');
- expect(allContacts[2].name).to.equal('Patricio');
- await allContacts[1].click();
+ expect(allContacts[0].name).to.equal(personArea1.name);
+ expect(allContacts[1].name).to.equal(offlineUser.contact.name);
+ expect(allContacts[2].name).to.equal(personArea2.name);
await genericForm.submitForm();
await commonPage.waitForPageLoaded();
await commonPage.goToReports();
const firstReport = await reportsPage.getListReportInfo(await reportsPage.firstReport());
-
- expect(firstReport.heading).to.equal('OfflineUser');
+ expect(firstReport.heading).to.equal(offlineUser.contact.name);
expect(firstReport.form).to.equal('Form db-object-widget');
+
+ await reportsPage.openReport(firstReport.dataId);
+ expect(await reportsPage.getReportDetailFieldValueByLabel(
+ 'report.db-object-widget.people.person_test_same_parent'
+ )).to.equal(personArea1._id);
+ expect(await reportsPage.getReportDetailFieldValueByLabel(
+ 'report.db-object-widget.people.person_test_all'
+ )).to.equal(personArea2._id);
});
});
From 688a035d927673af458a84df3209f08849c9d72f Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Mon, 18 Dec 2023 18:16:47 +0700
Subject: [PATCH 10/13] feedback
---
.../search/src/generate-search-requests.js | 4 +++-
.../src/ts/services/select2-search.service.ts | 4 ++--
.../services/select2-search.service.spec.ts | 21 ++++++++++++++++++-
3 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/shared-libs/search/src/generate-search-requests.js b/shared-libs/search/src/generate-search-requests.js
index b67517f413f..e95da4f46eb 100644
--- a/shared-libs/search/src/generate-search-requests.js
+++ b/shared-libs/search/src/generate-search-requests.js
@@ -250,7 +250,9 @@ const requestBuilders = {
if (hasTypeRequest && freetextRequests?.length) {
const combinedRequests = freetextRequests.map(_.partial(getContactsByTypeAndFreetextRequest, typeRequest, _));
- contactsByParentRequest && combinedRequests.unshift(contactsByParentRequest);
+ if (contactsByParentRequest) {
+ combinedRequests.unshift(contactsByParentRequest);
+ }
return combinedRequests;
}
diff --git a/webapp/src/ts/services/select2-search.service.ts b/webapp/src/ts/services/select2-search.service.ts
index ae712910b0a..221d43f0fab 100644
--- a/webapp/src/ts/services/select2-search.service.ts
+++ b/webapp/src/ts/services/select2-search.service.ts
@@ -210,8 +210,8 @@ export class Select2SearchService {
while (activeRoute?.firstChild) {
activeRoute = activeRoute.firstChild;
}
-
- return activeRoute?.snapshot?.params?.id;
+ const params = activeRoute?.snapshot?.params;
+ return params?.parent_id || params?.id;
}
async init(selectEl, _types, _options:any = {}) {
diff --git a/webapp/tests/karma/ts/services/select2-search.service.spec.ts b/webapp/tests/karma/ts/services/select2-search.service.spec.ts
index 6bafe4d193e..6930ff5eb67 100644
--- a/webapp/tests/karma/ts/services/select2-search.service.spec.ts
+++ b/webapp/tests/karma/ts/services/select2-search.service.spec.ts
@@ -129,7 +129,7 @@ describe('Select2SearchService', () => {
expect(selectEl.trigger.args[0]).to.deep.equal([ 'change' ]); // the change is notified to the component
});
- it('should set the filter by parent contact and search', fakeAsync(async () => {
+ it('should set the filter by parent contact when app form is opened from contacts tab', fakeAsync(async () => {
activatedRoute.firstChild = { firstChild: { firstChild: { snapshot: { params: { id: 'A-123' } } } } };
await service.init(selectEl, [ 'person' ], { initialValue: '', filterByParent: true });
@@ -148,6 +148,25 @@ describe('Select2SearchService', () => {
expect(searchService.search.args[0][2]).to.deep.equal({ limit: 20, skip: 0, hydrateContactNames: true });
}));
+ it('should set the filter by parent contact when contact form is opened from contacts tab', fakeAsync(async () => {
+ activatedRoute.firstChild = { firstChild: { firstChild: { snapshot: { params: { parent_id: 'A-456' } } } } };
+
+ await service.init(selectEl, [ 'person' ], { initialValue: '', filterByParent: true });
+
+ const selectConfig = selectEl.select2.args[0][0];
+ selectConfig.ajax.transport({ data: { q: 'Eric' } }, () => {}, () => {});
+ flush();
+
+ expect(searchService.search.calledOnce).to.be.true;
+ expect(searchService.search.args[0][0]).to.equal('contacts');
+ expect(searchService.search.args[0][1]).to.deep.equal({
+ types: { selected: [ 'person' ] },
+ search: 'Eric',
+ parent: 'A-456'
+ });
+ expect(searchService.search.args[0][2]).to.deep.equal({ limit: 20, skip: 0, hydrateContactNames: true });
+ }));
+
it('should not set the filter by parent contact when no contact ID', fakeAsync(async () => {
activatedRoute.firstChild = { firstChild: { snapshot: { params: undefined } } };
From 95b2057fad8d1f5f20046a67892891847b6b9710 Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Tue, 19 Dec 2023 14:49:17 +0700
Subject: [PATCH 11/13] feedback
---
.../enketo/db-object-widget.wdio-spec.js | 56 ++++++++++---------
1 file changed, 29 insertions(+), 27 deletions(-)
diff --git a/tests/e2e/default/enketo/db-object-widget.wdio-spec.js b/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
index 36f82321a4f..58bb4a34ce1 100644
--- a/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
+++ b/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
@@ -9,36 +9,38 @@ const loginPage = require('@page-objects/default/login/login.wdio.page');
const genericForm = require('@page-objects/default/enketo/generic-form.wdio.page');
const reportsPage = require('@page-objects/default/reports/reports.wdio.page');
-describe('DB Object Widget', () => {
- const formId = 'db-object-widget';
- const form = fs.readFileSync(`${__dirname}/forms/${formId}.xml`, 'utf8');
- const formDocument = {
- _id: `form:${formId}`,
- internalId: formId,
- title: `Form ${formId}`,
- type: 'form',
- context: { person: true, place: true },
- _attachments: {
- xml: {
- content_type: 'application/octet-stream',
- data: Buffer.from(form).toString('base64')
- }
+// Test Data
+const formId = 'db-object-widget';
+const form = fs.readFileSync(`${__dirname}/forms/${formId}.xml`, 'utf8');
+const formDocument = {
+ _id: `form:${formId}`,
+ internalId: formId,
+ title: `Form ${formId}`,
+ type: 'form',
+ context: { person: true, place: true },
+ _attachments: {
+ xml: {
+ content_type: 'application/octet-stream',
+ data: Buffer.from(form).toString('base64')
}
- };
+ }
+};
- const places = placeFactory.generateHierarchy();
- const districtHospital = places.get('district_hospital');
- const area1 = places.get('health_center');
- const area2 = placeFactory.place().build({
- _id: 'area2',
- name: 'area 2',
- type: 'health_center',
- parent: { _id: districtHospital._id }
- });
+const places = placeFactory.generateHierarchy();
+const districtHospital = places.get('district_hospital');
+const area1 = places.get('health_center');
+const area2 = placeFactory.place().build({
+ _id: 'area2',
+ name: 'area 2',
+ type: 'health_center',
+ parent: { _id: districtHospital._id }
+});
- const offlineUser = userFactory.build({ place: districtHospital._id, roles: [ 'chw' ] });
- const personArea1 = personFactory.build({ parent: { _id: area1._id, parent: area1.parent } });
- const personArea2 = personFactory.build({ name: 'Patricio', parent: { _id: area2._id, parent: area2.parent } });
+const offlineUser = userFactory.build({ place: districtHospital._id, roles: [ 'chw' ] });
+const personArea1 = personFactory.build({ parent: { _id: area1._id, parent: area1.parent } });
+const personArea2 = personFactory.build({ name: 'Patricio', parent: { _id: area2._id, parent: area2.parent } });
+
+describe('DB Object Widget', () => {
before(async () => {
await utils.saveDocs([ ...places.values(), area2, personArea1, personArea2, formDocument ]);
From 5a039e4085fef3363b0d495730e5cd192049e97e Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Mon, 25 Dec 2023 15:26:29 +0700
Subject: [PATCH 12/13] renaming to "descendant-of-current-contact"
---
tests/e2e/default/enketo/forms/db-object-widget.xml | 2 +-
webapp/src/js/enketo/widgets/db-object-widget.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/e2e/default/enketo/forms/db-object-widget.xml b/tests/e2e/default/enketo/forms/db-object-widget.xml
index 8b8a23212a2..6f945b1cc65 100644
--- a/tests/e2e/default/enketo/forms/db-object-widget.xml
+++ b/tests/e2e/default/enketo/forms/db-object-widget.xml
@@ -21,7 +21,7 @@
-
+
diff --git a/webapp/src/js/enketo/widgets/db-object-widget.js b/webapp/src/js/enketo/widgets/db-object-widget.js
index 07a70acd1a7..81ca00ffc18 100644
--- a/webapp/src/js/enketo/widgets/db-object-widget.js
+++ b/webapp/src/js/enketo/widgets/db-object-widget.js
@@ -55,7 +55,7 @@ const construct = ( element ) => {
}
const contactTypes = getContactTypes($question, $textInput);
const allowNew = $question.hasClass('or-appearance-allow-new');
- const filterByParent = $question.hasClass('or-appearance-with-same-parent');
+ const filterByParent = $question.hasClass('or-appearance-descendant-of-current-contact');
Select2Search.init($selectInput, contactTypes, { allowNew, filterByParent }).then(function() {
// select2 doesn't understand readonly
$selectInput.prop('disabled', $textInput.prop('readonly'));
From 6e1082476a33add59dedbe2db4d7cd67db5d857a Mon Sep 17 00:00:00 2001
From: latin-panda <66472237+latin-panda@users.noreply.github.com>
Date: Thu, 4 Jan 2024 12:55:51 +0700
Subject: [PATCH 13/13] Moving back test data def
---
.../enketo/db-object-widget.wdio-spec.js | 56 +++++++++----------
1 file changed, 27 insertions(+), 29 deletions(-)
diff --git a/tests/e2e/default/enketo/db-object-widget.wdio-spec.js b/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
index 58bb4a34ce1..36f82321a4f 100644
--- a/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
+++ b/tests/e2e/default/enketo/db-object-widget.wdio-spec.js
@@ -9,38 +9,36 @@ const loginPage = require('@page-objects/default/login/login.wdio.page');
const genericForm = require('@page-objects/default/enketo/generic-form.wdio.page');
const reportsPage = require('@page-objects/default/reports/reports.wdio.page');
-// Test Data
-const formId = 'db-object-widget';
-const form = fs.readFileSync(`${__dirname}/forms/${formId}.xml`, 'utf8');
-const formDocument = {
- _id: `form:${formId}`,
- internalId: formId,
- title: `Form ${formId}`,
- type: 'form',
- context: { person: true, place: true },
- _attachments: {
- xml: {
- content_type: 'application/octet-stream',
- data: Buffer.from(form).toString('base64')
+describe('DB Object Widget', () => {
+ const formId = 'db-object-widget';
+ const form = fs.readFileSync(`${__dirname}/forms/${formId}.xml`, 'utf8');
+ const formDocument = {
+ _id: `form:${formId}`,
+ internalId: formId,
+ title: `Form ${formId}`,
+ type: 'form',
+ context: { person: true, place: true },
+ _attachments: {
+ xml: {
+ content_type: 'application/octet-stream',
+ data: Buffer.from(form).toString('base64')
+ }
}
- }
-};
-
-const places = placeFactory.generateHierarchy();
-const districtHospital = places.get('district_hospital');
-const area1 = places.get('health_center');
-const area2 = placeFactory.place().build({
- _id: 'area2',
- name: 'area 2',
- type: 'health_center',
- parent: { _id: districtHospital._id }
-});
+ };
-const offlineUser = userFactory.build({ place: districtHospital._id, roles: [ 'chw' ] });
-const personArea1 = personFactory.build({ parent: { _id: area1._id, parent: area1.parent } });
-const personArea2 = personFactory.build({ name: 'Patricio', parent: { _id: area2._id, parent: area2.parent } });
+ const places = placeFactory.generateHierarchy();
+ const districtHospital = places.get('district_hospital');
+ const area1 = places.get('health_center');
+ const area2 = placeFactory.place().build({
+ _id: 'area2',
+ name: 'area 2',
+ type: 'health_center',
+ parent: { _id: districtHospital._id }
+ });
-describe('DB Object Widget', () => {
+ const offlineUser = userFactory.build({ place: districtHospital._id, roles: [ 'chw' ] });
+ const personArea1 = personFactory.build({ parent: { _id: area1._id, parent: area1.parent } });
+ const personArea2 = personFactory.build({ name: 'Patricio', parent: { _id: area2._id, parent: area2.parent } });
before(async () => {
await utils.saveDocs([ ...places.values(), area2, personArea1, personArea2, formDocument ]);