import { test, expect } from '../../../fixtures/base'; import type { TestRequirements } from '../../../Types'; const requirements: TestRequirements = { config: { settings: { previewMode: true, }, }, }; // Simple workflow structures for testing const oldWorkflow = { id: 'old-workflow-id', name: 'Test Workflow - Before', nodes: [ { parameters: {}, id: '1', name: 'Manual Trigger', type: 'n8n-nodes-base.manualTrigger', typeVersion: 1, position: [250, 300], }, { parameters: {}, id: '2', name: 'Set', type: 'n8n-nodes-base.set', typeVersion: 1, position: [450, 300], }, ], connections: { 'Manual Trigger': { main: [ [ { node: 'Set', type: 'main', index: 0, }, ], ], }, }, }; const newWorkflow = { id: 'new-workflow-id', name: 'Test Workflow - After', nodes: [ { parameters: {}, id: '1', name: 'Manual Trigger', type: 'n8n-nodes-base.manualTrigger', typeVersion: 1, position: [250, 300], }, { parameters: { options: { keepOnlySet: true, }, }, id: '2', name: 'Set', type: 'n8n-nodes-base.set', typeVersion: 1, position: [450, 300], }, { parameters: {}, id: '3', name: 'Code', type: 'n8n-nodes-base.code', typeVersion: 1, position: [650, 300], }, ], connections: { 'Manual Trigger': { main: [ [ { node: 'Set', type: 'main', index: 0, }, ], ], }, Set: { main: [ [ { node: 'Code', type: 'main', index: 0, }, ], ], }, }, }; test.describe('Workflow Diff Demo', { annotation: [ { type: 'owner', description: 'Adore' }, ], }, () => { test.beforeEach(async ({ setupRequirements }) => { await setupRequirements(requirements); }); test('shows waiting state initially', async ({ n8n }) => { await n8n.page.goto('/workflows/demo/diff'); // Verify the waiting state message is shown await expect(n8n.page.getByText('Waiting for workflow data...')).toBeVisible(); }); test('renders diff view when receiving workflow data via postMessage', async ({ n8n }) => { await n8n.page.goto('/workflows/demo/diff'); // Verify the waiting state is initially shown await expect(n8n.page.getByText('Waiting for workflow data...')).toBeVisible(); // Send postMessage with workflow data await n8n.page.evaluate( ({ oldWf, newWf }) => { window.postMessage( JSON.stringify({ command: 'openDiff', oldWorkflow: oldWf, newWorkflow: newWf, }), '*', ); }, { oldWf: oldWorkflow, newWf: newWorkflow }, ); // Wait for the diff view to appear - the waiting message should disappear await expect(n8n.page.getByText('Waiting for workflow data...')).toBeHidden(); // The workflow name should appear in the header await expect( n8n.page.getByRole('heading', { name: /Test Workflow/, exact: false }), ).toBeVisible(); // The Changes button should be visible (part of the diff view header) await expect(n8n.page.getByRole('button', { name: /Changes/ })).toBeVisible(); }); test('renders diff view with only new workflow (creation scenario)', async ({ n8n }) => { await n8n.page.goto('/workflows/demo/diff'); // Wait for the page to be ready (component mounted with event listener active) await expect(n8n.page.getByText('Waiting for workflow data...')).toBeVisible(); // Send postMessage with only newWorkflow (simulating workflow creation) await n8n.page.evaluate( ({ newWf }) => { window.postMessage( JSON.stringify({ command: 'openDiff', newWorkflow: newWf, }), '*', ); }, { newWf: newWorkflow }, ); // Wait for the diff view to appear await expect(n8n.page.getByText('Waiting for workflow data...')).toBeHidden(); // The workflow name from newWorkflow should appear await expect(n8n.page.getByRole('heading', { name: 'Test Workflow - After' })).toBeVisible(); }); test('renders diff view with only old workflow (deletion scenario)', async ({ n8n }) => { await n8n.page.goto('/workflows/demo/diff'); // Wait for the page to be ready (component mounted with event listener active) await expect(n8n.page.getByText('Waiting for workflow data...')).toBeVisible(); // Send postMessage with only oldWorkflow (simulating workflow deletion) await n8n.page.evaluate( ({ oldWf }) => { window.postMessage( JSON.stringify({ command: 'openDiff', oldWorkflow: oldWf, }), '*', ); }, { oldWf: oldWorkflow }, ); // Wait for the diff view to appear await expect(n8n.page.getByText('Waiting for workflow data...')).toBeHidden(); // The workflow name from oldWorkflow should appear await expect(n8n.page.getByRole('heading', { name: 'Test Workflow - Before' })).toBeVisible(); }); test('applies tidy up when tidyUp option is true', async ({ n8n }) => { await n8n.page.goto('/workflows/demo/diff'); // Wait for the page to be ready (component mounted with event listener active) await expect(n8n.page.getByText('Waiting for workflow data...')).toBeVisible(); // Send postMessage with tidyUp: true await n8n.page.evaluate( ({ oldWf, newWf }) => { window.postMessage( JSON.stringify({ command: 'openDiff', oldWorkflow: oldWf, newWorkflow: newWf, tidyUp: true, }), '*', ); }, { oldWf: oldWorkflow, newWf: newWorkflow }, ); // Wait for the diff view to appear await expect(n8n.page.getByText('Waiting for workflow data...')).toBeHidden(); // The diff view should render (tidyUp affects node positioning but view should still render) await expect( n8n.page.getByRole('heading', { name: /Test Workflow/, exact: false }), ).toBeVisible(); // The Changes button should be visible (part of the diff view header) await expect(n8n.page.getByRole('button', { name: /Changes/ })).toBeVisible(); }); test('ignores malformed postMessage data', async ({ n8n }) => { await n8n.page.goto('/workflows/demo/diff'); // Verify initial waiting state await expect(n8n.page.getByText('Waiting for workflow data...')).toBeVisible(); // Send malformed message await n8n.page.evaluate(() => { window.postMessage('not valid json {{{', '*'); }); // Waiting state should still be shown await expect(n8n.page.getByText('Waiting for workflow data...')).toBeVisible(); }); test('ignores non-openDiff commands', async ({ n8n }) => { await n8n.page.goto('/workflows/demo/diff'); // Verify initial waiting state await expect(n8n.page.getByText('Waiting for workflow data...')).toBeVisible(); // Send a different command await n8n.page.evaluate(() => { window.postMessage( JSON.stringify({ command: 'someOtherCommand', data: {}, }), '*', ); }); // Waiting state should still be shown await expect(n8n.page.getByText('Waiting for workflow data...')).toBeVisible(); }); test('can override theme to dark', async ({ n8n }) => { await n8n.page.goto('/workflows/demo/diff?theme=dark'); await expect(n8n.page.locator('body')).toHaveAttribute('data-theme', 'dark'); }); test('can override theme to light', async ({ n8n }) => { await n8n.page.goto('/workflows/demo/diff?theme=light'); await expect(n8n.page.locator('body')).toHaveAttribute('data-theme', 'light'); }); });