ci: Fix prettier auto-formatting (no-changelog) (#7063)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-09-01 13:29:31 +02:00 committed by GitHub
parent fa3d7070b0
commit a693b29134
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 285 additions and 234 deletions

View file

@ -2,90 +2,100 @@ import fs from 'fs';
import path from 'path';
import util from 'util';
import { exec } from 'child_process';
import { glob } from "glob";
import { glob } from 'glob';
import ts from 'typescript';
const readFileAsync = util.promisify(fs.readFile);
const execAsync = util.promisify(exec);
const filterAsync = async (asyncPredicate, arr) => {
const filterResults = await Promise.all(arr.map(async item => ({
item,
shouldKeep: await asyncPredicate(item)
})));
const filterResults = await Promise.all(
arr.map(async (item) => ({
item,
shouldKeep: await asyncPredicate(item),
})),
);
return filterResults.filter(({shouldKeep}) => shouldKeep).map(({item}) => item);
}
return filterResults.filter(({ shouldKeep }) => shouldKeep).map(({ item }) => item);
};
const isAbstractClass = (node) => {
if (ts.isClassDeclaration(node)) {
return node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AbstractKeyword) || false;
return (
node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AbstractKeyword) || false
);
}
return false;
}
const isAbstractMethod = (node) => {
return ts.isMethodDeclaration(node) && Boolean(node.modifiers?.find((modifier) => modifier.kind === ts.SyntaxKind.AbstractKeyword));
}
};
const isAbstractMethod = (node) => {
return (
ts.isMethodDeclaration(node) &&
Boolean(node.modifiers?.find((modifier) => modifier.kind === ts.SyntaxKind.AbstractKeyword))
);
};
// Function to check if a file has a function declaration, function expression, object method or class
const hasFunctionOrClass = async filePath => {
const hasFunctionOrClass = async (filePath) => {
const fileContent = await readFileAsync(filePath, 'utf-8');
const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true);
let hasFunctionOrClass = false;
const visit = node => {
const visit = (node) => {
if (
ts.isFunctionDeclaration(node)
|| ts.isFunctionExpression(node)
|| ts.isArrowFunction(node)
|| (ts.isMethodDeclaration(node) && !isAbstractMethod(node))
|| (ts.isClassDeclaration(node) && !isAbstractClass(node))
ts.isFunctionDeclaration(node) ||
ts.isFunctionExpression(node) ||
ts.isArrowFunction(node) ||
(ts.isMethodDeclaration(node) && !isAbstractMethod(node)) ||
(ts.isClassDeclaration(node) && !isAbstractClass(node))
) {
hasFunctionOrClass = true;
}
node.forEachChild(visit);
}
};
visit(sourceFile);
return hasFunctionOrClass;
}
};
const main = async () => {
// Run a git command to get a list of all changed files in the branch (branch has to be up to date with master)
const changedFiles = await execAsync(
'git diff --name-only --diff-filter=d origin/master..HEAD',
).then(({ stdout }) => stdout.trim().split('\n').filter(Boolean));
// Run a git command to get a list of all changed files in the branch (branch has to be up to date with master)
const changedFiles = await execAsync('git diff --name-only --diff-filter=d origin/master..HEAD')
.then(({stdout}) => stdout.trim().split('\n').filter(Boolean));
// Get all .spec.ts and .test.ts files from the packages
// Get all .spec.ts and .test.ts files from the packages
const specAndTestTsFiles = await glob('packages/*/**/{test,__tests__}/**/*.{spec,test}.ts');
const specAndTestTsFilesNames = specAndTestTsFiles.map(file => path.parse(file).name.replace(/\.(test|spec)/, ''));
// Filter out the .ts and .vue files from the changed files
const changedVueFiles = changedFiles.filter(file => file.endsWith('.vue'));
// .ts files with any kind of function declaration or class and not in any of the test folders
const changedTsFilesWithFunction = await filterAsync(
async filePath =>
filePath.endsWith('.ts') &&
!(await glob('packages/*/**/{test,__tests__}/*.ts')).includes(filePath) &&
await hasFunctionOrClass(filePath),
changedFiles
const specAndTestTsFilesNames = specAndTestTsFiles.map((file) =>
path.parse(file).name.replace(/\.(test|spec)/, ''),
);
// For each .ts or .vue file, check if there's a corresponding .test.ts or .spec.ts file in the repository
const missingTests = changedVueFiles.concat(changedTsFilesWithFunction).reduce((filesList, nextFile) => {
const fileName = path.parse(nextFile).name;
// Filter out the .ts and .vue files from the changed files
const changedVueFiles = changedFiles.filter((file) => file.endsWith('.vue'));
// .ts files with any kind of function declaration or class and not in any of the test folders
const changedTsFilesWithFunction = await filterAsync(
async (filePath) =>
filePath.endsWith('.ts') &&
!(await glob('packages/*/**/{test,__tests__}/*.ts')).includes(filePath) &&
(await hasFunctionOrClass(filePath)),
changedFiles,
);
if (!specAndTestTsFilesNames.includes(fileName)) {
filesList.push(nextFile);
}
// For each .ts or .vue file, check if there's a corresponding .test.ts or .spec.ts file in the repository
const missingTests = changedVueFiles
.concat(changedTsFilesWithFunction)
.reduce((filesList, nextFile) => {
const fileName = path.parse(nextFile).name;
return filesList;
}, []);
if (!specAndTestTsFilesNames.includes(fileName)) {
filesList.push(nextFile);
}
if(missingTests.length) {
return filesList;
}, []);
if (missingTests.length) {
console.error(`Missing tests for:\n${missingTests.join('\n')}`);
process.exit(1);
}

View file

@ -2,6 +2,7 @@ coverage
dist
package.json
.pnpm-lock.yml
packages/editor-ui/index.html
packages/nodes-base/nodes/**/test
cypress/fixtures
CHANGELOG.md

View file

@ -9,6 +9,7 @@
"typescript.format.enable": false,
"typescript.tsdk": "node_modules/typescript/lib",
"workspace-default-settings.runOnActivation": true,
"prettier.prettierPath": "node_modules/prettier/index.cjs",
"eslint.probe": ["javascript", "typescript", "vue"],
"eslint.workingDirectories": [
{

View file

@ -54,16 +54,22 @@ describe('Resource Locator', () => {
// unlike RMC and remote options, RLC does not support loadOptionDependsOn
it('should retrieve list options when other params throw errors', () => {
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {action: 'Resource Locator'});
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Resource Locator' });
ndv.getters.resourceLocatorInput('rlc').click();
getVisiblePopper().should('have.length', 1).findChildByTestId('rlc-item').should('have.length', 5);
getVisiblePopper()
.should('have.length', 1)
.findChildByTestId('rlc-item')
.should('have.length', 5);
ndv.actions.setInvalidExpression('fieldId');
ndv.getters.container().click(); // remove focus from input, hide expression preview
ndv.getters.resourceLocatorInput('rlc').click();
getVisiblePopper().should('have.length', 1).findChildByTestId('rlc-item').should('have.length', 5);
getVisiblePopper()
.should('have.length', 1)
.findChildByTestId('rlc-item')
.should('have.length', 5);
});
});

View file

@ -42,7 +42,7 @@ describe('Two-factor authentication', () => {
signinPage.actions.loginWithEmailAndPassword(email, password);
personalSettingsPage.actions.enableMfa();
mainSidebar.actions.signout();
const token = generateOTPToken(user.mfaSecret)
const token = generateOTPToken(user.mfaSecret);
mfaLoginPage.actions.loginWithMfaToken(email, password, token);
mainSidebar.actions.signout();
});
@ -61,7 +61,7 @@ describe('Two-factor authentication', () => {
signinPage.actions.loginWithEmailAndPassword(email, password);
personalSettingsPage.actions.enableMfa();
mainSidebar.actions.signout();
const token = generateOTPToken(user.mfaSecret)
const token = generateOTPToken(user.mfaSecret);
mfaLoginPage.actions.loginWithMfaToken(email, password, token);
personalSettingsPage.actions.disableMfa();
mainSidebar.actions.signout();

View file

@ -1,5 +1,6 @@
import {
HTTP_REQUEST_NODE_NAME, IF_NODE_NAME,
HTTP_REQUEST_NODE_NAME,
IF_NODE_NAME,
INSTANCE_OWNER,
MANUAL_TRIGGER_NODE_NAME,
SET_NODE_NAME,
@ -11,119 +12,121 @@ const ndv = new NDV();
const executionsTab = new WorkflowExecutionsTab();
describe('Debug', () => {
it('should be able to debug executions', () => {
cy.intercept('GET', '/rest/settings', (req) => {
req.on('response', (res) => {
res.send({
data: { ...res.body.data, enterprise: { debugInEditor: true } },
});
});
}).as('loadSettings');
cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');
cy.intercept('GET', '/rest/executions/*').as('getExecution');
cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions');
cy.intercept('POST', '/rest/workflows/run').as('postWorkflowRun');
it('should be able to debug executions', () => {
cy.intercept('GET', '/rest/settings', (req) => {
req.on('response', (res) => {
res.send({
data: { ...res.body.data, enterprise: { debugInEditor: true } },
});
});
}).as('loadSettings');
cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');
cy.intercept('GET', '/rest/executions/*').as('getExecution');
cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions');
cy.intercept('POST', '/rest/workflows/run').as('postWorkflowRun');
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
workflowPage.actions.visit();
workflowPage.actions.visit();
workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
workflowPage.actions.addNodeToCanvas(HTTP_REQUEST_NODE_NAME);
workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME);
ndv.actions.typeIntoParameterInput('url', 'https://foo.bar');
ndv.actions.close();
workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
workflowPage.actions.addNodeToCanvas(HTTP_REQUEST_NODE_NAME);
workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME);
ndv.actions.typeIntoParameterInput('url', 'https://foo.bar');
ndv.actions.close();
workflowPage.actions.addNodeToCanvas(SET_NODE_NAME, true);
workflowPage.actions.addNodeToCanvas(SET_NODE_NAME, true);
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
workflowPage.actions.executeWorkflow();
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
workflowPage.actions.executeWorkflow();
cy.wait(['@postWorkflowRun']);
cy.wait(['@postWorkflowRun']);
executionsTab.actions.switchToExecutionsTab();
executionsTab.actions.switchToExecutionsTab();
cy.wait(['@getExecutions', '@getCurrentExecutions']);
cy.wait(['@getExecutions', '@getCurrentExecutions']);
executionsTab.getters.executionDebugButton().should('have.text', 'Debug in editor').click();
cy.get('.el-notification').contains('Execution data imported').should('be.visible');
cy.get('.matching-pinned-nodes-confirmation').should('not.exist');
executionsTab.getters.executionDebugButton().should('have.text', 'Debug in editor').click();
cy.get('.el-notification').contains('Execution data imported').should('be.visible');
cy.get('.matching-pinned-nodes-confirmation').should('not.exist');
workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME);
ndv.actions.clearParameterInput('url');
ndv.actions.typeIntoParameterInput('url', 'https://postman-echo.com/get?foo1=bar1&foo2=bar2');
ndv.actions.close();
workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME);
ndv.actions.clearParameterInput('url');
ndv.actions.typeIntoParameterInput('url', 'https://postman-echo.com/get?foo1=bar1&foo2=bar2');
ndv.actions.close();
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
workflowPage.actions.executeWorkflow();
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
workflowPage.actions.executeWorkflow();
cy.wait(['@postWorkflowRun']);
cy.wait(['@postWorkflowRun']);
workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME);
ndv.actions.pinData();
ndv.actions.close();
workflowPage.actions.openNode(HTTP_REQUEST_NODE_NAME);
ndv.actions.pinData();
ndv.actions.close();
executionsTab.actions.switchToExecutionsTab();
executionsTab.actions.switchToExecutionsTab();
cy.wait(['@getExecutions', '@getCurrentExecutions']);
cy.wait(['@getExecutions', '@getCurrentExecutions']);
executionsTab.getters.executionListItems().should('have.length', 2).first().click();
cy.wait(['@getExecution']);
executionsTab.getters.executionListItems().should('have.length', 2).first().click();
cy.wait(['@getExecution']);
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
let confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
confirmDialog.find('li').should('have.length', 2);
confirmDialog.get('.btn--cancel').click();
let confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
confirmDialog.find('li').should('have.length', 2);
confirmDialog.get('.btn--cancel').click();
cy.wait(['@getExecutions', '@getCurrentExecutions']);
cy.wait(['@getExecutions', '@getCurrentExecutions']);
executionsTab.getters.executionListItems().should('have.length', 2).first().click();
cy.wait(['@getExecution']);
executionsTab.getters.executionListItems().should('have.length', 2).first().click();
cy.wait(['@getExecution']);
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
confirmDialog.find('li').should('have.length', 2);
confirmDialog.get('.btn--confirm').click();
confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
confirmDialog.find('li').should('have.length', 2);
confirmDialog.get('.btn--confirm').click();
workflowPage.getters.canvasNodes().first().should('have.descendants', '.node-pin-data-icon');
workflowPage.getters
.canvasNodes()
.not(':first')
.should('not.have.descendants', '.node-pin-data-icon');
workflowPage.getters.canvasNodes().first().should('have.descendants', '.node-pin-data-icon');
workflowPage.getters.canvasNodes().not(':first').should('not.have.descendants', '.node-pin-data-icon');
cy.reload(true);
cy.wait(['@getExecution']);
cy.reload(true);
cy.wait(['@getExecution']);
confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
confirmDialog.find('li').should('have.length', 1);
confirmDialog.get('.btn--confirm').click();
confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
confirmDialog.find('li').should('have.length', 1);
confirmDialog.get('.btn--confirm').click();
workflowPage.getters.canvasNodePlusEndpointByName(SET_NODE_NAME).click();
workflowPage.actions.addNodeToCanvas(IF_NODE_NAME, false);
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
workflowPage.getters.canvasNodePlusEndpointByName(SET_NODE_NAME).click();
workflowPage.actions.addNodeToCanvas(IF_NODE_NAME, false);
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
executionsTab.actions.switchToExecutionsTab();
cy.wait(['@getExecutions', '@getCurrentExecutions']);
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
executionsTab.actions.switchToExecutionsTab();
cy.wait(['@getExecutions', '@getCurrentExecutions']);
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
confirmDialog.find('li').should('have.length', 1);
confirmDialog.get('.btn--confirm').click();
workflowPage.getters.canvasNodes().last().find('.node-info-icon').should('be.empty');
confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
confirmDialog.find('li').should('have.length', 1);
confirmDialog.get('.btn--confirm').click();
workflowPage.getters.canvasNodes().last().find('.node-info-icon').should('be.empty');
workflowPage.getters.canvasNodes().first().dblclick();
ndv.getters.pinDataButton().click();
ndv.actions.close();
workflowPage.getters.canvasNodes().first().dblclick();
ndv.getters.pinDataButton().click();
ndv.actions.close();
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
workflowPage.actions.executeWorkflow();
workflowPage.actions.deleteNode(IF_NODE_NAME);
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
workflowPage.actions.executeWorkflow();
workflowPage.actions.deleteNode(IF_NODE_NAME);
executionsTab.actions.switchToExecutionsTab();
cy.wait(['@getExecutions', '@getCurrentExecutions']);
executionsTab.getters.executionListItems().should('have.length', 3).first().click();
cy.wait(['@getExecution']);
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
cy.get('.el-notification').contains('Some execution data wasn\'t imported').should('be.visible');
});
executionsTab.actions.switchToExecutionsTab();
cy.wait(['@getExecutions', '@getCurrentExecutions']);
executionsTab.getters.executionListItems().should('have.length', 3).first().click();
cy.wait(['@getExecution']);
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
cy.get('.el-notification').contains("Some execution data wasn't imported").should('be.visible');
});
});

View file

@ -9,9 +9,15 @@ describe('Resource Mapper', () => {
});
it('should not retrieve list options when required params throw errors', () => {
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {action: 'Resource Mapping Component'});
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {
action: 'Resource Mapping Component',
});
ndv.getters.resourceMapperFieldsContainer().should('be.visible').findChildByTestId('parameter-input').should('have.length', 2);
ndv.getters
.resourceMapperFieldsContainer()
.should('be.visible')
.findChildByTestId('parameter-input')
.should('have.length', 2);
ndv.actions.setInvalidExpression('fieldId');
@ -20,13 +26,23 @@ describe('Resource Mapper', () => {
});
it('should retrieve list options when optional params throw errors', () => {
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {action: 'Resource Mapping Component'});
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {
action: 'Resource Mapping Component',
});
ndv.getters.resourceMapperFieldsContainer().should('be.visible').findChildByTestId('parameter-input').should('have.length', 2);
ndv.getters
.resourceMapperFieldsContainer()
.should('be.visible')
.findChildByTestId('parameter-input')
.should('have.length', 2);
ndv.actions.setInvalidExpression('otherField');
ndv.actions.refreshResourceMapperColumns();
ndv.getters.resourceMapperFieldsContainer().should('be.visible').findChildByTestId('parameter-input').should('have.length', 2);
ndv.getters
.resourceMapperFieldsContainer()
.should('be.visible')
.findChildByTestId('parameter-input')
.should('have.length', 2);
});
});

