mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-20 11:22:15 +00:00
230 lines
8.5 KiB
TypeScript
230 lines
8.5 KiB
TypeScript
import userEvent from '@testing-library/user-event';
|
||
import { createTestingPinia } from '@pinia/testing';
|
||
import { waitFor, screen } from '@testing-library/vue';
|
||
import { createComponentRenderer } from '@/__tests__/render';
|
||
import {
|
||
cleanupAppModals,
|
||
createAppModals,
|
||
mockedStore,
|
||
type MockedStore,
|
||
} from '@/__tests__/utils';
|
||
import { useUIStore } from '@/stores/ui.store';
|
||
import { WHATS_NEW_MODAL_KEY, VERSIONS_MODAL_KEY } from '@/constants';
|
||
import { useVersionsStore } from '@/stores/versions.store';
|
||
import type { Version } from '@n8n/rest-api-client/api/versions';
|
||
|
||
import WhatsNewModal from './WhatsNewModal.vue';
|
||
import { useTelemetry } from '@/composables/useTelemetry';
|
||
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
|
||
|
||
vi.mock('@/composables/usePageRedirectionHelper', () => {
|
||
const goToVersions = vi.fn();
|
||
return {
|
||
usePageRedirectionHelper: vi.fn().mockReturnValue({
|
||
goToVersions,
|
||
}),
|
||
};
|
||
});
|
||
|
||
vi.mock('@/composables/useTelemetry', () => {
|
||
const track = vi.fn();
|
||
return {
|
||
useTelemetry: () => {
|
||
return {
|
||
track,
|
||
};
|
||
},
|
||
};
|
||
});
|
||
|
||
const renderComponent = createComponentRenderer(WhatsNewModal, {
|
||
props: {
|
||
modalName: WHATS_NEW_MODAL_KEY,
|
||
},
|
||
});
|
||
|
||
let uiStore: MockedStore<typeof useUIStore>;
|
||
let versionsStore: MockedStore<typeof useVersionsStore>;
|
||
|
||
const telemetry = useTelemetry();
|
||
const pageRedirectionHelper = usePageRedirectionHelper();
|
||
|
||
const currentVersion: Version = {
|
||
name: '1.100.0',
|
||
nodes: [],
|
||
createdAt: '2025-06-24T00:00:00Z',
|
||
description: 'Latest version description',
|
||
documentationUrl: 'https://docs.n8n.io',
|
||
hasBreakingChange: false,
|
||
hasSecurityFix: false,
|
||
hasSecurityIssue: false,
|
||
securityIssueFixVersion: '',
|
||
};
|
||
|
||
describe('WhatsNewModal', () => {
|
||
beforeEach(() => {
|
||
createAppModals();
|
||
createTestingPinia();
|
||
uiStore = mockedStore(useUIStore);
|
||
uiStore.modalsById = {
|
||
[WHATS_NEW_MODAL_KEY]: {
|
||
open: true,
|
||
},
|
||
};
|
||
|
||
versionsStore = mockedStore(useVersionsStore);
|
||
versionsStore.hasVersionUpdates = false;
|
||
versionsStore.currentVersion = currentVersion;
|
||
versionsStore.latestVersion = currentVersion;
|
||
versionsStore.nextVersions = [];
|
||
versionsStore.whatsNew = {
|
||
createdAt: '2025-06-19T12:35:14.454Z',
|
||
updatedAt: null,
|
||
title: "What's New in n8n 1.100.0",
|
||
calloutText:
|
||
'Convert large workflows into sub-workflows for better modularity and performance.',
|
||
footer: 'This release contains performance improvements and bug fixes.',
|
||
items: [
|
||
{
|
||
id: 1,
|
||
title: 'Convert to sub-workflow',
|
||
content:
|
||
'Large, monolithic workflows can slow things down. They’re harder to maintain, ' +
|
||
'tougher to debug, and more difficult to scale. With sub-workflows, you can take a ' +
|
||
'more modular approach, breaking up big workflows into smaller, manageable parts that ' +
|
||
'are easier to reuse, test, understand, and explain.\n\nUntil now, creating sub-workflows ' +
|
||
'required copying and pasting nodes manually, setting up a new workflow from scratch, and ' +
|
||
'reconnecting everything by hand. **Convert to sub-workflow** allows you to simplify this ' +
|
||
'process into a single action, so you can spend more time building and less time restructuring.\n\n' +
|
||
'### How it works\n\n1. Highlight the nodes you want to convert to a sub-workflow. These must:\n' +
|
||
' - Be fully connected, meaning no missing steps in between them\n' +
|
||
' - Start from a single starting node\n' +
|
||
' - End with a single node\n' +
|
||
'2. Right-click to open the context menu and select ' +
|
||
'**Convert to sub-workflow**\n' +
|
||
' - Or use the shortcut: `Alt + X`\n' +
|
||
'3. n8n will:\n' +
|
||
' - Open a new tab containing the selected nodes\n' +
|
||
' - Preserve all node parameters as-is\n' +
|
||
' - Replace the selected nodes in the original workflow with a **Call My Sub-workflow** node\n\n' +
|
||
'_Note:_ You will need to manually adjust the field types in the Start and Return nodes in the new sub-workflow.\n\n' +
|
||
'This makes it easier to keep workflows modular, performant, and easier to maintain.\n\n' +
|
||
'Learn more about [sub-workflows](https://docs.n8n.io/flow-logic/subworkflows/).\n\n' +
|
||
'This release contains performance improvements and bug fixes.\n\n' +
|
||
'@[youtube](ZCuL2e4zC_4)\n\n' +
|
||
'Fusce malesuada diam eget tincidunt ultrices. Mauris quis mauris mollis, venenatis risus ut.\n\n' +
|
||
'## Second level title\n\n### Third level title\n\nThis **is bold**, this _in italics_.\n' +
|
||
"~~Strikethrough is also something we support~~.\n\nHere's a peace of code:\n\n" +
|
||
'```typescript\nconst props = defineProps<{\n\tmodalName: string;\n\tdata: {\n\t\tarticleId: number;\n\t};\n}>();\n```\n\n' +
|
||
'Inline `code also works` withing text.\n\nThis is a list:\n- first\n- second\n- third\n\nAnd this list is ordered\n' +
|
||
'1. foo\n2. bar\n3. qux\n\nDividers:\n\nThree or more...\n\n---\n\nHyphens\n\n***\n\nAsterisks\n\n___\n\nUnderscores\n\n---\n\n' +
|
||
'<details>\n<summary>Fixes (4)</summary>\n\n' +
|
||
'- **Credential Storage Issue** Resolved an issue where credentials would occasionally become inaccessible after server restarts\n' +
|
||
'- **Webhook Timeout Handling** Fixed timeout issues with long-running webhook requests\n' +
|
||
'- **Node Connection Validation** Improved validation for node connections to prevent invalid workflow configurations\n' +
|
||
'- **Memory Leak in Execution Engine** Fixed memory leak that could occur during long-running workflow executions\n\n</details>\n\n',
|
||
createdAt: '2025-06-19T12:35:14.454Z',
|
||
updatedAt: '2025-06-19T12:41:53.220Z',
|
||
publishedAt: '2025-06-19T12:41:53.216Z',
|
||
},
|
||
],
|
||
};
|
||
});
|
||
|
||
afterEach(() => {
|
||
cleanupAppModals();
|
||
vi.clearAllMocks();
|
||
});
|
||
|
||
it('should render with update button disabled', async () => {
|
||
const { getByTestId, queryByTestId } = renderComponent({
|
||
props: {
|
||
data: {
|
||
articleId: 1,
|
||
},
|
||
},
|
||
});
|
||
|
||
await waitFor(() => expect(getByTestId('whatsNew-modal')).toBeInTheDocument());
|
||
await waitFor(() => expect(getByTestId('whats-new-item-1')).toBeInTheDocument());
|
||
|
||
expect(screen.getByText("What's New in n8n 1.100.0")).toBeInTheDocument();
|
||
expect(getByTestId('whats-new-item-1')).toMatchSnapshot();
|
||
expect(getByTestId('whats-new-modal-update-button')).toBeDisabled();
|
||
expect(queryByTestId('whats-new-modal-next-versions-link')).not.toBeInTheDocument();
|
||
});
|
||
|
||
it('should render with update button enabled', async () => {
|
||
versionsStore.hasVersionUpdates = true;
|
||
versionsStore.nextVersions = [
|
||
{
|
||
name: '1.100.1',
|
||
nodes: [],
|
||
createdAt: '2025-06-24T00:00:00Z',
|
||
description: 'Next version description',
|
||
documentationUrl: 'https://docs.n8n.io',
|
||
hasBreakingChange: false,
|
||
hasSecurityFix: false,
|
||
hasSecurityIssue: false,
|
||
securityIssueFixVersion: '',
|
||
},
|
||
];
|
||
|
||
const { getByTestId } = renderComponent({
|
||
props: {
|
||
data: {
|
||
articleId: 1,
|
||
},
|
||
},
|
||
});
|
||
|
||
await waitFor(() => expect(getByTestId('whatsNew-modal')).toBeInTheDocument());
|
||
await waitFor(() => expect(getByTestId('whats-new-item-1')).toBeInTheDocument());
|
||
|
||
expect(getByTestId('whats-new-modal-update-button')).toBeEnabled();
|
||
expect(getByTestId('whats-new-modal-next-versions-link')).toBeInTheDocument();
|
||
expect(getByTestId('whats-new-modal-next-versions-link')).toHaveTextContent('1 version behind');
|
||
});
|
||
|
||
it('should take user to update page when Update is clicked', async () => {
|
||
versionsStore.hasVersionUpdates = true;
|
||
|
||
const { getByTestId } = renderComponent({
|
||
props: {
|
||
data: {
|
||
articleId: 1,
|
||
},
|
||
},
|
||
});
|
||
|
||
await waitFor(() => expect(getByTestId('whatsNew-modal')).toBeInTheDocument());
|
||
await waitFor(() => expect(getByTestId('whats-new-item-1')).toBeInTheDocument());
|
||
|
||
await userEvent.click(getByTestId('whats-new-modal-update-button'));
|
||
|
||
expect(telemetry.track).toHaveBeenCalledWith('User clicked on update button', {
|
||
source: 'whats-new-modal',
|
||
});
|
||
expect(pageRedirectionHelper.goToVersions).toHaveBeenCalledWith();
|
||
});
|
||
|
||
it('should open the next versions drawer when clicking on the next versions link', async () => {
|
||
versionsStore.hasVersionUpdates = true;
|
||
|
||
const { getByTestId } = renderComponent({
|
||
props: {
|
||
data: {
|
||
articleId: 1,
|
||
},
|
||
},
|
||
});
|
||
|
||
await waitFor(() => expect(getByTestId('whatsNew-modal')).toBeInTheDocument());
|
||
await waitFor(() => expect(getByTestId('whats-new-item-1')).toBeInTheDocument());
|
||
|
||
await userEvent.click(getByTestId('whats-new-modal-next-versions-link'));
|
||
|
||
expect(uiStore.openModal).toHaveBeenCalledWith(VERSIONS_MODAL_KEY);
|
||
});
|
||
});
|