mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
feat(editor): Improve performance by importing routes dynamically and add route guards (no-changelog) (#7567)
**Before:** <img width="657" alt="image" src="https://github.com/n8n-io/n8n/assets/6179477/0bcced2b-9d3a-43b3-80d7-3c72619941fa"> **After:** <img width="660" alt="image" src="https://github.com/n8n-io/n8n/assets/6179477/e74e0bbf-bf33-49b4-ae11-65f640405ac8">
This commit is contained in:
@@ -44,7 +44,7 @@ describe('Undo/Redo', () => {
|
||||
WorkflowPage.getters
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.css', 'left', '860px')
|
||||
.should('have.css', 'top', '220px')
|
||||
.should('have.css', 'top', '220px');
|
||||
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.have.length', 2);
|
||||
@@ -62,7 +62,7 @@ describe('Undo/Redo', () => {
|
||||
WorkflowPage.getters
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.css', 'left', '860px')
|
||||
.should('have.css', 'top', '220px')
|
||||
.should('have.css', 'top', '220px');
|
||||
});
|
||||
|
||||
it('should undo/redo deleting node using delete button', () => {
|
||||
@@ -137,18 +137,18 @@ describe('Undo/Redo', () => {
|
||||
WorkflowPage.getters
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.css', 'left', '740px')
|
||||
.should('have.css', 'top', '320px')
|
||||
.should('have.css', 'top', '320px');
|
||||
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.css', 'left', '640px')
|
||||
.should('have.css', 'top', '220px')
|
||||
.should('have.css', 'top', '220px');
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.css', 'left', '740px')
|
||||
.should('have.css', 'top', '320px')
|
||||
.should('have.css', 'top', '320px');
|
||||
});
|
||||
|
||||
it('should undo/redo deleting a connection by pressing delete button', () => {
|
||||
@@ -276,9 +276,6 @@ describe('Undo/Redo', () => {
|
||||
});
|
||||
|
||||
it('should undo/redo multiple steps', () => {
|
||||
const initialPosition = {left: '420px', top: '220px'};
|
||||
const movedPosition = {left: '540px', top: '360px'};
|
||||
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
// WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME);
|
||||
@@ -289,48 +286,54 @@ describe('Undo/Redo', () => {
|
||||
// Disable last node
|
||||
WorkflowPage.getters.canvasNodes().last().click();
|
||||
WorkflowPage.actions.hitDisableNodeShortcut();
|
||||
|
||||
// Move first one
|
||||
WorkflowPage.getters.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', initialPosition.left)
|
||||
.should('have.css', 'top', initialPosition.top)
|
||||
WorkflowPage.actions
|
||||
.getNodePosition(WorkflowPage.getters.canvasNodes().first())
|
||||
.then((initialPosition) => {
|
||||
WorkflowPage.getters.canvasNodes().first().click();
|
||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], {
|
||||
clickToFinish: true,
|
||||
});
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', `${initialPosition.left + 120}px`)
|
||||
.should('have.css', 'top', `${initialPosition.top + 140}px`);
|
||||
|
||||
WorkflowPage.getters.canvasNodes().first().click();
|
||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], { clickToFinish: true });
|
||||
WorkflowPage.getters.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', movedPosition.left)
|
||||
.should('have.css', 'top', movedPosition.top)
|
||||
// Delete the set node
|
||||
WorkflowPage.getters.canvasNodeByName(EDIT_FIELDS_SET_NODE_NAME).click().click();
|
||||
cy.get('body').type('{backspace}');
|
||||
// Delete the set node
|
||||
WorkflowPage.getters.canvasNodeByName(EDIT_FIELDS_SET_NODE_NAME).click().click();
|
||||
cy.get('body').type('{backspace}');
|
||||
|
||||
// First undo: Should return deleted node
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 4);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 3);
|
||||
// Second undo: Should move first node to it's original position
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', initialPosition.left)
|
||||
.should('have.css', 'top', initialPosition.top)
|
||||
// Third undo: Should enable last node
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
||||
// First undo: Should return deleted node
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 4);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 3);
|
||||
// Second undo: Should move first node to it's original position
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', `${initialPosition.left}px`)
|
||||
.should('have.css', 'top', `${initialPosition.top}px`);
|
||||
// Third undo: Should enable last node
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
||||
|
||||
// First redo: Should disable last node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
||||
// Second redo: Should move the first node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', movedPosition.left)
|
||||
.should('have.css', 'top', movedPosition.top)
|
||||
// Third redo: Should delete the Set node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 2);
|
||||
// First redo: Should disable last node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
||||
// Second redo: Should move the first node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', `${initialPosition.left + 120}px`)
|
||||
.should('have.css', 'top', `${initialPosition.top + 140}px`);
|
||||
// Third redo: Should delete the Set node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -176,6 +176,7 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
||||
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
|
||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], { clickToFinish: true });
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
|
||||
@@ -59,6 +59,7 @@ describe('Sharing', { disableAutoLogin: true }, () => {
|
||||
cy.visit(workflowsPage.url);
|
||||
workflowsPage.getters.createWorkflowButton().click();
|
||||
cy.createFixtureWorkflow('Test_workflow_1.json', 'Workflow W2');
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
cy.url().then((url) => {
|
||||
workflowW2Url = url;
|
||||
});
|
||||
|
||||
@@ -84,8 +84,11 @@ describe('Canvas Actions', () => {
|
||||
|
||||
moveSticky({ top: 200, left: 200 });
|
||||
|
||||
dragRightEdge({ left: 200, top: 200, height: 160, width: 240 }, 100);
|
||||
dragRightEdge({ left: 200, top: 200, height: 160, width: 240 }, -50);
|
||||
cy.drag('[data-test-id="sticky"] [data-dir="right"]', [100, 100]);
|
||||
checkStickiesStyle(100, 20, 160, 346);
|
||||
|
||||
cy.drag('[data-test-id="sticky"] [data-dir="right"]', [-50, -50]);
|
||||
checkStickiesStyle(100, 20, 160, 302);
|
||||
});
|
||||
|
||||
it('expands/shrinks sticky from the left edge', () => {
|
||||
@@ -205,27 +208,6 @@ type Position = {
|
||||
left: number;
|
||||
};
|
||||
|
||||
type BoundingBox = {
|
||||
height: number;
|
||||
width: number;
|
||||
top: number;
|
||||
left: number;
|
||||
};
|
||||
|
||||
function dragRightEdge(curr: BoundingBox, move: number) {
|
||||
workflowPage.getters
|
||||
.stickies()
|
||||
.first()
|
||||
.then(($el) => {
|
||||
const { left, top, height, width } = curr;
|
||||
cy.drag(`[data-test-id="sticky"] [data-dir="right"]`, [left + width + move, 0], {
|
||||
abs: true,
|
||||
});
|
||||
stickyShouldBePositionedCorrectly({ top, left });
|
||||
stickyShouldHaveCorrectSize([height, width * 1.5 + move]);
|
||||
});
|
||||
}
|
||||
|
||||
function shouldHaveOneSticky() {
|
||||
workflowPage.getters.stickies().should('have.length', 1);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ describe('BannerStack', { disableAutoLogin: true }, () => {
|
||||
});
|
||||
|
||||
it('should render trial banner for opt-in cloud user', () => {
|
||||
cy.intercept('GET', '/rest/admin/cloud-plan', {
|
||||
body: planData,
|
||||
}).as('getPlanData');
|
||||
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
@@ -22,10 +26,6 @@ describe('BannerStack', { disableAutoLogin: true }, () => {
|
||||
});
|
||||
}).as('loadSettings');
|
||||
|
||||
cy.intercept('GET', '/rest/admin/cloud-plan', {
|
||||
body: planData,
|
||||
}).as('getPlanData');
|
||||
|
||||
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
|
||||
|
||||
cy.visit(workflowPage.url);
|
||||
|
||||
@@ -100,6 +100,7 @@ describe('Workflow Actions', () => {
|
||||
cy.get('body').type(META_KEY, { release: false }).type('s');
|
||||
cy.get('body').type(META_KEY, { release: false }).type('s');
|
||||
cy.wrap(null).then(() => expect(interceptCalledCount).to.eq(0));
|
||||
cy.waitForLoad();
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
cy.get('body').type(META_KEY, { release: false }).type('s');
|
||||
cy.wait('@saveWorkflow');
|
||||
|
||||
@@ -72,15 +72,17 @@ export class NDV extends BasePage {
|
||||
this.getters.resourceLocator(paramName).find('[data-test-id="rlc-mode-selector"]'),
|
||||
resourceMapperFieldsContainer: () => cy.getByTestId('mapping-fields-container'),
|
||||
resourceMapperSelectColumn: () => cy.getByTestId('matching-column-select'),
|
||||
resourceMapperRemoveFieldButton: (fieldName: string) => cy.getByTestId(`remove-field-button-${fieldName}`),
|
||||
resourceMapperColumnsOptionsButton: () => cy.getByTestId('columns-parameter-input-options-container'),
|
||||
resourceMapperRemoveFieldButton: (fieldName: string) =>
|
||||
cy.getByTestId(`remove-field-button-${fieldName}`),
|
||||
resourceMapperColumnsOptionsButton: () =>
|
||||
cy.getByTestId('columns-parameter-input-options-container'),
|
||||
resourceMapperRemoveAllFieldsOption: () => cy.getByTestId('action-removeAllFields'),
|
||||
sqlEditorContainer: () => cy.getByTestId('sql-editor-container'),
|
||||
};
|
||||
|
||||
actions = {
|
||||
pinData: () => {
|
||||
this.getters.pinDataButton().click();
|
||||
this.getters.pinDataButton().click({ force: true });
|
||||
},
|
||||
editPinnedData: () => {
|
||||
this.getters.editPinnedDataButton().click();
|
||||
@@ -119,7 +121,7 @@ export class NDV extends BasePage {
|
||||
typeIntoParameterInput: (
|
||||
parameterName: string,
|
||||
content: string,
|
||||
opts?: { parseSpecialCharSequences: boolean, delay?: number },
|
||||
opts?: { parseSpecialCharSequences: boolean; delay?: number },
|
||||
) => {
|
||||
this.getters.parameterInput(parameterName).type(content, opts);
|
||||
},
|
||||
@@ -204,7 +206,15 @@ export class NDV extends BasePage {
|
||||
getVisiblePopper().find('li').last().click();
|
||||
},
|
||||
|
||||
setInvalidExpression: ({ fieldName, invalidExpression, delay }: { fieldName: string, invalidExpression?: string, delay?: number }) => {
|
||||
setInvalidExpression: ({
|
||||
fieldName,
|
||||
invalidExpression,
|
||||
delay,
|
||||
}: {
|
||||
fieldName: string;
|
||||
invalidExpression?: string;
|
||||
delay?: number;
|
||||
}) => {
|
||||
this.actions.typeIntoParameterInput(fieldName, '=');
|
||||
this.actions.typeIntoParameterInput(fieldName, invalidExpression ?? "{{ $('unknown')", {
|
||||
parseSpecialCharSequences: false,
|
||||
|
||||
@@ -214,6 +214,7 @@ export class WorkflowPage extends BasePage {
|
||||
this.getters.saveButton().should('contain', 'Save');
|
||||
this.getters.saveButton().click();
|
||||
this.getters.saveButton().should('contain', 'Saved');
|
||||
cy.url().should('not.have.string', '/new');
|
||||
},
|
||||
saveWorkflowUsingKeyboardShortcut: () => {
|
||||
cy.intercept('POST', '/rest/workflows').as('createWorkflow');
|
||||
@@ -340,5 +341,11 @@ export class WorkflowPage extends BasePage {
|
||||
cy.getByTestId('node-view-wrapper').trigger('mouseup', to[0], to[1], { force: true });
|
||||
cy.get('#select-box').should('not.be.visible');
|
||||
},
|
||||
getNodePosition: (node: Cypress.Chainable<JQuery<HTMLElement>>) => {
|
||||
return node.then(($el) => ({
|
||||
left: +$el[0].style.left.replace('px', ''),
|
||||
top: +$el[0].style.top.replace('px', ''),
|
||||
}));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ Cypress.Commands.add('createFixtureWorkflow', (fixtureKey, workflowName) => {
|
||||
|
||||
cy.waitForLoad(false);
|
||||
workflowPage.actions.setWorkflowName(workflowName);
|
||||
|
||||
workflowPage.getters.saveButton().should('contain', 'Saved');
|
||||
workflowPage.actions.zoomToFit();
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
@@ -33,7 +33,7 @@ Cypress.Commands.add('waitForLoad', (waitForIntercepts = true) => {
|
||||
// we can't set them up here because at this point it would be too late
|
||||
// and the requests would already have been made
|
||||
if (waitForIntercepts) {
|
||||
cy.wait(['@loadSettings']);
|
||||
cy.wait(['@loadSettings', '@loadNodeTypes']);
|
||||
}
|
||||
cy.getByTestId('node-view-loader', { timeout: 20000 }).should('not.exist');
|
||||
cy.get('.el-loading-mask', { timeout: 20000 }).should('not.exist');
|
||||
|
||||
@@ -18,6 +18,7 @@ beforeEach(() => {
|
||||
}
|
||||
|
||||
cy.intercept('GET', '/rest/settings').as('loadSettings');
|
||||
cy.intercept('GET', '/types/nodes.json').as('loadNodeTypes');
|
||||
|
||||
// Always intercept the request to test credentials and return a success
|
||||
cy.intercept('POST', '/rest/credentials/test', {
|
||||
|
||||
@@ -34,7 +34,7 @@ declare global {
|
||||
drag(
|
||||
selector: string | Cypress.Chainable<JQuery<HTMLElement>>,
|
||||
target: [number, number],
|
||||
options?: { abs?: boolean; index?: number; realMouse?: boolean },
|
||||
options?: { abs?: boolean; index?: number; realMouse?: boolean; clickToFinish?: boolean },
|
||||
): void;
|
||||
draganddrop(draggableSelector: string, droppableSelector: string): void;
|
||||
shouldNotHaveConsoleErrors(): void;
|
||||
|
||||
Reference in New Issue
Block a user