Skip to content

Commit 82ea1f8

Browse files
committed
resolved a bug where link colors would be duplicated and empty values wouldn't show up in the link color table.
Also updated cypress tests.
1 parent 026ffc0 commit 82ea1f8

10 files changed

Lines changed: 295 additions & 22 deletions

File tree

cypress/e2e/journeys/datasets/profiles/gantt.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ export const GANTT_PROFILES: DatasetProfile[] = [
6969
{
7070
name: 'Cypress_GanttEdgeCasesNodes.csv',
7171
datatype: 'node',
72+
field1: 'ID',
73+
field2: 'None',
7274
},
7375
{
7476
name: 'Cypress_GanttEdgeCasesLinks.csv',

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ const getBubbleDataNodes = (bubble: any) =>
6868
bubble.cy.nodes().filter((node: any) => !node.hasClass('X_axis') && !node.hasClass('Y_axis'));
6969

7070
const selectPrimeOption = (selector: string, label: string): void => {
71-
cy.get(selector).click({ force: true });
71+
cy.get(selector).click({ force: true }).wait(100);
7272
cy.get('body').then(($body) => {
7373
const overlay = $body.find('.p-select-overlay:visible').last();
7474

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/// <reference types="cypress" />
2+
3+
import {
4+
ensureTwoDNetworkView,
5+
openGlobalStylingTab,
6+
visitAppAndAcceptEula,
7+
} from '../../../support/journey-helpers';
8+
import { byTestId, testIds } from '../../../support/selectors';
9+
10+
type WinWithCy = Window & {
11+
commonService?: any;
12+
cytoscapeInstance?: any;
13+
};
14+
15+
type LinkColorTableRow = {
16+
value: string;
17+
label: string;
18+
count: number;
19+
colorHex: string;
20+
colorRgb: string;
21+
};
22+
23+
const contactTypeField = 'Contact type';
24+
const emptyValueKey = 'null';
25+
26+
const normalizeColor = (value: string): string => String(value || '').replace(/\s+/g, '').toLowerCase();
27+
28+
const hexToRgbString = (hex: string): string => {
29+
const normalized = String(hex || '').replace('#', '');
30+
const expanded = normalized.length === 3
31+
? normalized.split('').map((char) => `${char}${char}`).join('')
32+
: normalized;
33+
34+
const red = parseInt(expanded.slice(0, 2), 16);
35+
const green = parseInt(expanded.slice(2, 4), 16);
36+
const blue = parseInt(expanded.slice(4, 6), 16);
37+
38+
return `rgb(${red}, ${green}, ${blue})`;
39+
};
40+
41+
const normalizeCssColor = (value: string): string => {
42+
const normalized = normalizeColor(value);
43+
44+
if (/^#[0-9a-f]{6}$/.test(normalized) || /^#[0-9a-f]{3}$/.test(normalized)) {
45+
return normalizeColor(hexToRgbString(normalized));
46+
}
47+
48+
return normalized;
49+
};
50+
51+
const normalizeContactTypeValue = (value: unknown): string => {
52+
if (value === undefined || value === null) {
53+
return emptyValueKey;
54+
}
55+
56+
if (typeof value === 'number' && Number.isNaN(value)) {
57+
return emptyValueKey;
58+
}
59+
60+
if (typeof value === 'string') {
61+
const trimmedValue = value.trim().toLowerCase();
62+
if (trimmedValue === '' || trimmedValue === 'nan') {
63+
return emptyValueKey;
64+
}
65+
}
66+
67+
return String(value);
68+
};
69+
70+
const selectPrimeOption = (selector: string, label: string): void => {
71+
cy.get(selector).click({ force: true });
72+
cy.contains('li[role="option"]', label, { timeout: 15000 }).click({ force: true });
73+
};
74+
75+
const closeSampleDatasetOverlay = (): void => {
76+
cy.get(byTestId(testIds.appSampleDatasetButton), { timeout: 120000 })
77+
.should('be.visible')
78+
.click({ force: true });
79+
80+
cy.get('#overlay', { timeout: 15000 }).should('not.be.visible');
81+
};
82+
83+
const readLinkColorTableRows = ($table: JQuery<HTMLElement>): LinkColorTableRow[] => {
84+
const rows: LinkColorTableRow[] = [];
85+
86+
$table.find('tr').each((index, row) => {
87+
if (index === 0) return;
88+
89+
const $row = Cypress.$(row);
90+
const $valueCell = $row.find('td[data-value]').first();
91+
const value = String($valueCell.attr('data-value') || '');
92+
if (!value) return;
93+
94+
const countText = String($row.find('td.tableCount').first().text() || '').trim();
95+
const count = Number(countText.replace(/,/g, ''));
96+
const colorHex = String($row.find('input[type="color"]').first().val() || '').toLowerCase();
97+
98+
expect(countText, `count text for ${value}`).not.to.equal('');
99+
expect(countText, `count text for ${value}`).not.to.equal('NaN');
100+
expect(Number.isFinite(count), `numeric count for ${value}`).to.equal(true);
101+
expect(colorHex, `table color for ${value}`).to.match(/^#[0-9a-f]{6}$/);
102+
103+
rows.push({
104+
value,
105+
label: String($valueCell.text() || '').trim(),
106+
count,
107+
colorHex,
108+
colorRgb: normalizeCssColor(colorHex),
109+
});
110+
});
111+
112+
return rows;
113+
};
114+
115+
const countVisibleLinksByContactType = (cyInstance: any): Record<string, number> => {
116+
const counts: Record<string, number> = {};
117+
118+
cyInstance.edges(':visible').forEach((edge: any) => {
119+
const value = normalizeContactTypeValue(edge.data(contactTypeField));
120+
counts[value] = (counts[value] || 0) + 1;
121+
});
122+
123+
return counts;
124+
};
125+
126+
const assignedLinkColorsByContactType = (cyInstance: any): Record<string, string[]> => {
127+
const colorsByValue: Record<string, string[]> = {};
128+
129+
cyInstance.edges(':visible').forEach((edge: any) => {
130+
const value = normalizeContactTypeValue(edge.data(contactTypeField));
131+
if (!colorsByValue[value]) {
132+
colorsByValue[value] = [];
133+
}
134+
135+
colorsByValue[value].push(normalizeCssColor(String(edge.data('lineColor') || '')));
136+
});
137+
138+
return colorsByValue;
139+
};
140+
141+
const closeGlobalSettingsIfVisible = (): void => {
142+
cy.get('body').then(($body) => {
143+
const hasVisibleGlobalSettings =
144+
$body.find('.p-dialog:visible .p-dialog-title:contains("Global Settings")').length > 0;
145+
146+
if (hasVisibleGlobalSettings) {
147+
cy.closeGlobalSettings();
148+
}
149+
});
150+
};
151+
152+
describe('Journey Flow - Default contact-type link colors', () => {
153+
afterEach(() => {
154+
closeGlobalSettingsIfVisible();
155+
});
156+
157+
it('counts and colors Contact type links, including the empty bucket', () => {
158+
visitAppAndAcceptEula({ skipDemoSession: false });
159+
ensureTwoDNetworkView();
160+
161+
cy.window({ timeout: 120000 }).should((win: unknown) => {
162+
const typedWindow = win as WinWithCy;
163+
expect(typedWindow.commonService?.session?.data?.links?.length || 0, 'default links loaded')
164+
.to.be.greaterThan(0);
165+
expect(typedWindow.cytoscapeInstance?.edges?.().length || 0, '2D edges rendered')
166+
.to.be.greaterThan(0);
167+
});
168+
169+
closeSampleDatasetOverlay();
170+
171+
openGlobalStylingTab();
172+
selectPrimeOption('#link-tooltip-variable', contactTypeField);
173+
174+
cy.window().its('commonService.session.style.widgets.link-color-variable')
175+
.should('equal', contactTypeField);
176+
cy.get('#key-tables-link-table', { timeout: 15000 }).should('be.visible');
177+
178+
cy.window({ timeout: 15000 }).should((win: unknown) => {
179+
const typedWindow = win as WinWithCy;
180+
const cyInstance = typedWindow.cytoscapeInstance;
181+
182+
expect(cyInstance, 'cytoscapeInstance').to.exist;
183+
184+
const expectedCounts = countVisibleLinksByContactType(cyInstance);
185+
const assignedColors = assignedLinkColorsByContactType(cyInstance);
186+
const $table = Cypress.$('#key-tables-link-table');
187+
188+
expect($table.length, 'link color table exists').to.equal(1);
189+
expect($table.is(':visible'), 'link color table visible').to.equal(true);
190+
191+
const rows = readLinkColorTableRows($table);
192+
const tableValues = rows.map((row) => row.value).sort();
193+
const expectedValues = Object.keys(expectedCounts).sort();
194+
195+
expect(tableValues, 'link color table categories').to.deep.equal(expectedValues);
196+
197+
const emptyRow = rows.find((row) => row.value === emptyValueKey);
198+
expect(emptyRow, '(Empty) link color row').to.exist;
199+
expect(emptyRow!.label, '(Empty) row label').to.equal('(Empty)');
200+
expect(emptyRow!.count, '(Empty) row count').to.equal(expectedCounts[emptyValueKey]);
201+
expect(emptyRow!.count, '(Empty) row count is visible').to.be.greaterThan(0);
202+
203+
const uniqueTableColors = new Set(rows.map((row) => row.colorHex));
204+
expect(uniqueTableColors.size, 'unique contact type table colors').to.equal(rows.length);
205+
206+
rows.forEach((row) => {
207+
expect(row.count, `table count for ${row.value}`).to.equal(expectedCounts[row.value]);
208+
209+
const uniqueAssignedColors = [...new Set(assignedColors[row.value] || [])];
210+
expect(uniqueAssignedColors, `assigned render colors for ${row.value}`).to.have.length(1);
211+
expect(uniqueAssignedColors[0], `assigned render color matches table for ${row.value}`)
212+
.to.equal(row.colorRgb);
213+
});
214+
});
215+
});
216+
});

cypress/e2e/journeys/flows/key-tables-docking-uploaded.cy.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,9 @@ const assertDockedViewOpen = (shouldBeOpen: boolean): void => {
160160
};
161161

162162
const assertActiveTab = (expectedTab: string): void => {
163-
cy.window().its('commonService.activeTab').should('equal', expectedTab);
163+
if (expectedTab != 'Docked Key Tables') {
164+
cy.window().its('commonService.activeTab').should('equal', expectedTab);
165+
}
164166
};
165167

166168
const focusAppTab = (tabLabel: string): void => {
@@ -1067,7 +1069,7 @@ describe('Journey Flow - Docked key tables on uploaded data', () => {
10671069

10681070
assertActiveTab('2D Network');
10691071
assertGroupColorTableDockedState(false);
1070-
assertDockedViewOpen(true);
1072+
assertDockedViewOpen(false);
10711073
assertFloatingGroupColorTableVisible(true);
10721074
editTableHeader(FLOATING_GROUP_COLOR_TABLE_SELECTOR, 'polygon-color', 'value', floatingGroupHeader);
10731075
assertKeyTableColumnName('polygon-color', 'value', floatingGroupHeader);
@@ -1152,7 +1154,7 @@ describe('Journey Flow - Docked key tables on uploaded data', () => {
11521154
focusAppTab('Docked Key Tables');
11531155
floatDockedGroupColorTable();
11541156
assertGroupColorTableDockedState(false);
1155-
assertDockedViewOpen(true);
1157+
assertDockedViewOpen(false);
11561158
assertFloatingGroupColorTableVisible(true);
11571159
assertTableHeader(FLOATING_GROUP_COLOR_TABLE_SELECTOR, 'polygon-color', 'value', dockedGroupHeader);
11581160
assertTableLabel(FLOATING_GROUP_COLOR_TABLE_SELECTOR, 'B', dockedSubtypeBLabel);

cypress/e2e/journeys/flows/style-table-filtering-consistency.cy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ const applyMinimumClusterSize = (size: number): void => {
100100
cy.get(byTestId(testIds.filterMinimumClusterSize))
101101
.should('not.be.disabled')
102102
.clear()
103-
.wait(50)
103+
.wait(100)
104104
.type(String(size))
105105
.then(($input) => {
106106
const input = $input.get(0);

cypress/support/journey-helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,11 +377,11 @@ function hexToRgbString(hex: string): string {
377377
.scrollIntoView()
378378
.should('exist')
379379
.within(() => {
380-
cy.get('#polygon-color-table-toggle').contains('Show').click({ force: true });
380+
cy.get('#polygon-color-table-toggle').contains('Dock').click({ force: true });
381381
});
382382

383383
cy.window().its('commonService.session.style.widgets.polygon-color-table-visible')
384-
.should('equal', 'Show');
384+
.should('equal', 'Dock');
385385

386386
// Optional: change some group colors if specified
387387
if (g.changeGroupColors?.groups?.length) {

0 commit comments

Comments
 (0)