Files
n8n-enterprise-unlocked/packages/frontend/editor-ui/src/components/WhatsNewModal.test.ts

230 lines
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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. Theyre 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);
});
});