From fa8f7bd8886c5d3d47fea9fc605e238eae44f508 Mon Sep 17 00:00:00 2001 From: Ivan Atanasov Date: Tue, 12 Nov 2024 20:57:17 +0100 Subject: [PATCH 1/3] improve formatting of expired trial error message --- packages/editor-ui/src/composables/usePushConnection.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/editor-ui/src/composables/usePushConnection.ts b/packages/editor-ui/src/composables/usePushConnection.ts index ea9f79d27ecef..a5976d35d48bf 100644 --- a/packages/editor-ui/src/composables/usePushConnection.ts +++ b/packages/editor-ui/src/composables/usePushConnection.ts @@ -423,6 +423,7 @@ export function usePushConnection({ router }: { router: ReturnType Date: Wed, 13 Nov 2024 16:22:24 +0100 Subject: [PATCH 2/3] adding tests for error message --- .vscode/launch.json | 4 +- .../src/composables/usePushConnection.test.ts | 111 ++++++++++++++++++ .../src/composables/useToast.test.ts | 37 ++++++ 3 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 packages/editor-ui/src/composables/useToast.test.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 448d745236cbc..5501fe4439170 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -47,9 +47,7 @@ "request": "launch", "skipFiles": ["/**"], "type": "node", - "env": { - // "N8N_PORT": "5679", - }, + "envFile": "${workspaceFolder}/.env", "outputCapture": "std", "killBehavior": "polite" }, diff --git a/packages/editor-ui/src/composables/usePushConnection.test.ts b/packages/editor-ui/src/composables/usePushConnection.test.ts index c21fc767026a4..819b9e842e9a7 100644 --- a/packages/editor-ui/src/composables/usePushConnection.test.ts +++ b/packages/editor-ui/src/composables/usePushConnection.test.ts @@ -5,6 +5,10 @@ import type { PushMessage, PushPayload } from '@n8n/api-types'; import { usePushConnection } from '@/composables/usePushConnection'; import { usePushConnectionStore } from '@/stores/pushConnection.store'; import { useOrchestrationStore } from '@/stores/orchestration.store'; +import { useUIStore } from '@/stores/ui.store'; +import { useWorkflowsStore } from '@/stores/workflows.store'; +import { useToast } from '@/composables/useToast'; +import type { WorkflowOperationError } from 'n8n-workflow'; vi.mock('vue-router', () => { return { @@ -16,6 +20,19 @@ vi.mock('vue-router', () => { }; }); +vi.mock('@/composables/useToast', () => { + const showMessage = vi.fn(); + const showError = vi.fn(); + return { + useToast: () => { + return { + showMessage, + showError, + }; + }, + }; +}); + vi.useFakeTimers(); describe('usePushConnection()', () => { @@ -23,6 +40,9 @@ describe('usePushConnection()', () => { let pushStore: ReturnType; let orchestrationStore: ReturnType; let pushConnection: ReturnType; + let uiStore: ReturnType; + let workflowsStore: ReturnType; + let toast: ReturnType; beforeEach(() => { setActivePinia(createPinia()); @@ -30,7 +50,14 @@ describe('usePushConnection()', () => { router = vi.mocked(useRouter)(); pushStore = usePushConnectionStore(); orchestrationStore = useOrchestrationStore(); + uiStore = useUIStore(); + workflowsStore = useWorkflowsStore(); pushConnection = usePushConnection({ router }); + toast = useToast(); + }); + + afterEach(() => { + vi.restoreAllMocks(); }); describe('initialize()', () => { @@ -106,5 +133,89 @@ describe('usePushConnection()', () => { expect(result).toBeTruthy(); }); }); + + describe('executionFinished', () => { + it('should handle executionFinished event correctly', async () => { + const event: PushMessage = { + type: 'executionFinished', + data: { + executionId: '1', + data: { + data: { + resultData: { + runData: {}, + }, + }, + finished: true, + mode: 'manual', + startedAt: new Date(), + stoppedAt: new Date(), + status: 'success', + }, + }, + }; + + workflowsStore.activeExecutionId = '1'; + uiStore.isActionActive.workflowRunning = true; + + const result = await pushConnection.pushMessageReceived(event); + + expect(result).toBeTruthy(); + expect(workflowsStore.workflowExecutionData).toBeDefined(); + expect(uiStore.isActionActive['workflowRunning']).toBeTruthy(); + + expect(toast.showMessage).toHaveBeenCalledWith({ + title: 'Workflow executed successfully', + type: 'success', + }); + }); + + it('should handle isManualExecutionCancelled correctly', async () => { + const event: PushMessage = { + type: 'executionFinished', + data: { + executionId: '1', + data: { + data: { + startData: {}, + resultData: { + runData: { + 'Last Node': [], + }, + lastNodeExecuted: 'Last Node', + error: { + message: + 'Your trial has ended. Upgrade now to keep automating', + name: 'NodeApiError', + node: 'Last Node', + } as unknown as WorkflowOperationError, + }, + }, + startedAt: new Date(), + mode: 'manual', + status: 'running', + }, + }, + }; + + workflowsStore.activeExecutionId = '1'; + uiStore.isActionActive['workflowRunning'] = true; + + const result = await pushConnection.pushMessageReceived(event); + + expect(useToast().showMessage).toHaveBeenCalledWith({ + message: + 'Your trial has ended. Upgrade now to keep automating', + title: 'Problem in node ‘Last Node‘', + type: 'error', + duration: 0, + dangerouslyUseHTMLString: true, + }); + + expect(result).toBeTruthy(); + expect(workflowsStore.workflowExecutionData).toBeDefined(); + expect(uiStore.isActionActive.workflowRunning).toBeTruthy(); + }); + }); }); }); diff --git a/packages/editor-ui/src/composables/useToast.test.ts b/packages/editor-ui/src/composables/useToast.test.ts new file mode 100644 index 0000000000000..cf6ceb0567b30 --- /dev/null +++ b/packages/editor-ui/src/composables/useToast.test.ts @@ -0,0 +1,37 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { useToast } from './useToast'; +import { createPinia, setActivePinia } from 'pinia'; +import { sanitizeHtml } from '@/utils/htmlUtils'; + +vi.mock('@/utils/htmlUtils', () => ({ + sanitizeHtml: vi.fn((str) => str), +})); + +describe('useToast', () => { + let toast: ReturnType; + + beforeEach(() => { + setActivePinia(createPinia()); + + toast = useToast(); + }); + + it('should show a message', () => { + const messageData = { message: 'Test message', title: 'Test title' }; + const notification = toast.showMessage(messageData); + + expect(notification).toBeDefined(); + }); + + it('should sanitize message and title', () => { + const messageData = { + message: '', + title: '', + }; + + toast.showMessage(messageData); + + expect(sanitizeHtml).toHaveBeenCalledWith(messageData.message); + expect(sanitizeHtml).toHaveBeenCalledWith(messageData.title); + }); +}); From fe7f88745d9b59478f5a26f87ad2eadec37da298 Mon Sep 17 00:00:00 2001 From: Ivan Atanasov Date: Wed, 13 Nov 2024 23:16:34 +0100 Subject: [PATCH 3/3] add sanitize tests --- .../src/composables/useToast.test.ts | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/packages/editor-ui/src/composables/useToast.test.ts b/packages/editor-ui/src/composables/useToast.test.ts index cf6ceb0567b30..f476a29d70cd8 100644 --- a/packages/editor-ui/src/composables/useToast.test.ts +++ b/packages/editor-ui/src/composables/useToast.test.ts @@ -1,11 +1,16 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { useToast } from './useToast'; import { createPinia, setActivePinia } from 'pinia'; -import { sanitizeHtml } from '@/utils/htmlUtils'; - -vi.mock('@/utils/htmlUtils', () => ({ - sanitizeHtml: vi.fn((str) => str), -})); +import { ElNotification as Notification } from 'element-plus'; + +vi.mock('element-plus', async () => { + const original = await vi.importActual('element-plus'); + return { + ...original, + ElNotification: vi.fn(), + ElTooltip: vi.fn(), + }; +}); describe('useToast', () => { let toast: ReturnType; @@ -16,11 +21,20 @@ describe('useToast', () => { toast = useToast(); }); + afterEach(() => { + vi.restoreAllMocks(); + }); + it('should show a message', () => { const messageData = { message: 'Test message', title: 'Test title' }; - const notification = toast.showMessage(messageData); + toast.showMessage(messageData); - expect(notification).toBeDefined(); + expect(Notification).toHaveBeenCalledWith( + expect.objectContaining({ + message: 'Test message', + title: 'Test title', + }), + ); }); it('should sanitize message and title', () => { @@ -31,7 +45,11 @@ describe('useToast', () => { toast.showMessage(messageData); - expect(sanitizeHtml).toHaveBeenCalledWith(messageData.message); - expect(sanitizeHtml).toHaveBeenCalledWith(messageData.title); + expect(Notification).toHaveBeenCalledWith( + expect.objectContaining({ + message: 'alert("xss")', + title: 'alert("xss")', + }), + ); }); });