mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
test: Migrate 6-code-node tests to Playwright (#18454)
This commit is contained in:
@@ -1,209 +0,0 @@
|
|||||||
import { nanoid } from 'nanoid';
|
|
||||||
|
|
||||||
import { NDV } from '../pages/ndv';
|
|
||||||
import { successToast } from '../pages/notifications';
|
|
||||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
|
||||||
|
|
||||||
const WorkflowPage = new WorkflowPageClass();
|
|
||||||
const ndv = new NDV();
|
|
||||||
|
|
||||||
const getParameter = () => ndv.getters.parameterInput('jsCode').should('be.visible');
|
|
||||||
const getEditor = () => getParameter().find('.cm-content').should('exist');
|
|
||||||
|
|
||||||
describe('Code node', () => {
|
|
||||||
describe('Code editor', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
WorkflowPage.actions.visit();
|
|
||||||
WorkflowPage.actions.addInitialNodeToCanvas('Manual');
|
|
||||||
WorkflowPage.actions.addNodeToCanvas('Code', true, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show correct placeholders switching modes', () => {
|
|
||||||
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');
|
|
||||||
|
|
||||||
cy.contains("// Add a new field called 'myNewField'").should('be.visible');
|
|
||||||
|
|
||||||
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');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should execute the placeholder successfully in both modes', () => {
|
|
||||||
ndv.actions.execute();
|
|
||||||
|
|
||||||
successToast().contains('Node executed successfully');
|
|
||||||
ndv.getters.parameterInput('mode').click();
|
|
||||||
ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for Each Item');
|
|
||||||
|
|
||||||
ndv.actions.execute();
|
|
||||||
|
|
||||||
successToast().contains('Node executed successfully');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow switching between sibling code nodes', () => {
|
|
||||||
// Setup
|
|
||||||
getEditor().type('{selectall}').paste("console.log('code node 1')");
|
|
||||||
ndv.actions.close();
|
|
||||||
WorkflowPage.actions.addNodeToCanvas('Code', true, true);
|
|
||||||
getEditor().type('{selectall}').paste("console.log('code node 2')");
|
|
||||||
ndv.actions.close();
|
|
||||||
|
|
||||||
WorkflowPage.actions.openNode('Code');
|
|
||||||
ndv.actions.clickFloatingNode('Code1');
|
|
||||||
getEditor().should('have.text', "console.log('code node 2')");
|
|
||||||
|
|
||||||
ndv.actions.clickFloatingNode('Code');
|
|
||||||
getEditor().should('have.text', "console.log('code node 1')");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show lint errors in `runOnceForAllItems` mode', () => {
|
|
||||||
getEditor()
|
|
||||||
.type('{selectall}')
|
|
||||||
.paste(`$input.itemMatching()
|
|
||||||
$input.item
|
|
||||||
$('When clicking ‘Execute workflow’').item
|
|
||||||
$input.first(1)
|
|
||||||
|
|
||||||
for (const item of $input.all()) {
|
|
||||||
item.foo
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
`);
|
|
||||||
getParameter().get('.cm-lintRange-error').should('have.length', 6);
|
|
||||||
getParameter().contains('itemMatching').realHover();
|
|
||||||
cy.get('.cm-tooltip-lint').should(
|
|
||||||
'have.text',
|
|
||||||
'`.itemMatching()` expects an item index to be passed in as its argument.',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show lint errors in `runOnceForEachItem` mode', () => {
|
|
||||||
ndv.getters.parameterInput('mode').click();
|
|
||||||
ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for Each Item');
|
|
||||||
getEditor()
|
|
||||||
.type('{selectall}')
|
|
||||||
.paste(`$input.itemMatching()
|
|
||||||
$input.all()
|
|
||||||
$input.first()
|
|
||||||
$input.item()
|
|
||||||
|
|
||||||
return []
|
|
||||||
`);
|
|
||||||
|
|
||||||
getParameter().get('.cm-lintRange-error').should('have.length.gte', 5);
|
|
||||||
getParameter().contains('all').realHover();
|
|
||||||
cy.get('.cm-tooltip-lint').should(
|
|
||||||
'have.text',
|
|
||||||
"Method `$input.all()` is only available in the 'Run Once for All Items' mode.",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Ask AI', () => {
|
|
||||||
describe('Enabled', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.enableFeature('askAi');
|
|
||||||
WorkflowPage.actions.visit();
|
|
||||||
|
|
||||||
cy.window().then(() => {
|
|
||||||
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');
|
|
||||||
});
|
|
||||||
|
|
||||||
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');
|
|
||||||
ndv.actions.executePrevious();
|
|
||||||
cy.getByTestId('ask-ai-cta').realHover();
|
|
||||||
cy.getByTestId('ask-ai-cta-tooltip-no-prompt').should('exist');
|
|
||||||
cy.getByTestId('ask-ai-prompt-input')
|
|
||||||
// Type random 14 character string
|
|
||||||
.type(nanoid(14));
|
|
||||||
|
|
||||||
cy.getByTestId('ask-ai-cta').realHover();
|
|
||||||
cy.getByTestId('ask-ai-cta-tooltip-prompt-too-short').should('exist');
|
|
||||||
|
|
||||||
cy.getByTestId('ask-ai-prompt-input')
|
|
||||||
.clear()
|
|
||||||
// Type random 15 character string
|
|
||||||
.type(nanoid(15));
|
|
||||||
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 = nanoid(20);
|
|
||||||
cy.get('#tab-ask-ai').click();
|
|
||||||
ndv.actions.executePrevious();
|
|
||||||
|
|
||||||
cy.getByTestId('ask-ai-prompt-input').type(prompt);
|
|
||||||
|
|
||||||
cy.intercept('POST', '/rest/ai/ask-ai', {
|
|
||||||
statusCode: 200,
|
|
||||||
body: {
|
|
||||||
data: {
|
|
||||||
code: 'console.log("Hello World")',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}).as('ask-ai');
|
|
||||||
|
|
||||||
cy.getByTestId('ask-ai-cta').click();
|
|
||||||
const askAiReq = cy.wait('@ask-ai');
|
|
||||||
|
|
||||||
askAiReq.its('request.body').should('have.keys', ['question', 'context', 'forNode']);
|
|
||||||
askAiReq
|
|
||||||
.its('context')
|
|
||||||
.should('have.keys', ['schema', 'ndvPushRef', 'pushRef', 'inputSchema']);
|
|
||||||
|
|
||||||
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');
|
|
||||||
});
|
|
||||||
|
|
||||||
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 with error: Request failed with status code 500. Try again in a few minutes',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
handledCodes.forEach(({ code, message }) => {
|
|
||||||
it(`should show error based on status code ${code}`, () => {
|
|
||||||
const prompt = nanoid(20);
|
|
||||||
cy.get('#tab-ask-ai').click();
|
|
||||||
ndv.actions.executePrevious();
|
|
||||||
|
|
||||||
cy.getByTestId('ask-ai-prompt-input').type(prompt);
|
|
||||||
|
|
||||||
cy.intercept('POST', '/rest/ai/ask-ai', {
|
|
||||||
statusCode: code,
|
|
||||||
status: code,
|
|
||||||
}).as('ask-ai');
|
|
||||||
|
|
||||||
cy.getByTestId('ask-ai-cta').click();
|
|
||||||
cy.contains(message).should('be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -47,4 +47,106 @@ export class NodeDisplayViewPage extends BasePage {
|
|||||||
getParameterExpressionPreviewValue() {
|
getParameterExpressionPreviewValue() {
|
||||||
return this.page.getByTestId('parameter-expression-preview-value');
|
return this.page.getByTestId('parameter-expression-preview-value');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get parameter input by name (for Code node and similar)
|
||||||
|
* @param parameterName - The name of the parameter e.g 'jsCode', 'mode'
|
||||||
|
*/
|
||||||
|
getParameterInput(parameterName: string) {
|
||||||
|
return this.page.getByTestId(`parameter-input-${parameterName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select option in parameter dropdown
|
||||||
|
* @param parameterName - The parameter name
|
||||||
|
* @param optionText - The text of the option to select
|
||||||
|
*/
|
||||||
|
async selectOptionInParameterDropdown(parameterName: string, optionText: string) {
|
||||||
|
const dropdown = this.getParameterInput(parameterName);
|
||||||
|
await dropdown.click();
|
||||||
|
await this.page.getByRole('option', { name: optionText }).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click on a floating node in the NDV (for switching between connected nodes)
|
||||||
|
* @param nodeName - The name of the node to click
|
||||||
|
*/
|
||||||
|
async clickFloatingNode(nodeName: string) {
|
||||||
|
await this.page.locator(`[data-test-id="floating-node"][data-node-name="${nodeName}"]`).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the previous node (useful for providing input data)
|
||||||
|
*/
|
||||||
|
async executePrevious() {
|
||||||
|
await this.clickByTestId('execute-previous-node');
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickAskAiTab() {
|
||||||
|
await this.page.locator('#tab-ask-ai').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
getAskAiTabPanel() {
|
||||||
|
return this.page.getByTestId('code-node-tab-ai');
|
||||||
|
}
|
||||||
|
|
||||||
|
getAskAiCtaButton() {
|
||||||
|
return this.page.getByTestId('ask-ai-cta');
|
||||||
|
}
|
||||||
|
|
||||||
|
getAskAiPromptInput() {
|
||||||
|
return this.page.getByTestId('ask-ai-prompt-input');
|
||||||
|
}
|
||||||
|
|
||||||
|
getAskAiPromptCounter() {
|
||||||
|
return this.page.getByTestId('ask-ai-prompt-counter');
|
||||||
|
}
|
||||||
|
|
||||||
|
getAskAiCtaTooltipNoInputData() {
|
||||||
|
return this.page.getByTestId('ask-ai-cta-tooltip-no-input-data');
|
||||||
|
}
|
||||||
|
|
||||||
|
getAskAiCtaTooltipNoPrompt() {
|
||||||
|
return this.page.getByTestId('ask-ai-cta-tooltip-no-prompt');
|
||||||
|
}
|
||||||
|
|
||||||
|
getAskAiCtaTooltipPromptTooShort() {
|
||||||
|
return this.page.getByTestId('ask-ai-cta-tooltip-prompt-too-short');
|
||||||
|
}
|
||||||
|
|
||||||
|
getCodeTabPanel() {
|
||||||
|
return this.page.getByTestId('code-node-tab-code');
|
||||||
|
}
|
||||||
|
|
||||||
|
getCodeTab() {
|
||||||
|
return this.page.locator('#tab-code');
|
||||||
|
}
|
||||||
|
|
||||||
|
getCodeEditor() {
|
||||||
|
return this.getParameterInput('jsCode').locator('.cm-content');
|
||||||
|
}
|
||||||
|
|
||||||
|
getLintErrors() {
|
||||||
|
return this.getParameterInput('jsCode').locator('.cm-lintRange-error');
|
||||||
|
}
|
||||||
|
|
||||||
|
getLintTooltip() {
|
||||||
|
return this.page.locator('.cm-tooltip-lint');
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlaceholderText(text: string) {
|
||||||
|
return this.page.getByText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeyAiText() {
|
||||||
|
return this.page.locator('text=Hey AI, generate JavaScript');
|
||||||
|
}
|
||||||
|
|
||||||
|
getCodeGenerationCompletedText() {
|
||||||
|
return this.page.locator('text=Code generation completed');
|
||||||
|
}
|
||||||
|
|
||||||
|
getErrorMessageText(message: string) {
|
||||||
|
return this.page.locator(`text=${message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
216
packages/testing/playwright/tests/ui/6-code-node.spec.ts
Normal file
216
packages/testing/playwright/tests/ui/6-code-node.spec.ts
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
|
import { CODE_NODE_NAME, MANUAL_TRIGGER_NODE_NAME } from '../../config/constants';
|
||||||
|
import { test, expect } from '../../fixtures/base';
|
||||||
|
|
||||||
|
test.describe('Code node', () => {
|
||||||
|
test.describe('Code editor', () => {
|
||||||
|
test.beforeEach(async ({ n8n }) => {
|
||||||
|
await n8n.goHome();
|
||||||
|
await n8n.workflows.clickAddWorkflowButton();
|
||||||
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||||
|
await n8n.canvas.addNode(CODE_NODE_NAME);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show correct placeholders switching modes', async ({ n8n }) => {
|
||||||
|
await expect(
|
||||||
|
n8n.ndv.getPlaceholderText('// Loop over input items and add a new field'),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.getParameterInput('mode').click();
|
||||||
|
await n8n.page.getByRole('option', { name: 'Run Once for Each Item' }).click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
n8n.ndv.getPlaceholderText("// Add a new field called 'myNewField'"),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.getParameterInput('mode').click();
|
||||||
|
await n8n.page.getByRole('option', { name: 'Run Once for All Items' }).click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
n8n.ndv.getPlaceholderText('// Loop over input items and add a new field'),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should execute the placeholder successfully in both modes', async ({ n8n }) => {
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
n8n.notifications.notificationContainerByText('Node executed successfully').first(),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.getParameterInput('mode').click();
|
||||||
|
await n8n.page.getByRole('option', { name: 'Run Once for Each Item' }).click();
|
||||||
|
|
||||||
|
await n8n.ndv.execute();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
n8n.notifications.notificationContainerByText('Node executed successfully').first(),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should allow switching between sibling code nodes', async ({ n8n }) => {
|
||||||
|
await n8n.ndv.getCodeEditor().fill("console.log('code node 1')");
|
||||||
|
await n8n.ndv.close();
|
||||||
|
|
||||||
|
await n8n.canvas.addNode(CODE_NODE_NAME);
|
||||||
|
|
||||||
|
await n8n.ndv.getCodeEditor().fill("console.log('code node 2')");
|
||||||
|
await n8n.ndv.close();
|
||||||
|
|
||||||
|
await n8n.canvas.openNode(CODE_NODE_NAME);
|
||||||
|
|
||||||
|
await n8n.ndv.clickFloatingNode('Code1');
|
||||||
|
await expect(n8n.ndv.getCodeEditor()).toContainText("console.log('code node 2')");
|
||||||
|
|
||||||
|
await n8n.ndv.clickFloatingNode('Code');
|
||||||
|
await expect(n8n.ndv.getCodeEditor()).toContainText("console.log('code node 1')");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show lint errors in `runOnceForAllItems` mode', async ({ n8n }) => {
|
||||||
|
await n8n.ndv.getCodeEditor().fill(`$input.itemMatching()
|
||||||
|
$input.item
|
||||||
|
$('When clicking ‘Execute workflow’').item
|
||||||
|
$input.first(1)
|
||||||
|
|
||||||
|
for (const item of $input.all()) {
|
||||||
|
item.foo
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
`);
|
||||||
|
await expect(n8n.ndv.getLintErrors()).toHaveCount(6);
|
||||||
|
await n8n.ndv.getParameterInput('jsCode').getByText('itemMatching').hover();
|
||||||
|
await expect(n8n.ndv.getLintTooltip()).toContainText(
|
||||||
|
'`.itemMatching()` expects an item index to be passed in as its argument.',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show lint errors in `runOnceForEachItem` mode', async ({ n8n }) => {
|
||||||
|
await n8n.ndv.getParameterInput('mode').click();
|
||||||
|
await n8n.page.getByRole('option', { name: 'Run Once for Each Item' }).click();
|
||||||
|
|
||||||
|
await n8n.ndv.getCodeEditor().fill(`$input.itemMatching()
|
||||||
|
$input.all()
|
||||||
|
$input.first()
|
||||||
|
$input.item()
|
||||||
|
|
||||||
|
return []
|
||||||
|
`);
|
||||||
|
await expect(n8n.ndv.getLintErrors()).toHaveCount(5);
|
||||||
|
await n8n.ndv.getParameterInput('jsCode').getByText('all').hover();
|
||||||
|
await expect(n8n.ndv.getLintTooltip()).toContainText(
|
||||||
|
"Method `$input.all()` is only available in the 'Run Once for All Items' mode.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Ask AI', () => {
|
||||||
|
test.describe('Enabled', () => {
|
||||||
|
test.beforeEach(async ({ api, n8n }) => {
|
||||||
|
await api.enableFeature('askAi');
|
||||||
|
await n8n.goHome();
|
||||||
|
await n8n.workflows.clickAddWorkflowButton();
|
||||||
|
await n8n.canvas.addNode(MANUAL_TRIGGER_NODE_NAME);
|
||||||
|
await n8n.canvas.addNode(CODE_NODE_NAME);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tab should exist if experiment selected and be selectable', async ({ n8n }) => {
|
||||||
|
await n8n.ndv.clickAskAiTab();
|
||||||
|
await expect(n8n.ndv.getAskAiTabPanel()).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getHeyAiText()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('generate code button should have correct state & tooltips', async ({ n8n }) => {
|
||||||
|
await n8n.ndv.clickAskAiTab();
|
||||||
|
await expect(n8n.ndv.getAskAiTabPanel()).toBeVisible();
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getAskAiCtaButton()).toBeDisabled();
|
||||||
|
await n8n.ndv.getAskAiCtaButton().hover();
|
||||||
|
await expect(n8n.ndv.getAskAiCtaTooltipNoInputData()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.executePrevious();
|
||||||
|
await n8n.ndv.getAskAiCtaButton().hover();
|
||||||
|
await expect(n8n.ndv.getAskAiCtaTooltipNoPrompt()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.getAskAiPromptInput().fill(nanoid(14));
|
||||||
|
|
||||||
|
await n8n.ndv.getAskAiCtaButton().hover();
|
||||||
|
await expect(n8n.ndv.getAskAiCtaTooltipPromptTooShort()).toBeVisible();
|
||||||
|
|
||||||
|
await n8n.ndv.getAskAiPromptInput().fill(nanoid(15));
|
||||||
|
await expect(n8n.ndv.getAskAiCtaButton()).toBeEnabled();
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getAskAiPromptCounter()).toContainText('15 / 600');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should send correct schema and replace code', async ({ n8n }) => {
|
||||||
|
const prompt = nanoid(20);
|
||||||
|
await n8n.ndv.clickAskAiTab();
|
||||||
|
await n8n.ndv.executePrevious();
|
||||||
|
|
||||||
|
await n8n.ndv.getAskAiPromptInput().fill(prompt);
|
||||||
|
|
||||||
|
await n8n.page.route('**/rest/ai/ask-ai', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
data: {
|
||||||
|
code: 'console.log("Hello World")',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const [request] = await Promise.all([
|
||||||
|
n8n.page.waitForRequest('**/rest/ai/ask-ai'),
|
||||||
|
n8n.ndv.getAskAiCtaButton().click(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const requestBody = request.postDataJSON();
|
||||||
|
expect(requestBody).toHaveProperty('question');
|
||||||
|
expect(requestBody).toHaveProperty('context');
|
||||||
|
expect(requestBody).toHaveProperty('forNode');
|
||||||
|
expect(requestBody.context).toHaveProperty('schema');
|
||||||
|
expect(requestBody.context).toHaveProperty('ndvPushRef');
|
||||||
|
expect(requestBody.context).toHaveProperty('pushRef');
|
||||||
|
expect(requestBody.context).toHaveProperty('inputSchema');
|
||||||
|
|
||||||
|
await expect(n8n.ndv.getCodeGenerationCompletedText()).toBeVisible();
|
||||||
|
await expect(n8n.ndv.getCodeTabPanel()).toContainText('console.log("Hello World")');
|
||||||
|
await expect(n8n.ndv.getCodeTab()).toHaveClass(/is-active/);
|
||||||
|
});
|
||||||
|
|
||||||
|
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 with error: Request failed with status code 500. Try again in a few minutes',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
handledCodes.forEach(({ code, message }) => {
|
||||||
|
test(`should show error based on status code ${code}`, async ({ n8n }) => {
|
||||||
|
const prompt = nanoid(20);
|
||||||
|
await n8n.ndv.clickAskAiTab();
|
||||||
|
await n8n.ndv.executePrevious();
|
||||||
|
|
||||||
|
await n8n.ndv.getAskAiPromptInput().fill(prompt);
|
||||||
|
|
||||||
|
await n8n.page.route('**/rest/ai/ask-ai', async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: code,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await n8n.ndv.getAskAiCtaButton().click();
|
||||||
|
await expect(n8n.ndv.getErrorMessageText(message)).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user