View file

@ -291,7 +291,7 @@ describe('NDV', () => {
});
it('should not retrieve remote options when required params throw errors', () => {
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {action: 'Remote Options'});
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' });
ndv.getters.parameterInput('remoteOptions').click();
getVisibleSelect().find('.el-select-dropdown__item').should('have.length', 3);
@ -308,7 +308,7 @@ describe('NDV', () => {
});
it('should retrieve remote options when non-required params throw errors', () => {
workflowPage.actions.addInitialNodeToCanvas('E2e Test', {action: 'Remote Options'});
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' });
ndv.getters.parameterInput('remoteOptions').click();
getVisibleSelect().find('.el-select-dropdown__item').should('have.length', 3);
@ -338,7 +338,7 @@ describe('NDV', () => {
workflowPage.getters.nodeIssuesByName('Webhook').should('exist');
workflowPage.getters.canvasNodes().first().dblclick();
ndv.getters.parameterInput('path').type('t')
ndv.getters.parameterInput('path').type('t');
ndv.getters.nodeExecuteButton().should('not.be.disabled');
ndv.getters.triggerPanelExecuteButton().should('exist');

View file

@ -13,7 +13,7 @@ describe('Code node', () => {
});
it('should show correct placeholders switching modes', () => {
cy.contains("// Loop over input items and add a new field").should('be.visible');
cy.contains('// Loop over input items and add a new field').should('be.visible');
ndv.getters.parameterInput('mode').click();
ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for Each Item');
@ -22,8 +22,8 @@ describe('Code node', () => {
ndv.getters.parameterInput('mode').click();
ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for All Items');
cy.contains("// Loop over input items and add a new field").should('be.visible');
})
cy.contains('// Loop over input items and add a new field').should('be.visible');
});
it('should execute the placeholder successfully in both modes', () => {
ndv.actions.execute();
@ -36,7 +36,7 @@ describe('Code node', () => {
WorkflowPage.getters.successToast().contains('Node executed successfully');
});
})
});
describe('Ask AI', () => {
it('tab should display based on experiment', () => {
@ -53,8 +53,8 @@ describe('Code node', () => {
win.featureFlags.override('011_ask_AI', undefined);
WorkflowPage.actions.openNode('Code');
cy.getByTestId('code-node-tab-ai').should('not.exist');
})
})
});
});
describe('Enabled', () => {
beforeEach(() => {
@ -63,20 +63,19 @@ describe('Code node', () => {
win.featureFlags.override('011_ask_AI', 'gpt3');
WorkflowPage.actions.addInitialNodeToCanvas('Manual');
WorkflowPage.actions.addNodeToCanvas('Code', true, true);
})
})
});
});
it('tab should exist if experiment selected and be selectable', () => {
cy.getByTestId('code-node-tab-ai').should('exist');
cy.get('#tab-ask-ai').click();
cy.contains('Hey AI, generate JavaScript').should('exist');
})
cy.getByTestId('code-node-tab-ai').should('exist');
cy.get('#tab-ask-ai').click();
cy.contains('Hey AI, generate JavaScript').should('exist');
});
it('generate code button should have correct state & tooltips', () => {
cy.getByTestId('code-node-tab-ai').should('exist');
cy.get('#tab-ask-ai').click();
cy.getByTestId('ask-ai-cta').should('be.disabled');
cy.getByTestId('ask-ai-cta').realHover();
cy.getByTestId('ask-ai-cta-tooltip-no-input-data').should('exist');
@ -85,7 +84,7 @@ describe('Code node', () => {
cy.getByTestId('ask-ai-cta-tooltip-no-prompt').should('exist');
cy.getByTestId('ask-ai-prompt-input')
// Type random 14 character string
.type([...Array(14)].map(() => (Math.random() * 36 | 0).toString(36)).join(''))
.type([...Array(14)].map(() => ((Math.random() * 36) | 0).toString(36)).join(''));
cy.getByTestId('ask-ai-cta').realHover();
cy.getByTestId('ask-ai-cta-tooltip-prompt-too-short').should('exist');
@ -93,64 +92,66 @@ describe('Code node', () => {
cy.getByTestId('ask-ai-prompt-input')
.clear()
// Type random 15 character string
.type([...Array(15)].map(() => (Math.random() * 36 | 0).toString(36)).join(''))
.type([...Array(15)].map(() => ((Math.random() * 36) | 0).toString(36)).join(''));
cy.getByTestId('ask-ai-cta').should('be.enabled');
cy.getByTestId('ask-ai-prompt-counter').should('contain.text', '15 / 600');
})
});
it('should send correct schema and replace code', () => {
const prompt = [...Array(20)].map(() => (Math.random() * 36 | 0).toString(36)).join('');
const prompt = [...Array(20)].map(() => ((Math.random() * 36) | 0).toString(36)).join('');
cy.get('#tab-ask-ai').click();
ndv.actions.executePrevious();
cy.getByTestId('ask-ai-prompt-input').type(prompt)
cy.getByTestId('ask-ai-prompt-input').type(prompt);
cy.intercept('POST', '/rest/ask-ai', {
statusCode: 200,
body: {
data: {
code: 'console.log("Hello World")'
code: 'console.log("Hello World")',
},
}
},
}).as('ask-ai');
cy.getByTestId('ask-ai-cta').click();
const askAiReq = cy.wait('@ask-ai')
const askAiReq = cy.wait('@ask-ai');
askAiReq.its('request.body').should('have.keys', ['question', 'model', 'context', 'n8nVersion']);
askAiReq
.its('request.body')
.should('have.keys', ['question', 'model', 'context', 'n8nVersion']);
askAiReq.its('context').should('have.keys', ['schema', 'ndvSessionId', 'sessionId']);
cy.contains('Code generation completed').should('be.visible')
cy.contains('Code generation completed').should('be.visible');
cy.getByTestId('code-node-tab-code').should('contain.text', 'console.log("Hello World")');
cy.get('#tab-code').should('have.class', 'is-active');
})
});
it('should show error based on status code', () => {
const prompt = [...Array(20)].map(() => (Math.random() * 36 | 0).toString(36)).join('');
cy.get('#tab-ask-ai').click();
ndv.actions.executePrevious();
const prompt = [...Array(20)].map(() => ((Math.random() * 36) | 0).toString(36)).join('');
cy.get('#tab-ask-ai').click();
ndv.actions.executePrevious();
cy.getByTestId('ask-ai-prompt-input').type(prompt)
cy.getByTestId('ask-ai-prompt-input').type(prompt);
const handledCodes = [
{ code: 400, message: 'Code generation failed due to an unknown reason' },
{ code: 413, message: 'Your workflow data is too large for AI to process' },
{ code: 429, message: 'We\'ve hit our rate limit with our AI partner' },
{ code: 500, message: 'Code generation failed due to an unknown reason' },
]
const handledCodes = [
{ code: 400, message: 'Code generation failed due to an unknown reason' },
{ code: 413, message: 'Your workflow data is too large for AI to process' },
{ code: 429, message: "We've hit our rate limit with our AI partner" },
{ code: 500, message: 'Code generation failed due to an unknown reason' },
];
handledCodes.forEach(({ code, message }) => {
cy.intercept('POST', '/rest/ask-ai', {
statusCode: code,
status: code,
}).as('ask-ai');
handledCodes.forEach(({ code, message }) => {
cy.intercept('POST', '/rest/ask-ai', {
statusCode: code,
status: code,
}).as('ask-ai');
cy.getByTestId('ask-ai-cta').click();
cy.contains(message).should('be.visible')
})
})
})
cy.getByTestId('ask-ai-cta').click();
cy.contains(message).should('be.visible');
});
});
});
});
});

