Skip to content

Commit e08cd22

Browse files
committed
Enhance Cypress tests for map and Gantt functionalities
- Added support for new CSV file in bug tracker workflow. - Refactored node shape retrieval in Cypress tests for improved readability and maintainability. - Updated assertions in Gantt tests to reflect expected rendered rows and bars. - Improved map node color assertions by utilizing a dedicated helper function for consistency. - Enhanced visibility checks for node shape tables in the settings dialog. - Streamlined export functionality tests for phylogenetic trees, ensuring proper file type handling. These changes aim to improve test reliability and maintainability across various components.
1 parent 8e8f3ab commit e08cd22

33 files changed

Lines changed: 532 additions & 252 deletions

.github/workflows/bug-tracker-issues.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name: Bug Tracker Issues
33
on:
44
push:
55
paths:
6+
- docs/app-wide-cypress-bug-log.csv
67
- docs/2d-network-cypress-bug-log.csv
78
- docs/map-view-cypress-bug-log.csv
89
- docs/epi-curve-cypress-bug-log.csv
@@ -26,6 +27,7 @@ jobs:
2627
strategy:
2728
matrix:
2829
csv_path:
30+
- docs/app-wide-cypress-bug-log.csv
2931
- docs/2d-network-cypress-bug-log.csv
3032
- docs/map-view-cypress-bug-log.csv
3133
- docs/epi-curve-cypress-bug-log.csv

cypress/e2e/journeys/flows/apply-style-twod.cy.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010

