mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
470 lines
13 KiB
TypeScript
470 lines
13 KiB
TypeScript
import { within, waitFor } from '@testing-library/dom';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { useRoute } from 'vue-router';
|
|
import { createComponentRenderer } from '@/__tests__/render';
|
|
import SourceControlPushModal from '@/components/SourceControlPushModal.ee.vue';
|
|
import { createTestingPinia } from '@pinia/testing';
|
|
import { createEventBus } from '@n8n/utils/event-bus';
|
|
import type { SourceControlledFile } from '@n8n/api-types';
|
|
import { useSourceControlStore } from '@/stores/sourceControl.store';
|
|
import { mockedStore } from '@/__tests__/utils';
|
|
import { VIEWS } from '@/constants';
|
|
import { useTelemetry } from '@/composables/useTelemetry';
|
|
|
|
const eventBus = createEventBus();
|
|
|
|
vi.mock('vue-router', () => ({
|
|
useRoute: vi.fn().mockReturnValue({
|
|
name: vi.fn(),
|
|
params: vi.fn(),
|
|
fullPath: vi.fn(),
|
|
}),
|
|
RouterLink: vi.fn(),
|
|
useRouter: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('@/composables/useTelemetry', () => {
|
|
const track = vi.fn();
|
|
return {
|
|
useTelemetry: () => {
|
|
return {
|
|
track,
|
|
};
|
|
},
|
|
};
|
|
});
|
|
|
|
let route: ReturnType<typeof useRoute>;
|
|
let telemetry: ReturnType<typeof useTelemetry>;
|
|
|
|
const DynamicScrollerStub = {
|
|
props: {
|
|
items: Array,
|
|
},
|
|
template: '<div><template v-for="item in items"><slot v-bind="{ item }"></slot></template></div>',
|
|
methods: {
|
|
scrollToItem: vi.fn(),
|
|
},
|
|
};
|
|
|
|
const DynamicScrollerItemStub = {
|
|
template: '<slot></slot>',
|
|
};
|
|
|
|
const renderModal = createComponentRenderer(SourceControlPushModal, {
|
|
global: {
|
|
stubs: {
|
|
DynamicScroller: DynamicScrollerStub,
|
|
DynamicScrollerItem: DynamicScrollerItemStub,
|
|
Modal: {
|
|
template: `
|
|
<div>
|
|
<slot name="header" />
|
|
<slot name="title" />
|
|
<slot name="content" />
|
|
<slot name="footer" />
|
|
</div>
|
|
`,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
describe('SourceControlPushModal', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
route = useRoute();
|
|
telemetry = useTelemetry();
|
|
createTestingPinia();
|
|
});
|
|
|
|
it('mounts', () => {
|
|
vi.spyOn(route, 'fullPath', 'get').mockReturnValue('');
|
|
|
|
const { getByText } = renderModal({
|
|
pinia: createTestingPinia(),
|
|
props: {
|
|
data: {
|
|
eventBus,
|
|
status: [],
|
|
},
|
|
},
|
|
});
|
|
expect(getByText('Commit and push changes')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should toggle checkboxes', async () => {
|
|
const status: SourceControlledFile[] = [
|
|
{
|
|
id: 'gTbbBkkYTnNyX1jD',
|
|
name: 'My workflow 1',
|
|
type: 'workflow',
|
|
status: 'created',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '/home/user/.n8n/git/workflows/gTbbBkkYTnNyX1jD.json',
|
|
updatedAt: '2024-09-20T10:31:40.000Z',
|
|
},
|
|
{
|
|
id: 'JIGKevgZagmJAnM6',
|
|
name: 'My workflow 2',
|
|
type: 'workflow',
|
|
status: 'created',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '/home/user/.n8n/git/workflows/JIGKevgZagmJAnM6.json',
|
|
updatedAt: '2024-09-20T14:42:51.968Z',
|
|
},
|
|
];
|
|
|
|
const { getByTestId, getAllByTestId } = renderModal({
|
|
props: {
|
|
data: {
|
|
eventBus,
|
|
status,
|
|
},
|
|
},
|
|
});
|
|
|
|
const files = getAllByTestId('source-control-push-modal-file-checkbox');
|
|
expect(files).toHaveLength(2);
|
|
|
|
await userEvent.click(files[0]);
|
|
expect(within(files[0]).getByRole('checkbox')).toBeChecked();
|
|
expect(within(files[1]).getByRole('checkbox')).not.toBeChecked();
|
|
|
|
await userEvent.click(within(files[0]).getByRole('checkbox'));
|
|
expect(within(files[0]).getByRole('checkbox')).not.toBeChecked();
|
|
expect(within(files[1]).getByRole('checkbox')).not.toBeChecked();
|
|
|
|
await userEvent.click(within(files[1]).getByRole('checkbox'));
|
|
expect(within(files[0]).getByRole('checkbox')).not.toBeChecked();
|
|
expect(within(files[1]).getByRole('checkbox')).toBeChecked();
|
|
|
|
await userEvent.click(files[1]);
|
|
expect(within(files[0]).getByRole('checkbox')).not.toBeChecked();
|
|
expect(within(files[1]).getByRole('checkbox')).not.toBeChecked();
|
|
|
|
await userEvent.click(within(files[0]).getByText('My workflow 2'));
|
|
expect(within(files[0]).getByRole('checkbox')).toBeChecked();
|
|
expect(within(files[1]).getByRole('checkbox')).not.toBeChecked();
|
|
|
|
await userEvent.click(within(files[1]).getByText('My workflow 1'));
|
|
expect(within(files[0]).getByRole('checkbox')).toBeChecked();
|
|
expect(within(files[1]).getByRole('checkbox')).toBeChecked();
|
|
|
|
await userEvent.click(within(files[1]).getByText('My workflow 1'));
|
|
expect(within(files[0]).getByRole('checkbox')).toBeChecked();
|
|
expect(within(files[1]).getByRole('checkbox')).not.toBeChecked();
|
|
|
|
await userEvent.click(getByTestId('source-control-push-modal-toggle-all'));
|
|
expect(within(files[0]).getByRole('checkbox')).toBeChecked();
|
|
expect(within(files[1]).getByRole('checkbox')).toBeChecked();
|
|
|
|
await userEvent.click(within(files[0]).getByText('My workflow 2'));
|
|
await userEvent.click(within(files[1]).getByText('My workflow 1'));
|
|
expect(within(files[0]).getByRole('checkbox')).not.toBeChecked();
|
|
expect(within(files[1]).getByRole('checkbox')).not.toBeChecked();
|
|
expect(
|
|
within(getByTestId('source-control-push-modal-toggle-all')).getByRole('checkbox'),
|
|
).not.toBeChecked();
|
|
|
|
await userEvent.click(within(files[0]).getByText('My workflow 2'));
|
|
await userEvent.click(within(files[1]).getByText('My workflow 1'));
|
|
expect(within(files[0]).getByRole('checkbox')).toBeChecked();
|
|
expect(within(files[1]).getByRole('checkbox')).toBeChecked();
|
|
expect(
|
|
within(getByTestId('source-control-push-modal-toggle-all')).getByRole('checkbox'),
|
|
).toBeChecked();
|
|
|
|
await userEvent.click(getByTestId('source-control-push-modal-toggle-all'));
|
|
expect(within(files[0]).getByRole('checkbox')).not.toBeChecked();
|
|
expect(within(files[1]).getByRole('checkbox')).not.toBeChecked();
|
|
});
|
|
|
|
it('should push non workflow entities', async () => {
|
|
const status: SourceControlledFile[] = [
|
|
{
|
|
id: 'gTbbBkkYTnNyX1jD',
|
|
name: 'credential',
|
|
type: 'credential',
|
|
status: 'created',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '',
|
|
updatedAt: '2024-09-20T10:31:40.000Z',
|
|
},
|
|
{
|
|
id: 'JIGKevgZagmJAnM6',
|
|
name: 'variables',
|
|
type: 'variables',
|
|
status: 'created',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '',
|
|
updatedAt: '2024-09-20T14:42:51.968Z',
|
|
},
|
|
{
|
|
id: 'mappings',
|
|
name: 'tags',
|
|
type: 'tags',
|
|
status: 'modified',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '/Users/raul/.n8n/git/tags.json',
|
|
updatedAt: '2024-12-04T11:29:22.095Z',
|
|
},
|
|
{
|
|
id: 'mappings',
|
|
name: 'folders',
|
|
type: 'folders',
|
|
status: 'modified',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '/Users/raul/.n8n/git/folders.json',
|
|
updatedAt: '2024-12-04T11:29:22.095Z',
|
|
},
|
|
];
|
|
|
|
const sourceControlStore = mockedStore(useSourceControlStore);
|
|
|
|
const { getByTestId, getByRole } = renderModal({
|
|
props: {
|
|
data: {
|
|
eventBus,
|
|
status,
|
|
},
|
|
},
|
|
});
|
|
|
|
const submitButton = getByTestId('source-control-push-modal-submit');
|
|
const commitMessage = 'commit message';
|
|
expect(submitButton).toBeDisabled();
|
|
expect(getByRole('alert').textContent).toContain('Credentials: 1 added.');
|
|
expect(getByRole('alert').textContent).toContain('Variables: at least one new or modified.');
|
|
expect(getByRole('alert').textContent).toContain('Tags: at least one new or modified.');
|
|
expect(getByRole('alert').textContent).toContain('Folders: at least one new or modified.');
|
|
|
|
await userEvent.type(getByTestId('source-control-push-modal-commit'), commitMessage);
|
|
|
|
expect(submitButton).not.toBeDisabled();
|
|
await userEvent.click(submitButton);
|
|
|
|
expect(sourceControlStore.pushWorkfolder).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
commitMessage,
|
|
fileNames: expect.arrayContaining(status),
|
|
force: true,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('should auto select currentWorkflow', async () => {
|
|
const status: SourceControlledFile[] = [
|
|
{
|
|
id: 'gTbbBkkYTnNyX1jD',
|
|
name: 'My workflow 1',
|
|
type: 'workflow',
|
|
status: 'created',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '/home/user/.n8n/git/workflows/gTbbBkkYTnNyX1jD.json',
|
|
updatedAt: '2024-09-20T10:31:40.000Z',
|
|
},
|
|
{
|
|
id: 'JIGKevgZagmJAnM6',
|
|
name: 'My workflow 2',
|
|
type: 'workflow',
|
|
status: 'created',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '/home/user/.n8n/git/workflows/JIGKevgZagmJAnM6.json',
|
|
updatedAt: '2024-09-20T14:42:51.968Z',
|
|
},
|
|
];
|
|
|
|
vi.spyOn(route, 'name', 'get').mockReturnValue(VIEWS.WORKFLOW);
|
|
vi.spyOn(route, 'params', 'get').mockReturnValue({ name: 'gTbbBkkYTnNyX1jD' });
|
|
|
|
const { getByTestId, getAllByTestId } = renderModal({
|
|
props: {
|
|
data: {
|
|
eventBus,
|
|
status,
|
|
},
|
|
},
|
|
});
|
|
|
|
const submitButton = getByTestId('source-control-push-modal-submit');
|
|
expect(submitButton).toBeDisabled();
|
|
|
|
const files = getAllByTestId('source-control-push-modal-file-checkbox');
|
|
expect(files).toHaveLength(2);
|
|
|
|
await waitFor(() => expect(within(files[0]).getByRole('checkbox')).toBeChecked());
|
|
expect(within(files[1]).getByRole('checkbox')).not.toBeChecked();
|
|
|
|
await userEvent.type(getByTestId('source-control-push-modal-commit'), 'message');
|
|
expect(submitButton).not.toBeDisabled();
|
|
});
|
|
|
|
describe('filters', () => {
|
|
it('should filter by name', async () => {
|
|
const status: SourceControlledFile[] = [
|
|
{
|
|
id: 'gTbbBkkYTnNyX1jD',
|
|
name: 'My workflow 1',
|
|
type: 'workflow',
|
|
status: 'created',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '/home/user/.n8n/git/workflows/gTbbBkkYTnNyX1jD.json',
|
|
updatedAt: '2024-09-20T10:31:40.000Z',
|
|
},
|
|
{
|
|
id: 'JIGKevgZagmJAnM6',
|
|
name: 'My workflow 2',
|
|
type: 'workflow',
|
|
status: 'created',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '/home/user/.n8n/git/workflows/JIGKevgZagmJAnM6.json',
|
|
updatedAt: '2024-09-20T14:42:51.968Z',
|
|
},
|
|
];
|
|
|
|
const { getByTestId, getAllByTestId } = renderModal({
|
|
props: {
|
|
data: {
|
|
eventBus,
|
|
status,
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(getAllByTestId('source-control-push-modal-file-checkbox')).toHaveLength(2);
|
|
|
|
await userEvent.type(getByTestId('source-control-push-search'), '1');
|
|
await waitFor(() => {
|
|
expect(getAllByTestId('source-control-push-modal-file-checkbox')).toHaveLength(1);
|
|
expect(telemetry.track).toHaveBeenCalledWith('User searched workflows in commit modal', {
|
|
search: '1',
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should filter by status', async () => {
|
|
const status: SourceControlledFile[] = [
|
|
{
|
|
id: 'gTbbBkkYTnNyX1jD',
|
|
name: 'Created Workflow',
|
|
type: 'workflow',
|
|
status: 'created',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '/home/user/.n8n/git/workflows/gTbbBkkYTnNyX1jD.json',
|
|
updatedAt: '2024-09-20T10:31:40.000Z',
|
|
},
|
|
{
|
|
id: 'JIGKevgZagmJAnM6',
|
|
name: 'Modified workflow',
|
|
type: 'workflow',
|
|
status: 'modified',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '/home/user/.n8n/git/workflows/JIGKevgZagmJAnM6.json',
|
|
updatedAt: '2024-09-20T14:42:51.968Z',
|
|
},
|
|
];
|
|
|
|
const { getByTestId, getAllByTestId } = renderModal({
|
|
props: {
|
|
data: {
|
|
eventBus,
|
|
status,
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(getAllByTestId('source-control-push-modal-file-checkbox')).toHaveLength(2);
|
|
|
|
await userEvent.click(getByTestId('source-control-filter-dropdown'));
|
|
|
|
expect(getByTestId('source-control-status-filter')).toBeVisible();
|
|
|
|
await userEvent.click(
|
|
within(getByTestId('source-control-status-filter')).getByRole('combobox'),
|
|
);
|
|
|
|
await waitFor(() =>
|
|
expect(getAllByTestId('source-control-status-filter-option')[0]).toBeVisible(),
|
|
);
|
|
|
|
const menu = getAllByTestId('source-control-status-filter-option')[0]
|
|
.parentElement as HTMLElement;
|
|
|
|
await userEvent.click(within(menu).getByText('New'));
|
|
await waitFor(() => {
|
|
const items = getAllByTestId('source-control-push-modal-file-checkbox');
|
|
expect(items).toHaveLength(1);
|
|
expect(items[0]).toHaveTextContent('Created Workflow');
|
|
expect(telemetry.track).toHaveBeenCalledWith('User filtered by status in commit modal', {
|
|
status: 'created',
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should reset', async () => {
|
|
const status: SourceControlledFile[] = [
|
|
{
|
|
id: 'JIGKevgZagmJAnM6',
|
|
name: 'Modified workflow',
|
|
type: 'workflow',
|
|
status: 'modified',
|
|
location: 'local',
|
|
conflict: false,
|
|
file: '/home/user/.n8n/git/workflows/JIGKevgZagmJAnM6.json',
|
|
updatedAt: '2024-09-20T14:42:51.968Z',
|
|
},
|
|
];
|
|
|
|
const { getByTestId, getAllByTestId, queryAllByTestId } = renderModal({
|
|
props: {
|
|
data: {
|
|
eventBus,
|
|
status,
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(getAllByTestId('source-control-push-modal-file-checkbox')).toHaveLength(1);
|
|
|
|
await userEvent.click(getByTestId('source-control-filter-dropdown'));
|
|
|
|
expect(getByTestId('source-control-status-filter')).toBeVisible();
|
|
|
|
await userEvent.click(
|
|
within(getByTestId('source-control-status-filter')).getByRole('combobox'),
|
|
);
|
|
|
|
await waitFor(() =>
|
|
expect(getAllByTestId('source-control-status-filter-option')[0]).toBeVisible(),
|
|
);
|
|
|
|
const menu = getAllByTestId('source-control-status-filter-option')[0]
|
|
.parentElement as HTMLElement;
|
|
|
|
await userEvent.click(within(menu).getByText('New'));
|
|
await waitFor(() => {
|
|
expect(queryAllByTestId('source-control-push-modal-file-checkbox')).toHaveLength(0);
|
|
expect(getByTestId('source-control-filters-reset')).toBeInTheDocument();
|
|
});
|
|
|
|
await userEvent.click(getByTestId('source-control-filters-reset'));
|
|
|
|
const items = getAllByTestId('source-control-push-modal-file-checkbox');
|
|
expect(items).toHaveLength(1);
|
|
});
|
|
});
|
|
});
|