View file

@ -39,7 +39,11 @@ export class NDV extends BasePage {
inlineExpressionEditorInput: () => cy.getByTestId('inline-expression-editor-input'),
nodeParameters: () => cy.getByTestId('node-parameters'),
parameterInput: (parameterName: string) => cy.getByTestId(`parameter-input-${parameterName}`),
parameterInputIssues: (parameterName: string) => cy.getByTestId(`parameter-input-${parameterName}`).should('have.length', 1).findChildByTestId('parameter-issues'),
parameterInputIssues: (parameterName: string) =>
cy
.getByTestId(`parameter-input-${parameterName}`)
.should('have.length', 1)
.findChildByTestId('parameter-issues'),
parameterExpressionPreview: (parameterName: string) =>
this.getters
.nodeParameters()
@ -102,7 +106,11 @@ export class NDV extends BasePage {
clearParameterInput: (parameterName: string) => {
this.getters.parameterInput(parameterName).type(`{selectall}{backspace}`);
},
typeIntoParameterInput: (parameterName: string, content: string, opts?: { parseSpecialCharSequences: boolean }) => {
typeIntoParameterInput: (
parameterName: string,
content: string,
opts?: { parseSpecialCharSequences: boolean },
) => {
this.getters.parameterInput(parameterName).type(content, opts);
},
selectOptionInParameterDropdown: (parameterName: string, content: string) => {
@ -177,16 +185,22 @@ export class NDV extends BasePage {
refreshResourceMapperColumns: () => {
this.getters.resourceMapperSelectColumn().realHover();
this.getters.resourceMapperSelectColumn().findChildByTestId('action-toggle').should('have.length', 1).click();
this.getters
.resourceMapperSelectColumn()
.findChildByTestId('action-toggle')
.should('have.length', 1)
.click();
getVisiblePopper().find('li').last().click();
},
setInvalidExpression: (fieldName: string, invalidExpression?: string) => {
this.actions.typeIntoParameterInput(fieldName, "=");
this.actions.typeIntoParameterInput(fieldName, invalidExpression ?? "{{ $('unknown')", { parseSpecialCharSequences: false });
this.actions.typeIntoParameterInput(fieldName, '=');
this.actions.typeIntoParameterInput(fieldName, invalidExpression ?? "{{ $('unknown')", {
parseSpecialCharSequences: false,
});
this.actions.validateExpressionPreview(fieldName, `node doesn't exist`);
}
},
};
}

View file

@ -62,7 +62,7 @@ export class PersonalSettingsPage extends BasePage {
this.getters.enableMfaButton().click();
mfaSetupModal.getters.copySecretToClipboardButton().realClick();
cy.readClipboard().then((secret) => {
const token = generateOTPToken(secret)
const token = generateOTPToken(secret);
mfaSetupModal.getters.tokenInput().type(token);
mfaSetupModal.getters.downloadRecoveryCodesButton().click();

View file

@ -29,9 +29,11 @@ export class WorkflowPage extends BasePage {
canvasNodeByName: (nodeName: string) =>
this.getters.canvasNodes().filter(`:contains(${nodeName})`),
nodeIssuesByName: (nodeName: string) =>
this.getters.canvasNodes().filter(`:contains(${nodeName})`)
.should('have.length.greaterThan', 0)
.findChildByTestId('node-issues'),
this.getters
.canvasNodes()
.filter(`:contains(${nodeName})`)
.should('have.length.greaterThan', 0)
.findChildByTestId('node-issues'),
getEndpointSelector: (type: 'input' | 'output' | 'plus', nodeName: string, index = 0) => {
return `[data-endpoint-name='${nodeName}'][data-endpoint-type='${type}'][data-input-index='${index}']`;
},
@ -132,14 +134,16 @@ export class WorkflowPage extends BasePage {
win.preventNodeViewBeforeUnload = preventNodeViewUnload;
});
},
addInitialNodeToCanvas: (nodeDisplayName: string, opts?: { keepNdvOpen?: boolean, action?: string }) => {
addInitialNodeToCanvas: (
nodeDisplayName: string,
opts?: { keepNdvOpen?: boolean; action?: string },
) => {
this.getters.canvasPlusButton().click();
this.getters.nodeCreatorSearchBar().type(nodeDisplayName);
this.getters.nodeCreatorSearchBar().type('{enter}');
if (opts?.action) {
nodeCreator.getters.getCreatorItem(opts.action).click();
}
else if (!opts?.keepNdvOpen) {
} else if (!opts?.keepNdvOpen) {
cy.get('body').type('{esc}');
}
},

View file

@ -74,7 +74,7 @@
"@types/node": "^18.16.16",
"chokidar": "3.5.2",
"jsonwebtoken": "9.0.0",
"prettier": "^3.0.0",
"prettier": "^3.0.3",
"semver": "^7.5.4",
"tough-cookie": "^4.1.3",
"tslib": "^2.6.1",

View file

@ -6,7 +6,7 @@ This list shows all the versions which include breaking changes and how to upgra
### What changed?
In the Code node, `console.log` does not output to stdout by default.
In the Code node, `console.log` does not output to stdout by default.
### When is action necessary?

View file

@ -56,9 +56,8 @@ export class ActiveExecutions {
fullExecutionData.workflowId = workflowId;
}
const executionResult = await Container.get(ExecutionRepository).createNewExecution(
fullExecutionData,
);
const executionResult =
await Container.get(ExecutionRepository).createNewExecution(fullExecutionData);
executionId = executionResult.id;
if (executionId === undefined) {
throw new Error('There was an issue assigning an execution id to the execution');

View file

@ -1251,9 +1251,8 @@ export class Server extends AbstractServer {
Object.assign(findOptions.where, { workflowId: In(sharedWorkflowIds) });
}
const executions = await Container.get(ExecutionRepository).findMultipleExecutions(
findOptions,
);
const executions =
await Container.get(ExecutionRepository).findMultipleExecutions(findOptions);
if (!executions.length) return [];

View file

@ -54,9 +54,8 @@ export class SourceControlController {
await this.sourceControlPreferencesService.validateSourceControlPreferences(
sanitizedPreferences,
);
const updatedPreferences = await this.sourceControlPreferencesService.setPreferences(
sanitizedPreferences,
);
const updatedPreferences =
await this.sourceControlPreferencesService.setPreferences(sanitizedPreferences);
if (sanitizedPreferences.initRepo === true) {
try {
await this.sourceControlService.initializeRepository(

View file

@ -317,9 +317,8 @@ export class ExecutionsService {
const workflowRunner = new WorkflowRunner();
const retriedExecutionId = await workflowRunner.run(data);
const executionData = await Container.get(ActiveExecutions).getPostExecutePromise(
retriedExecutionId,
);
const executionData =
await Container.get(ActiveExecutions).getPostExecutePromise(retriedExecutionId);
if (!executionData) {
throw new Error('The retry did not start for an unknown reason.');

View file

@ -3,7 +3,7 @@ import { createEventBus } from '../event-bus';
// @TODO: Remove when conflicting vitest and jest globals are reconciled
declare global {
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
const vi: typeof import('vitest')['vitest'];
const vi: (typeof import('vitest'))['vitest'];
}
describe('createEventBus()', () => {

View file

@ -65,7 +65,7 @@
"n8n-workflow": "workspace:*",
"normalize-wheel": "^1.0.1",
"pinia": "^2.1.6",
"prettier": "^3.0.0",
"prettier": "^3.0.3",
"stream-browserify": "^3.0.0",
"qrcode.vue": "^3.3.4",
"timeago.js": "^4.0.2",

View file

@ -3891,9 +3891,8 @@ export default defineComponent({
await Promise.all([this.loadCredentials(), this.loadVariables(), this.tagsStore.fetchAll()]);
if (workflowId !== null && !this.uiStore.stateIsDirty) {
const workflow: IWorkflowDb | undefined = await this.workflowsStore.fetchWorkflow(
workflowId,
);
const workflow: IWorkflowDb | undefined =
await this.workflowsStore.fetchWorkflow(workflowId);
if (workflow) {
this.titleSet(workflow.name, 'IDLE');
await this.openWorkflow(workflow);

View file

@ -11,7 +11,7 @@ overrides:
'@types/node': ^18.16.16
chokidar: 3.5.2
jsonwebtoken: 9.0.0
prettier: ^3.0.0
prettier: ^3.0.3
semver: ^7.5.4
tough-cookie: ^4.1.3
tslib: ^2.6.1
@ -178,7 +178,7 @@ importers:
version: 1.0.0
eslint-plugin-prettier:
specifier: ^5.0.0
version: 5.0.0(@types/eslint@8.44.1)(eslint-config-prettier@8.9.0)(eslint@8.45.0)(prettier@3.0.0)
version: 5.0.0(@types/eslint@8.44.1)(eslint-config-prettier@8.9.0)(eslint@8.45.0)(prettier@3.0.3)
eslint-plugin-unicorn:
specifier: ^48.0.1
version: 48.0.1(eslint@8.45.0)
@ -894,8 +894,8 @@ importers:
specifier: ^2.1.6
version: 2.1.6(typescript@5.2.2)(vue@3.3.4)
prettier:
specifier: ^3.0.0
version: 3.0.0
specifier: ^3.0.3
version: 3.0.3
qrcode.vue:
specifier: ^3.3.4
version: 3.3.4(vue@3.3.4)
@ -5885,7 +5885,7 @@ packages:
less-loader: 11.1.3(less@4.1.3)(webpack@5.75.0)
postcss: 8.4.27
postcss-loader: 7.3.3(postcss@8.4.27)(webpack@5.75.0)
prettier: 3.0.0
prettier: 3.0.3
react: 17.0.2
react-dom: 18.2.0(react@17.0.2)
resolve-url-loader: 5.0.0
@ -6199,7 +6199,7 @@ packages:
jscodeshift: 0.14.0(@babel/preset-env@7.22.9)
leven: 3.1.0
ora: 5.4.1
prettier: 3.0.0
prettier: 3.0.3
prompts: 2.4.2
puppeteer-core: 2.1.1
read-pkg-up: 7.0.1
@ -6250,7 +6250,7 @@ packages:
globby: 11.1.0
jscodeshift: 0.14.0(@babel/preset-env@7.22.9)
lodash: 4.17.21
prettier: 3.0.0
prettier: 3.0.3
recast: 0.23.3
transitivePeerDependencies:
- supports-color
@ -10620,7 +10620,7 @@ packages:
cypress: ^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x || ^11.x || ^12.x
dependencies:
cypress: 12.17.2
prettier: 3.0.0
prettier: 3.0.3
dev: true
/cypress@12.17.2:
@ -11810,14 +11810,14 @@ packages:
- typescript
dev: true
/eslint-plugin-prettier@5.0.0(@types/eslint@8.44.1)(eslint-config-prettier@8.9.0)(eslint@8.45.0)(prettier@3.0.0):
/eslint-plugin-prettier@5.0.0(@types/eslint@8.44.1)(eslint-config-prettier@8.9.0)(eslint@8.45.0)(prettier@3.0.3):
resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
'@types/eslint': '>=8.0.0'
eslint: '>=8.0.0'
eslint-config-prettier: '*'
prettier: ^3.0.0
prettier: ^3.0.3
peerDependenciesMeta:
'@types/eslint':
optional: true
@ -11827,7 +11827,7 @@ packages:
'@types/eslint': 8.44.1
eslint: 8.45.0
eslint-config-prettier: 8.9.0(eslint@8.45.0)
prettier: 3.0.0
prettier: 3.0.3
prettier-linter-helpers: 1.0.0
synckit: 0.8.5
dev: true
@ -18035,8 +18035,8 @@ packages:
fast-diff: 1.2.0
dev: true
/prettier@3.0.0:
resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==}
/prettier@3.0.3:
resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==}
engines: {node: '>=14'}
hasBin: true