Skip to content

Commit dcdb96d

Browse files
Merge pull request #7164 from folio-org/FAT-26115-fqm-custom-field-label-alias-inheritance
FAT-26115 Add testing around the user-friendly query and custom fields in UIPQB
2 parents 15fda03 + adacdd7 commit dcdb96d

3 files changed

Lines changed: 207 additions & 5 deletions

File tree

cypress/e2e/lists/query-builder/search-query-builder-ui.cy.js

Lines changed: 194 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ import ClassificationIdentifierTypes from '../../../support/fragments/settings/i
1212
import TopMenu from '../../../support/fragments/topMenu';
1313
import Users from '../../../support/fragments/users/users';
1414
import DateTools from '../../../support/utils/dateTools';
15-
import { generateDatePickerCustomFieldData } from '../../../support/utils/customFields';
15+
import {
16+
generateDatePickerCustomFieldData,
17+
generateMultiSelectCustomFieldData,
18+
generateRadioButtonCustomFieldData,
19+
generateSingleSelectCustomFieldData,
20+
} from '../../../support/utils/customFields';
1621
import { poll } from '../../../support/utils/polling';
1722
import { getCurrentTimestamp, getTestEntityValue } from '../../../support/utils/stringTools';
1823
import {
@@ -92,23 +97,34 @@ describe('Lists', () => {
9297
};
9398

9499
const waitForCustomFieldToBeQueryable = (recordType, fieldLabel) => {
100+
const fieldLabels = Array.isArray(fieldLabel) ? fieldLabel : [fieldLabel];
101+
95102
return Lists.getEntityTypeIdByNameViaApi(recordType).then((entityTypeId) => {
96103
return poll(
97104
() => Lists.getEntityTypeByIdViaApi(entityTypeId, { failOnStatusCode: false }),
98105
({ body }) => {
99-
return body.columns?.some(
100-
({ labelAlias, queryable }) => labelAlias === fieldLabel && queryable,
101-
);
106+
return fieldLabels.every((label) => body.columns?.some(({ labelAlias, queryable }) => labelAlias === label && queryable));
102107
},
103108
{
104109
timeout: 360000,
105110
delay: 15000,
106-
errorMessage: `"${fieldLabel}" custom field did not become queryable for ${recordType}`,
111+
errorMessage: `"${fieldLabels.join(', ')}" custom field(s) did not become queryable for ${recordType}`,
107112
},
108113
);
109114
});
110115
};
111116

117+
const verifyUserFriendlyQueryText = ({ queryBuilderPreview, listDetails, expectedQuery }) => {
118+
const actualQueryMessage = `Query builder preview: "${queryBuilderPreview}"\nList details: "${listDetails}"`;
119+
120+
expect(queryBuilderPreview, actualQueryMessage).to.equal(expectedQuery);
121+
expect(listDetails, actualQueryMessage).to.include(`Query: ${expectedQuery}`);
122+
[queryBuilderPreview, listDetails].forEach((actualQueryText) => {
123+
expect(actualQueryText, actualQueryMessage).not.to.match(/\bopt_\d+\b/);
124+
expect(actualQueryText, actualQueryMessage).not.to.include('customfield_');
125+
});
126+
};
127+
112128
const verifyQueryBuilder = (
113129
field,
114130
operator,
@@ -258,6 +274,179 @@ describe('Lists', () => {
258274
);
259275
});
260276

277+
describe('Users custom field option labels', () => {
278+
const recordType = 'Users';
279+
const testNumber = 'C831957';
280+
const customFieldNamePostfix = getCurrentTimestamp();
281+
const customFieldSpecs = {
282+
multiSelect: {
283+
fieldLabel: 'Corsair - multi select',
284+
generator: generateMultiSelectCustomFieldData,
285+
multiSelect: true,
286+
valueType: 'multiselect',
287+
options: [
288+
{ id: 'opt_0', value: 'Corsair - multi select1', default: false },
289+
{ id: 'opt_1', value: 'Corsair - multi select2', default: false },
290+
],
291+
},
292+
singleSelect: {
293+
fieldLabel: 'Corsair - single select',
294+
generator: generateSingleSelectCustomFieldData,
295+
multiSelect: false,
296+
options: [
297+
{ id: 'opt_0', value: 'Corsair - single select1', default: false },
298+
{ id: 'opt_1', value: 'Corsair - single select2', default: false },
299+
],
300+
},
301+
radioButton: {
302+
fieldLabel: 'Corsair - radio button',
303+
generator: generateRadioButtonCustomFieldData,
304+
multiSelect: false,
305+
options: [
306+
{ id: 'opt_0', value: 'Corsair - radio button1', default: false },
307+
{ id: 'opt_1', value: 'Corsair - radio button2', default: false },
308+
],
309+
},
310+
};
311+
const queryCustomFieldKeys = Object.keys(customFieldSpecs);
312+
const getCustomFieldName = (fieldLabel) => `${fieldLabel} ${customFieldNamePostfix}`;
313+
const createCustomFieldData = ({ fieldLabel, generator, multiSelect, options }) => generator({
314+
testNumber,
315+
data: {
316+
name: getCustomFieldName(fieldLabel),
317+
selectField: {
318+
multiSelect,
319+
options: { values: options },
320+
},
321+
},
322+
});
323+
const testData = {
324+
customFields: queryCustomFieldKeys.reduce((customFields, key) => {
325+
return {
326+
...customFields,
327+
[key]: createCustomFieldData(customFieldSpecs[key]),
328+
};
329+
}, {}),
330+
};
331+
const getCustomFieldLabel = ({ name }) => `User — ${name}`;
332+
const getCustomFieldLabels = () => Object.values(testData.customFields).map(getCustomFieldLabel);
333+
const getCustomFieldValue = (key) => customFieldSpecs[key].options[0].value;
334+
const getQueryRow = (key, operator) => ({
335+
field: getCustomFieldLabel(testData.customFields[key]),
336+
operator,
337+
value: getCustomFieldValue(key),
338+
valueType: [QUERY_OPERATIONS.IN, QUERY_OPERATIONS.NOT_IN].includes(operator)
339+
? customFieldSpecs[key].valueType
340+
: 'select',
341+
});
342+
343+
const configureSelectQueryRow = (row, { field, operator, value, valueType = 'select' }) => {
344+
QueryModal.selectField(field, row);
345+
QueryModal.selectOperator(operator, row);
346+
347+
if (valueType === 'multiselect') {
348+
QueryModal.chooseFromValueMultiselect(value, row, { exactMatch: true });
349+
} else {
350+
QueryModal.chooseValueSelect(value, row);
351+
}
352+
};
353+
354+
const createQueryRows = (queryRows) => {
355+
queryRows.forEach((queryRow, rowIndex) => {
356+
configureSelectQueryRow(rowIndex, queryRow);
357+
if (rowIndex < queryRows.length - 1) {
358+
QueryModal.addNewRow(rowIndex);
359+
}
360+
});
361+
};
362+
363+
before('Create custom fields', () => {
364+
cy.getAdminToken()
365+
.then(() => cy.createCustomFieldsViaApi(Object.values(testData.customFields)))
366+
.then((createdCustomFields) => {
367+
queryCustomFieldKeys.forEach((key, index) => {
368+
testData.customFields[key] = createdCustomFields[index];
369+
});
370+
371+
testData.createdCustomFieldIds = createdCustomFields.map(({ id }) => id);
372+
373+
return waitForCustomFieldToBeQueryable(recordType, getCustomFieldLabels());
374+
});
375+
});
376+
377+
after('Delete custom field test data', () => {
378+
cy.getAdminToken();
379+
if (testData.createdCustomFieldIds?.length) {
380+
cy.deleteCustomFieldsViaApi({
381+
ids: testData.createdCustomFieldIds,
382+
});
383+
}
384+
});
385+
386+
afterEach('Delete test list', () => {
387+
deleteTestList(cy.getAdminToken);
388+
});
389+
390+
it(
391+
'C831957 Verify custom field option labels in the user-friendly query (corsair)',
392+
{ tags: ['extendedPath', 'corsair', 'C831957'] },
393+
() => {
394+
const multiSelectInRow = getQueryRow('multiSelect', QUERY_OPERATIONS.IN);
395+
const equalsQueryRows = queryCustomFieldKeys.map((key) => getQueryRow(key, QUERY_OPERATIONS.EQUAL));
396+
const [multiSelectEqualsRow, singleSelectEqualsRow, radioButtonEqualsRow] =
397+
equalsQueryRows;
398+
const expectedInQuery = `(${multiSelectInRow.field} in [${multiSelectInRow.value}])`;
399+
const expectedEqualsQuery =
400+
`(${multiSelectEqualsRow.field} == ${multiSelectEqualsRow.value}) AND ` +
401+
`(${singleSelectEqualsRow.field} == ${singleSelectEqualsRow.value}) AND ` +
402+
`(${radioButtonEqualsRow.field} == ${radioButtonEqualsRow.value})`;
403+
const actualQueries = {};
404+
405+
listName = getTestEntityValue(`${testNumber}_List`);
406+
cy.loginAsAdmin({
407+
path: TopMenu.listsPath,
408+
waiter: Lists.filtersWaitLoading,
409+
});
410+
411+
Lists.openNewListPane();
412+
Lists.setName(listName);
413+
Lists.selectRecordType(recordType);
414+
Lists.verifySelectedOptionsInRecordTypeDropdown(recordType);
415+
Lists.verifySaveButtonIsActive();
416+
Lists.verifyCancelButtonIsActive();
417+
418+
Lists.buildQuery();
419+
QueryModal.verify();
420+
QueryModal.verifyQueryTextboxReadOnly();
421+
QueryModal.verifyQueryTextboxResizable();
422+
423+
configureSelectQueryRow(0, multiSelectInRow);
424+
QueryModal.verifyQueryAreaContent(expectedInQuery);
425+
QueryModal.verifyQueryAreaDoesNotContain('opt_');
426+
QueryModal.verifyQueryAreaDoesNotContain('customfield_');
427+
createQueryRows(equalsQueryRows);
428+
QueryModal.testQuery();
429+
QueryModal.waitForQueryTestToFinish();
430+
QueryModal.getQueryAreaContent().then((actualPreviewQuery) => {
431+
actualQueries.preview = actualPreviewQuery;
432+
});
433+
QueryModal.clickRunQueryAndSave();
434+
QueryModal.verifyClosed();
435+
Lists.waitForCompilingToComplete(3000);
436+
Lists.getQueryText().then((actualSavedQuery) => {
437+
actualQueries.saved = actualSavedQuery;
438+
});
439+
cy.then(() => {
440+
verifyUserFriendlyQueryText({
441+
queryBuilderPreview: actualQueries.preview,
442+
listDetails: actualQueries.saved,
443+
expectedQuery: expectedEqualsQuery,
444+
});
445+
});
446+
},
447+
);
448+
});
449+
261450
describe('Users custom fields', () => {
262451
const recordType = 'Users';
263452
const testNumber = 'C831970';

cypress/support/fragments/bulk-edit/query-modal.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,10 @@ export default {
450450
cy.get('[class^="queryArea"]').should('have.text', content);
451451
},
452452

453+
getQueryAreaContent() {
454+
return cy.get('[class^="queryArea"]').invoke('text');
455+
},
456+
453457
verifyQueryAreaDoesNotContain(content) {
454458
cy.get('[class^="queryArea"]').should(($queryArea) => {
455459
expect($queryArea.text().toLowerCase()).not.to.include(content.toLowerCase());
@@ -757,6 +761,11 @@ export default {
757761
cy.expect([HTML('Executing test query...').exists(), Spinner().exists()]);
758762
},
759763

764+
waitForQueryTestToFinish() {
765+
cy.contains(/^Query returns/, { timeout: 60000 }).should('be.visible');
766+
this.runQueryDisabled(false);
767+
},
768+
760769
verifyPreviewOfRecordsMatched() {
761770
cy.expect([MultiColumnList().exists(), showColumnsButton.exists()]);
762771
this.testQueryDisabled(false);

cypress/support/fragments/lists/lists.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,10 @@ const UI = {
552552
cy.get('#results-viewer-accordion').contains(`Query: (${query})`).should('be.visible');
553553
},
554554

555+
getQueryText() {
556+
return cy.get('#results-viewer-accordion').contains('Query:').invoke('text');
557+
},
558+
555559
verifyRecordWithContent(content) {
556560
cy.expect(MultiColumnListCell({ content }).exists());
557561
},

0 commit comments

Comments
 (0)