import { nanoid } from 'nanoid'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { NDV } from '../pages/ndv'; import { successToast } from '../pages/notifications'; const WorkflowPage = new WorkflowPageClass(); const ndv = new NDV(); 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'); }); }); describe('Ask AI', () => { it('tab should display based on experiment', () => { WorkflowPage.actions.visit(); cy.window().then((win) => { win.featureFlags.override('011_ask_AI', 'control'); WorkflowPage.actions.addInitialNodeToCanvas('Manual'); WorkflowPage.actions.addNodeToCanvas('Code'); WorkflowPage.actions.openNode('Code'); cy.getByTestId('code-node-tab-ai').should('not.exist'); ndv.actions.close(); win.featureFlags.override('011_ask_AI', undefined); WorkflowPage.actions.openNode('Code'); cy.getByTestId('code-node-tab-ai').should('not.exist'); }); }); describe('Enabled', () => { beforeEach(() => { WorkflowPage.actions.visit(); cy.window().then((win) => { 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'); }); 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/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', 'model', 'context', 'n8nVersion']); askAiReq.its('context').should('have.keys', ['schema', 'ndvPushRef', 'pushRef']); 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 = nanoid(20); cy.get('#tab-ask-ai').click(); ndv.actions.executePrevious(); 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' }, ]; 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'); }); }); }); }); });