text(react): cleanup tests, add onNodesChange tests

This commit is contained in:
moklick
2024-03-06 18:07:06 +01:00
parent 9565689dfc
commit 5c50ecfb42
9 changed files with 330 additions and 168 deletions
@@ -6,7 +6,7 @@ import * as simpleflow from '../../fixtures/simpleflow';
describe('<ReactFlow />: Basic Props', () => {
describe('uses defaultNodes and defaultEdges', () => {
beforeEach(() => {
cy.mount(<ReactFlow defaultNodes={simpleflow.nodes} defaultEdges={simpleflow.edges} />);
cy.mount(<ReactFlow defaultNodes={simpleflow.nodes} defaultEdges={simpleflow.edges} nodeDragThreshold={0} />);
});
it('mounts nodes and edges', () => {
@@ -0,0 +1,138 @@
import { useCallback, useState } from 'react';
import { OnNodesChange, Panel, ReactFlow, ReactFlowProps, applyNodeChanges, useReactFlow, Node } from '@xyflow/react';
const nodes = [
{
id: '1',
data: { label: 'Node 1' },
position: { x: 0, y: 0 },
style: { width: 100, height: 40 },
},
{
id: '2',
data: { label: 'Node 2' },
position: { x: 200, y: 200 },
style: { width: 80, height: 50 },
},
];
describe('<ReactFlow />: onNodesChange', () => {
beforeEach(() => {
const onNodesChange = cy.spy().as('onNodesChange');
cy.mount(<Comp nodes={nodes} onNodesChange={onNodesChange} />).wait(500);
});
it('receive initial dimension change', () => {
const expectedChanges = nodes.map((n) => ({
type: 'dimensions',
id: n.id,
dimensions: n.style,
}));
cy.get('@onNodesChange').should('have.callCount', 1);
cy.get('@onNodesChange').should('have.calledWith', expectedChanges);
});
it('receive replace and dimension change', () => {
const expectedDimensionChanges = [
{
type: 'dimensions',
id: '1',
dimensions: { width: 200, height: 100 },
},
];
const expectedReplaceChanges = [
{
type: 'replace',
id: '1',
item: {
...nodes[0],
computed: { positionAbsolute: nodes[0].position, width: 200, height: 100 },
style: { width: 200, height: 100 },
},
},
];
cy.get('[data-id=update-btn]').click();
cy.get('@onNodesChange').should('have.callCount', 3);
cy.get('@onNodesChange').then((s) => {
// dimension change (already checked above)
// const firstCall = s.getCall(0);
// replace change
// @ts-ignore
const secondChangeCallArgs = s.getCall(1).args[0];
// dimension change
// @ts-ignore
const thirdChangeCallArgs = s.getCall(2).args[0];
expect(secondChangeCallArgs).to.be.deep.eq(expectedReplaceChanges);
expect(thirdChangeCallArgs).to.be.deep.eq(expectedDimensionChanges);
});
});
it('receive select and unselect change', () => {
const expectedSelectChanges = [{ id: '1', type: 'select', selected: true }];
const expectedUnselectChanges = [{ id: '1', type: 'select', selected: false }];
cy.get('.react-flow__node').first().click();
cy.get('@onNodesChange').should('have.calledWith', expectedSelectChanges);
cy.get('.react-flow__pane').first().click({ force: true });
cy.get('@onNodesChange').should('have.calledWith', expectedUnselectChanges);
});
it('receive position change', () => {
const endPosition = { x: 200, y: 25 };
const expectedChanges = [
{ id: '1', type: 'position', dragging: false, position: endPosition, positionAbsolute: endPosition },
];
cy.drag('.react-flow__node:first', endPosition).then(() => {
cy.get('@onNodesChange').should('have.calledWith', expectedChanges);
});
});
it('receive remove change', () => {
const expectedChanges = [{ id: '1', type: 'remove' }];
cy.get('.react-flow__node').first().click();
cy.realPress('Backspace');
cy.get('@onNodesChange').should('have.calledWith', expectedChanges);
});
});
// test specific helpers
function UpdateButton() {
const { updateNode } = useReactFlow();
const updateNodeDimensions = () => {
updateNode('1', { style: { width: 200, height: 100 } });
};
return (
<Panel position="top-right">
<button data-id="update-btn" onClick={updateNodeDimensions} id="btn">
update
</button>
</Panel>
);
}
type CompProps = ReactFlowProps & { onNodesChange: (changes: any) => void };
function Comp(props: CompProps) {
const [nodes, setNodes] = useState(props.nodes || []);
const onNodesChange: OnNodesChange<Node> = useCallback((changes) => {
props.onNodesChange(changes);
setNodes((nds) => applyNodeChanges(changes, nds));
}, []);
return (
<ReactFlow nodes={nodes} onNodesChange={onNodesChange} nodeDragThreshold={0}>
<UpdateButton />
</ReactFlow>
);
}
+16 -22
View File
@@ -49,21 +49,16 @@ describe('Basic Flow Rendering', { testIsolation: false }, () => {
it('selects one node with a selection', () => {
cy.get('body')
.type('{shift}', { release: false })
.wait(50)
.type('{Shift}', { release: false })
.get('.react-flow__pane')
.trigger('mousedown', 1, 10, { button: 0, force: true })
.trigger('mousemove', 1000, 450, { button: 0 })
.wait(50)
.trigger('mouseup', 1000, 450, { button: 0, force: true });
.trigger('mousemove', 1000, 200, { button: 0 })
.trigger('mouseup', 1000, 200, { button: 0 });
cy.wait(200);
cy.get('.react-flow__node').eq(1).should('have.class', 'selected');
cy.get('.react-flow__node').eq(0).should('have.class', 'selected');
cy.get('.react-flow__node').eq(3).should('have.not.class', 'selected');
cy.get('.react-flow__nodesselection-rect');
cy.get('body').type('{shift}', { release: true, force: true });
});
@@ -103,13 +98,13 @@ describe('Basic Flow Rendering', { testIsolation: false }, () => {
});
});
// @TODO: why does this fail since react18?
// it('removes a node', () => {
// cy.get('.react-flow__node').contains('Node 1').click().should('have.class', 'selected');
// cy.get('html').type('{backspace}').wait(100);
// cy.get('.react-flow__node').should('have.length', 3);
// cy.get('.react-flow__edge').should('have.length', 1);
// });
it('removes a node', () => {
cy.get('.react-flow__node').contains('Node 1').click().should('have.class', 'selected');
cy.get('html').realPress('Backspace');
cy.get('.react-flow__node').should('have.length', 3);
cy.get('.react-flow__edge').should('have.length', 1);
});
it('connects nodes', () => {
cy.get('.react-flow__node')
@@ -128,13 +123,12 @@ describe('Basic Flow Rendering', { testIsolation: false }, () => {
cy.get('@edge').should('have.length', 3);
});
// @TODO: why does this fail since react18?
// it('removes an edge', () => {
// cy.get('.react-flow__edge:first').click();
// cy.get('body').type('{backspace}');
it('removes an edge', () => {
cy.get('.react-flow__edge:first').click();
cy.get('html').realPress('Backspace');
// cy.get('.react-flow__edge').should('have.length', 1);
// });
cy.get('.react-flow__edge').should('have.length', 1);
});
it('drags the pane', () => {
const styleBeforeDrag = Cypress.$('.react-flow__viewport').css('transform');
@@ -59,7 +59,7 @@ function ControlledFlow({
handlers.onConnect = onConnect;
}
return <ReactFlow nodes={nodes} edges={edges} {...handlers} {...rest} />;
return <ReactFlow nodes={nodes} edges={edges} {...handlers} {...rest} nodeDragThreshold={0} />;
}
export default ControlledFlow;
+1 -1
View File
@@ -9,7 +9,7 @@ Cypress.Commands.add('drag', (selector, { x, y }) =>
const nextY: number = centerY + y;
return elementToDrag
.trigger('mousedown', { view: window })
.trigger('mousedown', { view: window, force: true })
.trigger('mousemove', nextX, nextY, { force: true })
.wait(50)
.trigger('mouseup', { view: window, force: true });
+1
View File
@@ -15,6 +15,7 @@
// Import commands.js using ES2015 syntax:
import './commands';
import 'cypress-real-events/support';
const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/;
+2 -2
View File
@@ -30,8 +30,8 @@
"@types/react-dom": "^18.2.14",
"@vitejs/plugin-react": "4.1.1",
"@vitejs/plugin-react-swc": "^3.4.1",
"cypress": "12.17.3",
"cypress-real-events": "1.11.0",
"cypress": "13.6.6",
"cypress-real-events": "1.12.0",
"start-server-and-test": "^2.0.2",
"typescript": "5.2.2",
"vite": "4.5.0"
+1 -1
View File
@@ -15,7 +15,7 @@
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"types": ["cypress", "cypress-real-events"]
"types": ["cypress", "node", "cypress-real-events"]
},
"include": ["**/*.ts", "**/*.tsx"],
"references": [{ "path": "./tsconfig.node.json" }]