import { test, expect } from '../../fixtures/base'; test.describe('Data Mapping', () => { test.describe .serial('Expression Preview', () => { test('maps expressions from table json, and resolves value based on hover', async ({ n8n, }) => { // This test is marked as serial because hover/tooltips are unreliable when running in parallel against a single server due to resource contention. await n8n.start.fromImportedWorkflow('Test_workflow_3.json'); await n8n.canvas.openNode('Set'); await n8n.ndv.inputPanel.switchDisplayMode('table'); await expect(n8n.ndv.inputPanel.getTable()).toBeVisible(); await expect(n8n.ndv.getParameterInputField('name')).toHaveValue('other'); await expect(n8n.ndv.getParameterInputField('value')).toHaveValue(''); const countCell = n8n.ndv.inputPanel.getTableCellSpan(0, 0, 'count'); await expect(countCell).toBeVisible(); const valueParameter = n8n.ndv.getParameterInput('value'); await n8n.interactions.precisionDragToTarget(countCell, valueParameter, 'bottom'); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText( '{{ $json.input[0].count }}', ); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('0'); await n8n.ndv.inputPanel.getTbodyCell(0, 0).hover(); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('0'); await n8n.ndv.inputPanel.getTbodyCell(1, 0).hover(); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('1'); await n8n.ndv.execute(); await expect(n8n.ndv.outputPanel.getTable()).toBeVisible(); await n8n.ndv.outputPanel.getTbodyCell(0, 0).hover(); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('0'); await n8n.ndv.outputPanel.getTbodyCell(1, 0).hover(); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('1'); }); }); test('maps expressions from json view', async ({ n8n }) => { await n8n.start.fromImportedWorkflow('Test_workflow_3.json'); await n8n.canvas.openNode('Set'); await n8n.ndv.inputPanel.switchDisplayMode('json'); const expectedJsonText = '[{"input": [{"count": 0,"with space": "!!","with.dot": "!!","with"quotes": "!!"}]},{"input": [{"count": 1}]}]'; await expect(n8n.ndv.inputPanel.get().getByText(expectedJsonText)).toBeVisible(); await expect(n8n.ndv.inputPanel.getJsonDataContainer()).toBeVisible(); const inputSpan = n8n.ndv.inputPanel.getJsonProperty('input'); await expect(inputSpan).toBeVisible(); const valueParameterInput = n8n.ndv.getParameterInput('value'); await expect(valueParameterInput).toBeVisible(); await inputSpan.dragTo(valueParameterInput); const expressionEditor = n8n.ndv.getInlineExpressionEditorInput(); await expect(expressionEditor).toBeVisible(); await expect(expressionEditor).toHaveText('{{ $json.input }}'); await n8n.page.keyboard.press('Escape'); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('Array:'); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('"count": 0'); const countSpan = n8n.ndv.inputPanel.getJsonPropertyContaining('count'); await expect(countSpan).toBeVisible(); await n8n.interactions.precisionDragToTarget( countSpan, n8n.ndv.getInlineExpressionEditorInput(), 'bottom', ); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText( '{{ $json.input }}{{ $json.input[0].count }}', ); await n8n.page.keyboard.press('Escape'); const previewElement = n8n.ndv.getParameterExpressionPreviewValue(); await expect(previewElement).toBeVisible(); }); test('maps expressions from previous nodes', async ({ n8n }) => { await n8n.start.fromImportedWorkflow('Test_workflow_3.json'); await n8n.canvas.openNode('Set1'); await n8n.ndv.executePrevious(); const scheduleNode = n8n.ndv.inputPanel.get().getByText('Schedule Trigger'); await expect(scheduleNode).toBeVisible(); await scheduleNode.click(); const schemaItem = n8n.ndv.inputPanel.getSchemaItemText('count'); await expect(schemaItem).toBeVisible(); const valueParameterInput = n8n.ndv.getParameterInput('value'); await expect(valueParameterInput).toBeVisible(); await n8n.interactions.precisionDragToTarget( schemaItem.locator('span'), valueParameterInput, 'top', ); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText( "{{ $('Schedule Trigger').item.json.input[0].count }}", ); await n8n.page.keyboard.press('Escape'); await n8n.ndv.inputPanel.switchDisplayMode('table'); await n8n.ndv.selectInputNode('Schedule Trigger'); const headerElement = n8n.ndv.inputPanel.getTableHeader(0); await expect(headerElement).toBeVisible(); await n8n.interactions.precisionDragToTarget( headerElement, n8n.ndv.getInlineExpressionEditorInput(), 'top', ); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText( "{{ $('Schedule Trigger').item.json.input }}{{ $('Schedule Trigger').item.json.input[0].count }}", ); await n8n.ndv.selectInputNode('Set'); }); test('maps expressions from table header', async ({ n8n }) => { await n8n.start.fromImportedWorkflow('Test_workflow-actions_paste-data.json'); await n8n.canvas.openNode('Set'); await n8n.ndv.executePrevious(); await n8n.ndv.inputPanel.switchDisplayMode('table'); await expect(n8n.ndv.inputPanel.getTable()).toBeVisible(); const addValueButton = n8n.ndv.getAddValueButton(); await expect(addValueButton).toBeVisible(); await addValueButton.click(); await n8n.page.getByRole('option', { name: 'String' }).click(); await expect(n8n.ndv.getParameterInputField('name')).toHaveValue('propertyName'); await expect(n8n.ndv.getParameterInputField('value')).toHaveValue(''); const firstHeader = n8n.ndv.inputPanel.getTableHeader(0); await expect(firstHeader).toBeVisible(); const valueParameter = n8n.ndv.getParameterInput('value'); await n8n.interactions.precisionDragToTarget(firstHeader, valueParameter, 'bottom'); await expect(n8n.ndv.getInlineExpressionEditorInput()).toBeVisible(); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText('{{ $json.timestamp }}'); await n8n.page.keyboard.press('Escape'); const currentYear = new Date().getFullYear().toString(); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText(currentYear); const secondHeader = n8n.ndv.inputPanel.getTableHeader(1); await expect(secondHeader).toBeVisible(); await n8n.interactions.precisionDragToTarget( secondHeader, n8n.ndv.getInlineExpressionEditorInput(), 'top', ); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText( "{{ $json['Readable date'] }}{{ $json.timestamp }}", ); }); test('maps expressions from schema view', async ({ n8n }) => { await n8n.start.fromImportedWorkflow('Test_workflow_3.json'); await n8n.canvas.openNode('Set'); await n8n.ndv.getParameterInputField('value').clear(); await n8n.page.keyboard.press('Escape'); const countSchemaItem = n8n.ndv.inputPanel.getSchemaItemText('count'); await expect(countSchemaItem).toBeVisible(); const valueParameter = n8n.ndv.getParameterInput('value'); await n8n.interactions.precisionDragToTarget(countSchemaItem, valueParameter, 'bottom'); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText('{{ $json.input[0].count }}'); await n8n.page.keyboard.press('Escape'); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('0'); const inputSchemaItem = n8n.ndv.inputPanel.getSchemaItemText('input'); await expect(inputSchemaItem).toBeVisible(); await n8n.interactions.precisionDragToTarget(inputSchemaItem, valueParameter, 'top'); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText( '{{ $json.input }}{{ $json.input[0].count }}', ); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('[object Object]0'); }); test('maps keys to path', async ({ n8n }) => { await n8n.start.fromBlankCanvas(); await n8n.canvas.addNode('Manual Trigger'); await n8n.canvas.openNode('When clicking ‘Execute workflow’'); await n8n.ndv.setPinnedData([ { input: [ { 'hello.world': { 'my count': 0, }, }, ], }, { input: [ { 'hello.world': { 'my count': 1, }, }, ], }, ]); await n8n.ndv.close(); await n8n.canvas.addNode('Sort'); const addFieldButton = n8n.ndv.getAddFieldToSortByButton(); await addFieldButton.click(); const myCountSpan = n8n.ndv.inputPanel.getSchemaItemText('my count'); await expect(myCountSpan).toBeVisible(); const fieldNameParameter = n8n.ndv.getParameterInput('fieldName'); await n8n.interactions.precisionDragToTarget(myCountSpan, fieldNameParameter, 'bottom'); await expect(n8n.ndv.getInlineExpressionEditorInput()).toBeHidden(); await expect(n8n.ndv.getParameterInputField('fieldName')).toHaveValue( "input[0]['hello.world']['my count']", ); }); test('maps expressions to updated fields correctly', async ({ n8n }) => { await n8n.start.fromImportedWorkflow('Test_workflow_3.json'); await n8n.canvas.openNode('Set'); await n8n.ndv.fillParameterInputByName('value', 'delete me'); await n8n.ndv.fillParameterInputByName('name', 'test'); await n8n.ndv.getParameterInputField('name').blur(); await n8n.ndv.fillParameterInputByName('value', 'fun'); await n8n.ndv.getParameterInputField('value').clear(); const countSchemaItem = n8n.ndv.inputPanel.getSchemaItemText('count'); await expect(countSchemaItem).toBeVisible(); const valueParameter = n8n.ndv.getParameterInput('value'); await n8n.interactions.precisionDragToTarget(countSchemaItem, valueParameter, 'bottom'); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText('{{ $json.input[0].count }}'); await n8n.page.keyboard.press('Escape'); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('0'); const inputSchemaItem = n8n.ndv.inputPanel.getSchemaItemText('input'); await expect(inputSchemaItem).toBeVisible(); await n8n.interactions.precisionDragToTarget(inputSchemaItem, valueParameter, 'top'); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText( '{{ $json.input }}{{ $json.input[0].count }}', ); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('[object Object]0'); }); test('renders expression preview when a previous node is selected', async ({ n8n }) => { await n8n.start.fromImportedWorkflow('Test_workflow_3.json'); await n8n.canvas.openNode('Set'); await n8n.ndv.fillParameterInputByName('value', 'test_value'); await n8n.ndv.fillParameterInputByName('name', 'test_name'); await n8n.ndv.close(); await n8n.canvas.openNode('Set1'); await n8n.ndv.executePrevious(); await n8n.ndv.inputPanel.switchDisplayMode('table'); const firstHeader = n8n.ndv.inputPanel.getTableHeader(0); await expect(firstHeader).toBeVisible(); const valueParameter = n8n.ndv.getParameterInput('value'); await n8n.interactions.precisionDragToTarget(firstHeader, valueParameter, 'bottom'); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('test_value'); await n8n.ndv.selectInputNode('Schedule Trigger'); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText('test_value'); }); test('shows you can drop to inputs, including booleans', async ({ n8n }) => { await n8n.start.fromImportedWorkflow('Test_workflow_3.json'); await n8n.canvas.openNode('Set'); await expect(n8n.ndv.getParameterSwitch('includeOtherFields')).toBeVisible(); await expect(n8n.ndv.getParameterTextInput('includeOtherFields')).toBeHidden(); const countSpan = n8n.ndv.inputPanel.getSchemaItemText('count'); await expect(countSpan).toBeVisible(); await countSpan.hover(); await n8n.page.mouse.down(); await n8n.page.mouse.move(100, 100); await expect(n8n.ndv.getParameterSwitch('includeOtherFields')).toBeHidden(); await expect(n8n.ndv.getParameterTextInput('includeOtherFields')).toBeVisible(); const includeOtherFieldsInput = n8n.ndv.getParameterTextInput('includeOtherFields'); await expect(includeOtherFieldsInput).toHaveCSS('border', /dashed.*rgb\(90, 76, 194\)/); const valueInput = n8n.ndv.getParameterTextInput('value'); await expect(valueInput).toHaveCSS('border', /dashed.*rgb\(90, 76, 194\)/); await n8n.page.mouse.up(); }); test('maps expressions to a specific location in the editor', async ({ n8n }) => { await n8n.start.fromImportedWorkflow('Test_workflow_3.json'); await n8n.canvas.openNode('Set'); await n8n.ndv.fillParameterInputByName('value', '='); await n8n.ndv.getInlineExpressionEditorContent().fill('hello world\n\nnewline'); await n8n.page.keyboard.press('Escape'); const countSchemaItem = n8n.ndv.inputPanel.getSchemaItemText('count'); await expect(countSchemaItem).toBeVisible(); const valueParameter = n8n.ndv.getParameterInput('value'); await n8n.interactions.precisionDragToTarget(countSchemaItem, valueParameter, 'top'); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText( '{{ $json.input[0].count }}hello worldnewline', ); await n8n.page.keyboard.press('Escape'); await expect(n8n.ndv.getParameterExpressionPreviewValue()).toContainText( '0hello world\n\nnewline', ); const inputSchemaItem = n8n.ndv.inputPanel.getSchemaItemText('input'); await expect(inputSchemaItem).toBeVisible(); await n8n.interactions.precisionDragToTarget(inputSchemaItem, valueParameter, 'center'); await expect(n8n.ndv.getInlineExpressionEditorInput()).toHaveText( '{{ $json.input[0].count }}hello world{{ $json.input }}newline', ); }); });