Skip to content

Commit

Permalink
test(spie-ui-e2e): add plotter test
Browse files Browse the repository at this point in the history
  • Loading branch information
robsonos committed Dec 10, 2024
1 parent 54b0e03 commit bc285cf
Show file tree
Hide file tree
Showing 4 changed files with 474 additions and 1 deletion.
257 changes: 257 additions & 0 deletions apps/spie-ui-e2e/src/e2e/plotter.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
import {
mockElectronAPI,
mockSerialPortList,
} from '../fixtures/mocks/electron-api.mock';
import plotOneVariable from '../fixtures/plotOneVariable.json';
import plotThreeVariables from '../fixtures/plotThreeVariables.json';

describe('Plotter routine', () => {
before(() => {
cy.fixture('plotOneVariable').then((data) => {
expect(plotOneVariable).to.deep.equal(data);
});
cy.fixture('plotThreeVariables').then((data) => {
expect(plotThreeVariables).to.deep.equal(data);
});
});

beforeEach(() => {
cy.visit('/');

cy.on('window:before:load', (win) => {
win.electron = mockElectronAPI(win);
});

// Ensure onSerialPortEventTrigger is loaded
cy.window().should((win) => {
expect(win.onSerialPortEventTrigger).to.be.a('function');
});

// Navigate to plotter tab
cy.get('ion-tab-button').contains('Plotter').parent().click();
cy.get('app-plotter-component').should('not.have.class', 'ion-page-hidden');

// Mock connection
cy.connect(mockSerialPortList[0].path, 9600);
cy.get('ion-tabs ion-accordion').contains('Connection').parent().click();
});

it('should render the chart', () => {
cy.get('apx-chart').should('exist');
cy.get('.apexcharts-canvas').should('be.visible');
});

it('should render series', () => {
const numberOfPoints = 2;
const mockEventData = plotOneVariable.eventData.slice(0, numberOfPoints);
const numberOfSeries = plotOneVariable.numberOfSeries;

cy.window()
.then((win) => {
return new Promise<void>((resolve) => {
// Mock data event with numberOfPoints point 50ms apart
mockEventData.forEach((data, index) => {
setTimeout(() => {
win.onSerialPortEventTrigger({
type: 'data',
data: data,
});

if (index === mockEventData.length - 1) {
resolve();
}
}, index * 50);
});
});
})
.then(() => {
cy.get('apx-chart svg path.apexcharts-line').should(
'have.length',
numberOfSeries
);
});
});

it('should render tooltips', () => {
const numberOfPoints = 2;
const mockEventData = plotOneVariable.eventData.slice(0, numberOfPoints);

cy.window()
.then((win) => {
return new Promise<void>((resolve) => {
// Mock data event with numberOfPoints point 50ms apart
mockEventData.forEach((data, index) => {
setTimeout(() => {
win.onSerialPortEventTrigger({
type: 'data',
data: data,
});

if (index === mockEventData.length - 1) {
resolve();
}
}, index * 50);
});
});
})
.then(() => {
// Pause data stream (to enable tooltips)
cy.get('app-plotter-component ion-button').contains('Pause').click();

// Trigger tooltip
cy.get('.apexcharts-canvas').trigger('mousemove', {
clientX: 100,
clientY: 150,
});

// Ensure the tooltip is visible and active
cy.get('.apexcharts-tooltip').and('have.class', 'apexcharts-active');
});
});

it('should render labels and values for multiple variables', () => {
const numberOfPoints = 2;
const mockEventData = plotThreeVariables.eventData.slice(0, numberOfPoints);
const mockSeries = plotThreeVariables.series.slice(0, numberOfPoints);

cy.window()
.then((win) => {
return new Promise<void>((resolve) => {
// Mock data event with numberOfPoints point 50ms apart
mockEventData.forEach((data, index) => {
setTimeout(() => {
win.onSerialPortEventTrigger({
type: 'data',
data: data,
});

if (index === mockEventData.length - 1) {
resolve();
}
}, index * 50);
});
});
})
.then(() => {
// Pause data stream (to enable tooltips)
cy.get('app-plotter-component ion-button').contains('Pause').click();

// Test labels and values
mockSeries.forEach((points, pointsIndex) => {
// Move mouse to estimated tooltip position
cy.get('.apexcharts-canvas').trigger('mousemove', {
clientX: 100 + (pointsIndex * 1000) / numberOfPoints,
clientY: 150,
});

points.forEach((point, pointIndex) => {
// Ensure the label are correct
cy.get('.apexcharts-tooltip-text-y-label').should(
'contain',
`Variable ${pointIndex + 1}`
);

// Ensure the values are correct
cy.get('.apexcharts-tooltip-text-y-value').should('contain', point);
});
});
});
});

it('should render multiple variables and large series', () => {
const mockEventData = plotThreeVariables.eventData;
const mockSeries = plotThreeVariables.series;

cy.window()
.then((win) => {
return new Promise<void>((resolve) => {
// Mock data event with numberOfPoints point 50ms apart
mockEventData.forEach((data, index) => {
setTimeout(() => {
win.onSerialPortEventTrigger({
type: 'data',
data: data,
});

if (index === mockEventData.length - 1) {
resolve();
}
}, index * 50);
});
});
})
.then(() => {
// Pause data stream (to enable tooltips)
cy.get('app-plotter-component ion-button').contains('Pause').click();

// INFO: hacky way to wait until the canvas has finished updating from stream pause
cy.get('.apexcharts-canvas').invoke('width').should('be.below', 940);

// Select the first path element and extract X coordinates
cy.get('g.apexcharts-series path.apexcharts-line')
.first()
.then(($path) => {
const pageOffset = 85;
const pathData = $path.attr('d');

const xCoordinates = (pathData as string)
.split('L')
.map((segment, index, array) => {
const [x] = segment
.trim()
.replace('M', '')
.split(' ')
.map(Number);

// If it's the last element in the array, adjust the X coordinate
if (index === array.length - 1) {
return x + pageOffset - 1;
}

// Otherwise, add the pageOffset to the X value
return x + pageOffset;
})
.filter((x) => !isNaN(x)); // Filter out NaN values

// Test labels and values
mockSeries.forEach((points, pointsIndex) => {
// // Helper for debug
// cy.get('body').then(($body) => {
// const refCircle = document.createElement('div');
// refCircle.style.position = 'absolute';
// refCircle.style.left = `${xCoordinates[pointsIndex]}px`;
// refCircle.style.top = `${150}px`;
// refCircle.style.width = '5px';
// refCircle.style.height = '5px';
// refCircle.style.borderRadius = '50%';
// refCircle.style.backgroundColor = 'red'; // Red for visibility
// refCircle.style.zIndex = '9999'; // High z-index to appear on top

// // Append the circle to the body to mark the inferred points
// $body[0].appendChild(refCircle);
// });

// Move mouse to estimated tooltip position
cy.get('.apexcharts-canvas').trigger('mousemove', {
clientX: xCoordinates[pointsIndex],
clientY: 150,
});

points.forEach((point, pointIndex) => {
// Ensure the label is correct
cy.get('.apexcharts-tooltip-text-y-label').should(
'contain',
`Variable ${pointIndex + 1}`
);

// Ensure the values are correct
cy.get('.apexcharts-tooltip-text-y-value').should(
'contain',
point
);
});
});
});
});
});
});
107 changes: 107 additions & 0 deletions apps/spie-ui-e2e/src/fixtures/plotOneVariable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{
"eventData": [
"0.00\n",
"1.25\n",
"2.49\n",
"3.68\n",
"4.82\n",
"5.88\n",
"6.85\n",
"7.71\n",
"8.44\n",
"9.05\n",
"9.51\n",
"9.82\n",
"9.98\n",
"9.98\n",
"9.82\n",
"9.51\n",
"9.05\n",
"8.44\n",
"7.71\n",
"6.85\n",
"5.88\n",
"4.82\n",
"3.68\n",
"2.49\n",
"1.25\n",
"0.00\n",
"-1.25\n",
"-2.49\n",
"-3.68\n",
"-4.82\n",
"-5.88\n",
"-6.85\n",
"-7.71\n",
"-8.44\n",
"-9.05\n",
"-9.51\n",
"-9.82\n",
"-9.98\n",
"-9.98\n",
"-9.82\n",
"-9.51\n",
"-9.05\n",
"-8.44\n",
"-7.71\n",
"-6.85\n",
"-5.88\n",
"-4.82\n",
"-3.68\n",
"-2.49\n",
"-1.25\n"
],
"series": [
[0.0],
[1.25],
[2.49],
[3.68],
[4.82],
[5.88],
[6.85],
[7.71],
[8.44],
[9.05],
[9.51],
[9.82],
[9.98],
[9.98],
[9.82],
[9.51],
[9.05],
[8.44],
[7.71],
[6.85],
[5.88],
[4.82],
[3.68],
[2.49],
[1.25],
[0.0],
[-1.25],
[-2.49],
[-3.68],
[-4.82],
[-5.88],
[-6.85],
[-7.71],
[-8.44],
[-9.05],
[-9.51],
[-9.82],
[-9.98],
[-9.98],
[-9.82],
[-9.51],
[-9.05],
[-8.44],
[-7.71],
[-6.85],
[-5.88],
[-4.82],
[-3.68],
[-2.49],
[-1.25]
],
"numberOfSeries": 1
}
Loading

0 comments on commit bc285cf

Please sign in to comment.