fix(editor): Improve formatting of expired trial error message (#11708)

This commit is contained in:
Ivan Atanasov 2024-11-14 12:19:12 +01:00 committed by GitHub
parent a91abeeff5
commit 8a0ad0f910
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 168 additions and 3 deletions

4
.vscode/launch.json vendored
View file

@ -47,9 +47,7 @@
"request": "launch",
"skipFiles": ["<node_internals>/**"],
"type": "node",
"env": {
// "N8N_PORT": "5679",
},
"envFile": "${workspaceFolder}/.env",
"outputCapture": "std",
"killBehavior": "polite"
},

View file

@ -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<typeof usePushConnectionStore>;
let orchestrationStore: ReturnType<typeof useOrchestrationStore>;
let pushConnection: ReturnType<typeof usePushConnection>;
let uiStore: ReturnType<typeof useUIStore>;
let workflowsStore: ReturnType<typeof useWorkflowsStore>;
let toast: ReturnType<typeof useToast>;
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. <a href="https://app.n8n.cloud/account/change-plan">Upgrade now</a> 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. <a href="https://app.n8n.cloud/account/change-plan">Upgrade now</a> 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();
});
});
});
});

View file

@ -423,6 +423,7 @@ export function usePushConnection({ router }: { router: ReturnType<typeof useRou
message: runDataExecutedErrorMessage,
type: 'error',
duration: 0,
dangerouslyUseHTMLString: true,
});
}
}

View file

@ -0,0 +1,55 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { useToast } from './useToast';
import { createPinia, setActivePinia } from 'pinia';
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<typeof useToast>;
beforeEach(() => {
setActivePinia(createPinia());
toast = useToast();
});
afterEach(() => {
vi.restoreAllMocks();
});
it('should show a message', () => {
const messageData = { message: 'Test message', title: 'Test title' };
toast.showMessage(messageData);
expect(Notification).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Test message',
title: 'Test title',
}),
);
});
it('should sanitize message and title', () => {
const messageData = {
message: '<script>alert("xss")</script>',
title: '<script>alert("xss")</script>',
};
toast.showMessage(messageData);
expect(Notification).toHaveBeenCalledWith(
expect.objectContaining({
message: 'alert("xss")',
title: 'alert("xss")',
}),
);
});
});