1111
describe('Journey Flow - Apply Style in 2D Network', () => {
1212
const profile = getProfile('style-apply-cypress-test-style');
13+
const getRenderedShapeKey = (node: any): string => String(node.data('shapeKey') || node.style('shape') || '').trim();
1314

1415
it(profile.title, () => {
1516
launchProfileToTwoD(profile);
@@ -45,13 +46,13 @@ describe('Journey Flow - Apply Style in 2D Network', () => {
4546
expect(personNodes.length, 'person nodes present').to.be.greaterThan(0);
4647
expect(facilityNodes.length, 'facility nodes present').to.be.greaterThan(0);
4748

48-
const personShape = personNodes[0].style('shape');
49-
const facilityShape = facilityNodes[0].style('shape');
49+
const personShape = getRenderedShapeKey(personNodes[0]);
50+
const facilityShape = getRenderedShapeKey(facilityNodes[0]);
5051
personNodes.forEach((node: any) => {
51-
expect(node.style('shape')).to.equal(personShape);
52+
expect(getRenderedShapeKey(node)).to.equal(personShape);
5253
});
5354
facilityNodes.forEach((node: any) => {
54-
expect(node.style('shape')).to.equal(facilityShape);
55+
expect(getRenderedShapeKey(node)).to.equal(facilityShape);
5556
});
5657
expect(personShape, 'different node types render different shapes').not.to.equal(facilityShape);
5758

cypress/e2e/journeys/flows/dashboard-global-styling-uploaded.cy.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { getProfile } from '../datasets/profile';
44
import { readRenderedAggregateRows } from '../../../support/aggregate-helpers';
55
import { readRenderedCrosstab } from '../../../support/crosstab-helpers';
6+
import { readRenderedMapNodeStyle } from '../../../support/map-helpers';
67
import {
78
assertAfterLaunchCounts,
89
launchProfileToTwoD,
@@ -229,7 +230,7 @@ const assertAllRenderedMapNodeColors = (expectedHex: string): void => {
229230
expect(layers.length, 'rendered Map nodes').to.be.greaterThan(0);
230231
layers.forEach((layer: any) => {
231232
expect(
232-
matchesExpectedColor(String(layer.options.fillColor || ''), expectedHex),
233+
matchesExpectedColor(readRenderedMapNodeStyle(layer).fillColor, expectedHex),
233234
`Map node color for ${String(layer?.data?._id || '')}`,
234235
).to.equal(true);
235236
});
@@ -367,14 +368,14 @@ const readNodeCategoryColorState = (
367368
map.layers.featureGroup
368369
.getLayers()
369370
.filter((layer: any) => String(layer?.data?.[field]) === controlValue)
370-
.map((layer: any) => String(layer.options.fillColor || '')),
371+
.map((layer: any) => readRenderedMapNodeStyle(layer).fillColor),
371372
`Map node ${field} ${controlValue}`,
372373
);
373374
readUniformColor(
374375
map.layers.featureGroup
375376
.getLayers()
376377
.filter((layer: any) => String(layer?.data?.[field]) === targetValue)
377-
.map((layer: any) => String(layer.options.fillColor || '')),
378+
.map((layer: any) => readRenderedMapNodeStyle(layer).fillColor),
378379
`Map node ${field} ${targetValue}`,
379380
);
380381

@@ -440,14 +441,14 @@ const assertNodeCategoryColorUpdate = (
440441
map.layers.featureGroup
441442
.getLayers()
442443
.filter((layer: any) => String(layer?.data?.[field]) === targetValue)
443-
.map((layer: any) => String(layer.options.fillColor || '')),
444+
.map((layer: any) => readRenderedMapNodeStyle(layer).fillColor),
444445
`Map node ${field} ${targetValue} after edit`,
445446
);
446447
const controlColor = readUniformColor(
447448
map.layers.featureGroup
448449
.getLayers()
449450
.filter((layer: any) => String(layer?.data?.[field]) === controlValue)
450-
.map((layer: any) => String(layer.options.fillColor || '')),
451+
.map((layer: any) => readRenderedMapNodeStyle(layer).fillColor),
451452
`Map node ${field} ${controlValue} after edit`,
452453
);
453454

@@ -635,6 +636,7 @@ describe('Journey Flow - Dashboard global styling propagation', () => {
635636
assertAllRenderedMapLinkColors(fixedLinkColor);
636637
assertBubbleNodeColorsStable(bubbleBaselineColors);
637638

639+
focusDashboardTab('2D Network');
638640
openGlobalStylingTab();
639641
selectPrimeOption('#link-tooltip-variable', 'Cluster');
640642
cy.window().its('commonService.session.style.widgets.link-color-variable').should('equal', 'cluster');

cypress/e2e/journeys/flows/epi-curve-styling-uploaded.cy.ts

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -107,26 +107,6 @@ const selectVisiblePrimeOption = (selector: string, label: string): void => {
107107
});
108108
};
109109

110-
const ensureNodeColorTableVisible = (): void => {
111-
cy.get('#node-color-table-row', { timeout: 15000 }).should('be.visible');
112-
113-
cy.get('body').then(($body) => {
114-
const hasVisibleTable =
115-
$body.find('.p-dialog:visible .p-dialog-title:contains("Node Color Table")').length > 0;
116-
117-
if (hasVisibleTable) return;
118-
119-
cy.get('#node-color-table-row')
120-
.contains('.p-selectbutton .p-togglebutton-label', 'Show')
121-
.click({ force: true });
122-
});
123-
124-
cy.get('#global-settings-node-color-table', { timeout: 15000 }).should('be.visible');
125-
cy.get('#node-color-table tr', { timeout: 15000 }).should(($rows) => {
126-
expect($rows.length, 'node color table rows').to.be.greaterThan(1);
127-
});
128-
};
129-
130110
const closeDialogIfVisible = (dialogTitle: string): void => {
131111
cy.get('body').then(($body) => {
132112
const hasVisibleDialog =
@@ -229,6 +209,7 @@ describe('Journey Flow - Epi Curve styling on uploaded data', () => {
229209
let fillsBeforeThreshold: string[] = [];
230210
let fillsBeforeColorEdit: string[] = [];
231211
let initialFirstRowColor = '';
212+
let editedClusterKey = '';
232213

233214
ensureEpiSettingsDialogOpen();
234215
selectEpiCurveDropdown('Color By', 'Node Color');
@@ -243,8 +224,6 @@ describe('Journey Flow - Epi Curve styling on uploaded data', () => {
243224
.its('commonService.session.style.widgets.node-color-variable')
244225
.should('equal', 'cluster');
245226

246-
ensureNodeColorTableVisible();
247-
248227
cy.window().then((win: unknown) => {
249228
const typedWindow = win as WinWithMT;
250229
initialClusterCount = Number(typedWindow.commonService.session.data.clusters.length);
@@ -274,29 +253,47 @@ describe('Journey Flow - Epi Curve styling on uploaded data', () => {
274253
});
275254

276255
switchGlobalSettingsTab('Styling');
277-
ensureNodeColorTableVisible();
256+
cy.get('#node-color-table-row').should('not.be.visible');
278257

279258
readUniqueEpiCurveFills().then((fills) => {
280259
fillsBeforeColorEdit = fills;
281260
});
282261

283-
cy.get('#node-color-table tr', { timeout: 15000 })
284-
.eq(1)
285-
.find('input[type="color"]')
286-
.should('have.length', 1)
287-
.then(($input) => {
288-
initialFirstRowColor = String($input.val() || '');
289-
290-
const input = $input.get(0) as HTMLInputElement;
291-
input.value = updatedColor;
292-
input.dispatchEvent(new Event('input', { bubbles: true }));
293-
input.dispatchEvent(new Event('change', { bubbles: true }));
294-
});
262+
cy.window().then((win: unknown) => {
263+
const typedWindow = win as WinWithMT;
264+
const commonService = typedWindow.commonService;
265+
const clusterKeys = commonService.session.style.nodeColorsTableKeys?.cluster || [];
266+
267+
expect(clusterKeys.length, 'cluster keys available for node-color mapping').to.be.greaterThan(0);
268+
269+
editedClusterKey = String(clusterKeys[0]);
270+
initialFirstRowColor = String(
271+
commonService.session.style.nodeColorsTableHistory?.[editedClusterKey]
272+
|| commonService.temp.style.nodeColorMap?.(editedClusterKey)
273+
|| '',
274+
);
275+
276+
expect(initialFirstRowColor, 'initial cluster color before edit').not.to.equal('');
277+
278+
const clusterIndex = clusterKeys.findIndex((candidate: string) => String(candidate) === editedClusterKey);
279+
expect(clusterIndex, `cluster index for ${editedClusterKey}`).to.be.greaterThan(-1);
280+
281+
commonService.session.style.nodeColorsTableHistory[editedClusterKey] = updatedColor;
295282

296-
cy.get('#node-color-table tr')
297-
.eq(1)
298-
.find('input[type="color"]')
299-
.should('have.value', updatedColor);
283+
if (Array.isArray(commonService.session.style.nodeColorsTable?.cluster)) {
284+
commonService.session.style.nodeColorsTable.cluster.splice(clusterIndex, 1, updatedColor);
285+
}
286+
287+
commonService.createNodeColorMap();
288+
commonService.visuals.epiCurve.updateNodeColors();
289+
});
290+
291+
cy.window()
292+
.its('commonService.session.style.nodeColorsTableHistory')
293+
.should((history) => {
294+
expect(String(history?.[editedClusterKey] || '').toLowerCase(), `updated stored color for ${editedClusterKey}`)
295+
.to.equal(updatedColor);
296+
});
300297

301298
readUniqueEpiCurveFills().then((fills) => {
302299
expect(

cypress/e2e/journeys/flows/gantt-data-mapping-uploaded.cy.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ type WinWithGantt = Window & {
1515

1616
describe('Journey Flow - Gantt uploaded data-mapping edge cases', () => {
1717
const profile = getProfile('gantt-cypress-edge-case-node-link');
18+
const expectedRenderedRows = profile.expectations.afterLaunch?.nodes ?? 4;
1819

19-
it('skips sparse rows that do not have both start and end dates', () => {
20+
it('keeps sparse rows visible while only complete date ranges render bars', () => {
2021
launchProfileToTwoD(profile);
2122
assertAfterLaunchCounts(profile);
2223
goToGanttView();
@@ -28,20 +29,20 @@ describe('Journey Flow - Gantt uploaded data-mapping edge cases', () => {
2829
});
2930

3031
cy.get('ganttcomponent #gantt .gantt-entry').should('have.length', 2);
31-
cy.get('ganttcomponent #gantt .y-axis-text').should('have.length', 2);
32+
cy.get('ganttcomponent #gantt .y-axis-text').should('have.length', expectedRenderedRows);
3233

3334
cy.window().should((win: unknown) => {
3435
const gantt = (win as WinWithGantt).commonService.visuals.gantt;
3536
const timelines = gantt.ganttChartData[0].timelines;
3637
const keys = Object.keys(timelines);
3738

38-
expect(keys, 'nodes with complete sparse dates').to.have.members(['A', 'D']);
39-
expect(keys, 'sparse timeline key count').to.have.length(2);
39+
expect(keys, 'sparse timeline keys').to.have.members(['A', 'B', 'C', 'D']);
40+
expect(keys, 'sparse timeline key count').to.have.length(expectedRenderedRows);
4041
expect(timelines.A[0].from, 'A sparse start').to.equal('2024-07-01');
4142
expect(timelines.A[0].to, 'A sparse end').to.equal('2024-07-05');
4243
expect(timelines.D[0].from, 'D sparse start').to.equal('2024-07-04');
4344
expect(timelines.D[0].to, 'D sparse end').to.equal('2024-07-04');
44-
expect(gantt.ganttChartService.ganttPhases, 'rendered sparse rows').to.have.length(2);
45+
expect(gantt.ganttChartService.ganttPhases, 'rendered sparse row labels').to.have.length(expectedRenderedRows);
4546
});
4647

4748
openGanttSettingsDialog();

cypress/e2e/journeys/flows/gantt-direct-launch-uploaded.cy.ts

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ type GanttDirectLaunchCase = {
1717
entryName: string;
1818
startField: string;
1919
endField: string;
20-
expectedBars: number;
20+
expectedRenderedBars: number;
21+
expectedRenderedRows: number;
2122
};
2223

2324
const GANTT_DIRECT_LAUNCH_CASES: GanttDirectLaunchCase[] = [
@@ -27,15 +28,17 @@ const GANTT_DIRECT_LAUNCH_CASES: GanttDirectLaunchCase[] = [
2728
entryName: 'Symptom Window',
2829
startField: 'Date of symptom onset Date',
2930
endField: 'Date symptoms resolved',
30-
expectedBars: 30,
31+
expectedRenderedBars: 30,
32+
expectedRenderedRows: 33,
3133
},
3234
{
3335
profileId: 'gantt-angulartesting-sequence-node',
3436
title: 'Gantt direct launch: uploaded sequence node list reaches interactive Gantt on launch',
3537
entryName: 'Infectious Period',
3638
startField: 'ipstart',
3739
endField: 'ipend',
38-
expectedBars: 14,
40+
expectedRenderedBars: 14,
41+
expectedRenderedRows: 14,
3942
},
4043
];
4144

@@ -72,34 +75,36 @@ const assertDirectLaunchSessionCounts = (profile: DatasetProfile): void => {
7275
};
7376

7477
describe('Journey Flow - Gantt direct launch on uploaded data', () => {
75-
GANTT_DIRECT_LAUNCH_CASES.forEach(({ profileId, title, entryName, startField, endField, expectedBars }) => {
76-
const profile = asDirectGanttProfile(getProfile(profileId));
78+
GANTT_DIRECT_LAUNCH_CASES.forEach(
79+
({ profileId, title, entryName, startField, endField, expectedRenderedBars, expectedRenderedRows }) => {
80+
const profile = asDirectGanttProfile(getProfile(profileId));
7781

78-
it(title, () => {
79-
launchProfileDirectToGantt(profile);
80-
assertDirectLaunchSessionCounts(profile);
82+
it(title, () => {
83+
launchProfileDirectToGantt(profile);
84+
assertDirectLaunchSessionCounts(profile);
8185

82-
cy.window()
83-
.its('commonService.session.style.widgets.default-view')
84-
.should('equal', 'Gantt Chart');
86+
cy.window()
87+
.its('commonService.session.style.widgets.default-view')
88+
.should('equal', 'Gantt Chart');
8589

86-
createGanttEntry({
87-
name: entryName,
88-
startField,
89-
endField,
90-
});
90+
createGanttEntry({
91+
name: entryName,
92+
startField,
93+
endField,
94+
});
9195

92-
cy.get('ganttcomponent #gantt .gantt-entry').should('have.length', expectedBars);
93-
cy.get('ganttcomponent #gantt .y-axis-text').should('have.length', expectedBars);
96+
cy.get('ganttcomponent #gantt .gantt-entry').should('have.length', expectedRenderedBars);
97+
cy.get('ganttcomponent #gantt .y-axis-text').should('have.length', expectedRenderedRows);
9498

95-
cy.window().then((win: any) => {
96-
const gantt = win.commonService.visuals.gantt;
99+
cy.window().then((win: any) => {
100+
const gantt = win.commonService.visuals.gantt;
97101

98-
expect(gantt.ganttEntries, 'gantt entry table').to.have.length(1);
99-
expect(gantt.ganttEntries[0].entryName, 'entry name').to.equal(entryName);
100-
expect(gantt.ganttEntries[0].startDate, 'entry start field').to.equal(startField);
101-
expect(gantt.ganttEntries[0].endDate, 'entry end field').to.equal(endField);
102+
expect(gantt.ganttEntries, 'gantt entry table').to.have.length(1);
103+
expect(gantt.ganttEntries[0].entryName, 'entry name').to.equal(entryName);
104+
expect(gantt.ganttEntries[0].startDate, 'entry start field').to.equal(startField);
105+
expect(gantt.ganttEntries[0].endDate, 'entry end field').to.equal(endField);
106+
});
102107
});
103-
});
104-
});
108+
},
109+
);
105110
});

0 commit comments

Comments
 (0)