test(editor): Clean up duplicate DOM setup code in frontend tests (no-changelog) (#19382)

This commit is contained in:
Csaba Tuncsik
2025-09-11 12:28:20 +02:00
committed by GitHub
parent 1e00a7c788
commit ff1f9ff69e
32 changed files with 72 additions and 202 deletions

View File

@@ -2,12 +2,39 @@ import '@testing-library/jest-dom';
import 'fake-indexeddb/auto'; import 'fake-indexeddb/auto';
import { configure } from '@testing-library/vue'; import { configure } from '@testing-library/vue';
import 'core-js/proposals/set-methods-v2'; import 'core-js/proposals/set-methods-v2';
import { APP_MODALS_ELEMENT_ID } from '@/constants';
// Avoid tests failing because of difference between local and GitHub actions timezone // Avoid tests failing because of difference between local and GitHub actions timezone
process.env.TZ = 'UTC'; process.env.TZ = 'UTC';
configure({ testIdAttribute: 'data-test-id' }); configure({ testIdAttribute: 'data-test-id' });
// Create DOM containers for Element Plus components before each test
beforeEach(() => {
// Create app-grid container for toasts
const appGrid = document.createElement('div');
appGrid.id = 'app-grid';
document.body.appendChild(appGrid);
// Create app-modals container for modals
const appModals = document.createElement('div');
appModals.id = APP_MODALS_ELEMENT_ID;
document.body.appendChild(appModals);
});
afterEach(() => {
// Clean up only our specific DOM containers to avoid interfering with Vue's unmounting
const appGrid = document.getElementById('app-grid');
const appModals = document.getElementById(APP_MODALS_ELEMENT_ID);
if (appGrid) {
appGrid.remove();
}
if (appModals) {
appModals.remove();
}
});
window.ResizeObserver = window.ResizeObserver =
window.ResizeObserver || window.ResizeObserver ||
vi.fn().mockImplementation(() => ({ vi.fn().mockImplementation(() => ({

View File

@@ -3,7 +3,6 @@ import userEvent from '@testing-library/user-event';
import type { ISettingsState } from '@/Interface'; import type { ISettingsState } from '@/Interface';
import { UserManagementAuthenticationMethod } from '@/Interface'; import { UserManagementAuthenticationMethod } from '@/Interface';
import { defaultSettings } from './defaults'; import { defaultSettings } from './defaults';
import { APP_MODALS_ELEMENT_ID } from '@/constants';
import type { Mock } from 'vitest'; import type { Mock } from 'vitest';
import type { Store, StoreDefinition } from 'pinia'; import type { Store, StoreDefinition } from 'pinia';
import type { ComputedRef } from 'vue'; import type { ComputedRef } from 'vue';
@@ -95,23 +94,6 @@ export const getSelectedDropdownValue = async (items: NodeListOf<Element>) => {
return selectedItem?.querySelector('p')?.textContent?.trim(); return selectedItem?.querySelector('p')?.textContent?.trim();
}; };
/**
* Create a container for teleported modals
*
* More info: https://test-utils.vuejs.org/guide/advanced/teleport#Mounting-the-Component
* @returns {HTMLElement} appModals
*/
export const createAppModals = () => {
const appModals = document.createElement('div');
appModals.id = APP_MODALS_ELEMENT_ID;
document.body.appendChild(appModals);
return appModals;
};
export const cleanupAppModals = () => {
document.body.innerHTML = '';
};
/** /**
* Typescript helper for mocking pinia store actions return value * Typescript helper for mocking pinia store actions return value
* *

View File

@@ -2,9 +2,9 @@ import { createComponentRenderer } from '@/__tests__/render';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
import { API_KEY_CREATE_OR_EDIT_MODAL_KEY } from '@/constants'; import { API_KEY_CREATE_OR_EDIT_MODAL_KEY } from '@/constants';
import { STORES } from '@n8n/stores'; import { STORES } from '@n8n/stores';
import { cleanupAppModals, createAppModals, mockedStore, retry } from '@/__tests__/utils'; import { mockedStore, retry } from '@/__tests__/utils';
import ApiKeyEditModal from './ApiKeyCreateOrEditModal.vue'; import ApiKeyEditModal from './ApiKeyCreateOrEditModal.vue';
import { fireEvent } from '@testing-library/vue'; import userEvent from '@testing-library/user-event';
import { useApiKeysStore } from '@/stores/apiKeys.store'; import { useApiKeysStore } from '@/stores/apiKeys.store';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
@@ -40,13 +40,11 @@ const settingsStore = mockedStore(useSettingsStore);
describe('ApiKeyCreateOrEditModal', () => { describe('ApiKeyCreateOrEditModal', () => {
beforeEach(() => { beforeEach(() => {
createAppModals();
apiKeysStore.availableScopes = ['user:create', 'user:list']; apiKeysStore.availableScopes = ['user:create', 'user:list'];
settingsStore.settings.enterprise = createMockEnterpriseSettings({ apiKeyScopes: false }); settingsStore.settings.enterprise = createMockEnterpriseSettings({ apiKeyScopes: false });
}); });
afterEach(() => { afterEach(() => {
cleanupAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });
@@ -68,9 +66,9 @@ describe('ApiKeyCreateOrEditModal', () => {
expect(inputLabel).toBeInTheDocument(); expect(inputLabel).toBeInTheDocument();
expect(saveButton).toBeInTheDocument(); expect(saveButton).toBeInTheDocument();
await fireEvent.update(inputLabel, 'new label'); await userEvent.type(inputLabel, 'new label');
await fireEvent.click(saveButton); await userEvent.click(saveButton);
expect(getByText('API Key Created')).toBeInTheDocument(); expect(getByText('API Key Created')).toBeInTheDocument();
@@ -114,23 +112,23 @@ describe('ApiKeyCreateOrEditModal', () => {
expect(saveButton).toBeInTheDocument(); expect(saveButton).toBeInTheDocument();
expect(expirationSelect).toBeInTheDocument(); expect(expirationSelect).toBeInTheDocument();
await fireEvent.update(inputLabel, 'new label'); await userEvent.type(inputLabel, 'new label');
await fireEvent.click(expirationSelect); await userEvent.click(expirationSelect);
const customOption = getByText('Custom'); const customOption = getByText('Custom');
expect(customOption).toBeInTheDocument(); expect(customOption).toBeInTheDocument();
await fireEvent.click(customOption); await userEvent.click(customOption);
const customExpirationInput = getByPlaceholderText('yyyy-mm-dd'); const customExpirationInput = getByPlaceholderText('yyyy-mm-dd');
expect(customExpirationInput).toBeInTheDocument(); expect(customExpirationInput).toBeInTheDocument();
await fireEvent.input(customExpirationInput, '2029-12-31'); await userEvent.type(customExpirationInput, '2029-12-31');
await fireEvent.click(saveButton); await userEvent.click(saveButton);
expect(getByText('***456')).toBeInTheDocument(); expect(getByText('***456')).toBeInTheDocument();
@@ -167,17 +165,17 @@ describe('ApiKeyCreateOrEditModal', () => {
expect(saveButton).toBeInTheDocument(); expect(saveButton).toBeInTheDocument();
expect(expirationSelect).toBeInTheDocument(); expect(expirationSelect).toBeInTheDocument();
await fireEvent.update(inputLabel, 'new label'); await userEvent.type(inputLabel, 'new label');
await fireEvent.click(expirationSelect); await userEvent.click(expirationSelect);
const noExpirationOption = getByText('No Expiration'); const noExpirationOption = getByText('No Expiration');
expect(noExpirationOption).toBeInTheDocument(); expect(noExpirationOption).toBeInTheDocument();
await fireEvent.click(noExpirationOption); await userEvent.click(noExpirationOption);
await fireEvent.click(saveButton); await userEvent.click(saveButton);
expect(getByText('API Key Created')).toBeInTheDocument(); expect(getByText('API Key Created')).toBeInTheDocument();
@@ -215,22 +213,22 @@ describe('ApiKeyCreateOrEditModal', () => {
expect(scopesSelect).toBeInTheDocument(); expect(scopesSelect).toBeInTheDocument();
expect(saveButton).toBeInTheDocument(); expect(saveButton).toBeInTheDocument();
await fireEvent.update(inputLabel, 'new label'); await userEvent.type(inputLabel, 'new label');
await fireEvent.click(scopesSelect); await userEvent.click(scopesSelect);
const userCreateScope = getByText('user:create'); const userCreateScope = getByText('user:create');
expect(userCreateScope).toBeInTheDocument(); expect(userCreateScope).toBeInTheDocument();
await fireEvent.click(userCreateScope); await userEvent.click(userCreateScope);
const [userCreateTag, userCreateSelectOption] = getAllByText('user:create'); const [userCreateTag, userCreateSelectOption] = getAllByText('user:create');
expect(userCreateTag).toBeInTheDocument(); expect(userCreateTag).toBeInTheDocument();
expect(userCreateSelectOption).toBeInTheDocument(); expect(userCreateSelectOption).toBeInTheDocument();
await fireEvent.click(saveButton); await userEvent.click(saveButton);
expect(getByText('API Key Created')).toBeInTheDocument(); expect(getByText('API Key Created')).toBeInTheDocument();
@@ -250,7 +248,7 @@ describe('ApiKeyCreateOrEditModal', () => {
apiKeysStore.createApiKey.mockResolvedValue(testApiKey); apiKeysStore.createApiKey.mockResolvedValue(testApiKey);
const { getByText, getByPlaceholderText, getByTestId, getAllByText } = renderComponent({ const { getByText, getByPlaceholderText, getByTestId, getByRole } = renderComponent({
props: { props: {
mode: 'new', mode: 'new',
}, },
@@ -271,23 +269,23 @@ describe('ApiKeyCreateOrEditModal', () => {
expect(scopesSelect).toBeInTheDocument(); expect(scopesSelect).toBeInTheDocument();
expect(saveButton).toBeInTheDocument(); expect(saveButton).toBeInTheDocument();
await fireEvent.update(inputLabel, 'new label'); await userEvent.type(inputLabel, 'new label');
await fireEvent.click(scopesSelect); await userEvent.click(scopesSelect);
const userCreateScope = getAllByText('user:create'); // Use separate semantic queries instead of destructuring
// The text is nested inside .el-select__tags-text which is inside .el-tag
const userCreateTag = getByText('user:create', { selector: '.el-select__tags-text' });
const userCreateSelectOption = getByRole('option', { name: 'user:create' });
const [userCreateTag, userCreateSelectOption] = userCreateScope;
expect(userCreateTag).toBeInTheDocument(); expect(userCreateTag).toBeInTheDocument();
expect(userCreateSelectOption).toBeInTheDocument(); expect(userCreateSelectOption).toBeInTheDocument();
expect(userCreateSelectOption).toBeInTheDocument(); expect(userCreateSelectOption).toHaveClass('is-disabled');
expect(userCreateSelectOption.parentNode).toHaveClass('is-disabled'); await userEvent.click(userCreateSelectOption);
await fireEvent.click(userCreateSelectOption); await userEvent.click(saveButton);
await fireEvent.click(saveButton);
expect(getByText('API Key Created')).toBeInTheDocument(); expect(getByText('API Key Created')).toBeInTheDocument();
@@ -328,13 +326,14 @@ describe('ApiKeyCreateOrEditModal', () => {
expect((labelInput as unknown as HTMLInputElement).value).toBe('new api key'); expect((labelInput as unknown as HTMLInputElement).value).toBe('new api key');
await fireEvent.update(labelInput, 'updated api key'); await userEvent.clear(labelInput);
await userEvent.type(labelInput, 'updated api key');
const saveButton = getByText('Save'); const saveButton = getByText('Save');
expect(saveButton).toBeInTheDocument(); expect(saveButton).toBeInTheDocument();
await fireEvent.click(saveButton); await userEvent.click(saveButton);
expect(apiKeysStore.updateApiKey).toHaveBeenCalledWith('123', { expect(apiKeysStore.updateApiKey).toHaveBeenCalledWith('123', {
label: 'updated api key', label: 'updated api key',

View File

@@ -31,6 +31,7 @@ const { goToUpgrade } = usePageRedirectionHelper();
const checkAll = ref(false); const checkAll = ref(false);
const indeterminate = ref(false); const indeterminate = ref(false);
const popperContainer = ref(null);
const groupedScopes = computed(() => { const groupedScopes = computed(() => {
const groups = {}; const groups = {};

View File

@@ -7,7 +7,6 @@ import Assignment from './Assignment.vue';
import { defaultSettings } from '@/__tests__/defaults'; import { defaultSettings } from '@/__tests__/defaults';
import { STORES } from '@n8n/stores'; import { STORES } from '@n8n/stores';
import merge from 'lodash/merge'; import merge from 'lodash/merge';
import { cleanupAppModals, createAppModals } from '@/__tests__/utils';
import * as useResolvedExpression from '@/composables/useResolvedExpression'; import * as useResolvedExpression from '@/composables/useResolvedExpression';
const DEFAULT_SETUP = { const DEFAULT_SETUP = {
@@ -28,12 +27,7 @@ const DEFAULT_SETUP = {
const renderComponent = createComponentRenderer(Assignment, DEFAULT_SETUP); const renderComponent = createComponentRenderer(Assignment, DEFAULT_SETUP);
describe('Assignment.vue', () => { describe('Assignment.vue', () => {
beforeEach(() => {
createAppModals();
});
afterEach(() => { afterEach(() => {
cleanupAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });

View File

@@ -6,7 +6,7 @@ import { fireEvent, within } from '@testing-library/vue';
import * as workflowHelpers from '@/composables/useWorkflowHelpers'; import * as workflowHelpers from '@/composables/useWorkflowHelpers';
import AssignmentCollection from './AssignmentCollection.vue'; import AssignmentCollection from './AssignmentCollection.vue';
import { STORES } from '@n8n/stores'; import { STORES } from '@n8n/stores';
import { cleanupAppModals, createAppModals, SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils'; import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
const DEFAULT_SETUP = { const DEFAULT_SETUP = {
pinia: createTestingPinia({ pinia: createTestingPinia({
@@ -64,13 +64,8 @@ async function dropAssignment({
} }
describe('AssignmentCollection.vue', () => { describe('AssignmentCollection.vue', () => {
beforeEach(() => {
createAppModals();
});
afterEach(() => { afterEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
cleanupAppModals();
}); });
it('renders empty state properly', async () => { it('renders empty state properly', async () => {

View File

@@ -2,7 +2,6 @@ import { createTestingPinia } from '@pinia/testing';
import ChangePasswordModal from '@/components/ChangePasswordModal.vue'; import ChangePasswordModal from '@/components/ChangePasswordModal.vue';
import type { createPinia } from 'pinia'; import type { createPinia } from 'pinia';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { cleanupAppModals, createAppModals } from '@/__tests__/utils';
const renderComponent = createComponentRenderer(ChangePasswordModal); const renderComponent = createComponentRenderer(ChangePasswordModal);
@@ -10,14 +9,9 @@ describe('ChangePasswordModal', () => {
let pinia: ReturnType<typeof createPinia>; let pinia: ReturnType<typeof createPinia>;
beforeEach(() => { beforeEach(() => {
createAppModals();
pinia = createTestingPinia({}); pinia = createTestingPinia({});
}); });
afterEach(() => {
cleanupAppModals();
});
it('should render correctly', () => { it('should render correctly', () => {
const wrapper = renderComponent({ pinia }); const wrapper = renderComponent({ pinia });

View File

@@ -4,7 +4,6 @@ import { CHAT_EMBED_MODAL_KEY, WEBHOOK_NODE_TYPE } from '@/constants';
import { STORES } from '@n8n/stores'; import { STORES } from '@n8n/stores';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { waitFor } from '@testing-library/vue'; import { waitFor } from '@testing-library/vue';
import { cleanupAppModals, createAppModals } from '@/__tests__/utils';
const renderComponent = createComponentRenderer(ChatEmbedModal, { const renderComponent = createComponentRenderer(ChatEmbedModal, {
props: { props: {
@@ -28,13 +27,6 @@ const renderComponent = createComponentRenderer(ChatEmbedModal, {
}); });
describe('ChatEmbedModal', () => { describe('ChatEmbedModal', () => {
beforeEach(() => {
createAppModals();
});
afterEach(() => {
cleanupAppModals();
});
it('should render correctly', async () => { it('should render correctly', async () => {
const { getByTestId } = renderComponent(); const { getByTestId } = renderComponent();

View File

@@ -4,7 +4,7 @@ import { createTestingPinia } from '@pinia/testing';
import { COMMUNITY_PACKAGE_INSTALL_MODAL_KEY } from '@/constants'; import { COMMUNITY_PACKAGE_INSTALL_MODAL_KEY } from '@/constants';
import { STORES } from '@n8n/stores'; import { STORES } from '@n8n/stores';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { cleanupAppModals, createAppModals, retry } from '@/__tests__/utils'; import { retry } from '@/__tests__/utils';
const renderComponent = createComponentRenderer(CommunityPackageInstallModal, { const renderComponent = createComponentRenderer(CommunityPackageInstallModal, {
props: { props: {
@@ -34,13 +34,6 @@ const renderComponent = createComponentRenderer(CommunityPackageInstallModal, {
}); });
describe('CommunityPackageInstallModal', () => { describe('CommunityPackageInstallModal', () => {
beforeEach(() => {
createAppModals();
});
afterEach(() => {
cleanupAppModals();
});
it('should disable install button until user agrees', async () => { it('should disable install button until user agrees', async () => {
const { getByTestId } = renderComponent(); const { getByTestId } = renderComponent();

View File

@@ -1,7 +1,7 @@
import { useNodeTypesStore } from '@/stores/nodeTypes.store'; import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import CommunityPackageManageConfirmModal from './CommunityPackageManageConfirmModal.vue'; import CommunityPackageManageConfirmModal from './CommunityPackageManageConfirmModal.vue';
import { cleanupAppModals, createAppModals, SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils'; import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
import { useSettingsStore } from '@/stores/settings.store'; import { useSettingsStore } from '@/stores/settings.store';
import { defaultSettings } from '@/__tests__/defaults'; import { defaultSettings } from '@/__tests__/defaults';
import { mockNodeTypeDescription } from '@/__tests__/mocks'; import { mockNodeTypeDescription } from '@/__tests__/mocks';
@@ -65,15 +65,9 @@ describe('CommunityPackageManageConfirmModal', () => {
let nodeTypesStore: ReturnType<typeof useNodeTypesStore>; let nodeTypesStore: ReturnType<typeof useNodeTypesStore>;
beforeEach(() => { beforeEach(() => {
createAppModals();
nodeTypesStore = useNodeTypesStore(); nodeTypesStore = useNodeTypesStore();
}); });
afterEach(() => {
cleanupAppModals();
});
it('should call nodeTypesStore methods and update latestVerifiedVersion on mount', async () => { it('should call nodeTypesStore methods and update latestVerifiedVersion on mount', async () => {
nodeTypesStore.loadNodeTypesIfNotLoaded = vi.fn().mockResolvedValue(undefined); nodeTypesStore.loadNodeTypesIfNotLoaded = vi.fn().mockResolvedValue(undefined);
nodeTypesStore.getCommunityNodeAttributes = vi.fn().mockResolvedValue({ npmVersion: '2.0.0' }); nodeTypesStore.getCommunityNodeAttributes = vi.fn().mockResolvedValue({ npmVersion: '2.0.0' });

View File

@@ -3,7 +3,7 @@ import CredentialEdit from '@/components/CredentialEdit/CredentialEdit.vue';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
import { CREDENTIAL_EDIT_MODAL_KEY } from '@/constants'; import { CREDENTIAL_EDIT_MODAL_KEY } from '@/constants';
import { STORES } from '@n8n/stores'; import { STORES } from '@n8n/stores';
import { cleanupAppModals, createAppModals, retry, mockedStore } from '@/__tests__/utils'; import { retry, mockedStore } from '@/__tests__/utils';
import { useCredentialsStore } from '@/stores/credentials.store'; import { useCredentialsStore } from '@/stores/credentials.store';
import type { ICredentialsResponse } from '@/Interface'; import type { ICredentialsResponse } from '@/Interface';
import { within } from '@testing-library/vue'; import { within } from '@testing-library/vue';
@@ -212,12 +212,7 @@ const renderComponent = createComponentRenderer(CredentialEdit, {
}), }),
}); });
describe('CredentialEdit', () => { describe('CredentialEdit', () => {
beforeEach(() => {
createAppModals();
});
afterEach(() => { afterEach(() => {
cleanupAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });

View File

@@ -5,7 +5,6 @@ import DynamicModalLoader from '@/components/DynamicModalLoader.vue';
import * as modalRegistry from '@/moduleInitializer/modalRegistry'; import * as modalRegistry from '@/moduleInitializer/modalRegistry';
import type { ModalDefinition } from '@/moduleInitializer/module.types'; import type { ModalDefinition } from '@/moduleInitializer/module.types';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { cleanupAppModals, createAppModals } from '@/__tests__/utils';
// Mock the modalRegistry module // Mock the modalRegistry module
vi.mock('@/moduleInitializer/modalRegistry', () => ({ vi.mock('@/moduleInitializer/modalRegistry', () => ({
@@ -54,14 +53,9 @@ describe('DynamicModalLoader', () => {
const mockAsyncModalComponent = vi.fn(async () => await Promise.resolve(mockModalComponent)); const mockAsyncModalComponent = vi.fn(async () => await Promise.resolve(mockModalComponent));
beforeEach(() => { beforeEach(() => {
createAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });
afterEach(() => {
cleanupAppModals();
});
it('should render empty div when no modals are registered', () => { it('should render empty div when no modals are registered', () => {
mockModalRegistry.getAll.mockReturnValue(new Map()); mockModalRegistry.getAll.mockReturnValue(new Map());
mockModalRegistry.subscribe.mockReturnValue(vi.fn()); mockModalRegistry.subscribe.mockReturnValue(vi.fn());

View File

@@ -1,5 +1,4 @@
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { cleanupAppModals, createAppModals } from '@/__tests__/utils';
import ExpressionEditModal from '@/components/ExpressionEditModal.vue'; import ExpressionEditModal from '@/components/ExpressionEditModal.vue';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
import { waitFor, within } from '@testing-library/vue'; import { waitFor, within } from '@testing-library/vue';
@@ -29,14 +28,12 @@ describe('ExpressionEditModal', () => {
let pinia: Pinia; let pinia: Pinia;
beforeEach(() => { beforeEach(() => {
createAppModals();
pinia = createTestingPinia({ stubActions: false }); pinia = createTestingPinia({ stubActions: false });
setActivePinia(pinia); setActivePinia(pinia);
useSettingsStore().setSettings(defaultSettings); useSettingsStore().setSettings(defaultSettings);
}); });
afterEach(() => { afterEach(() => {
cleanupAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });

View File

@@ -1,5 +1,5 @@
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { cleanupAppModals, createAppModals, SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils'; import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
import FilterConditions from '@/components/FilterConditions/FilterConditions.vue'; import FilterConditions from '@/components/FilterConditions/FilterConditions.vue';
import { STORES } from '@n8n/stores'; import { STORES } from '@n8n/stores';
import { useNDVStore } from '@/stores/ndv.store'; import { useNDVStore } from '@/stores/ndv.store';
@@ -36,13 +36,8 @@ const DEFAULT_SETUP = {
const renderComponent = createComponentRenderer(FilterConditions, DEFAULT_SETUP); const renderComponent = createComponentRenderer(FilterConditions, DEFAULT_SETUP);
describe('FilterConditions.vue', () => { describe('FilterConditions.vue', () => {
beforeEach(() => {
createAppModals();
});
afterEach(() => { afterEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
cleanupAppModals();
}); });
it('renders default state properly', async () => { it('renders default state properly', async () => {

View File

@@ -1,5 +1,5 @@
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { cleanupAppModals, createAppModals, SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils'; import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
import FixedCollectionParameter, { type Props } from '@/components/FixedCollectionParameter.vue'; import FixedCollectionParameter, { type Props } from '@/components/FixedCollectionParameter.vue';
import { STORES } from '@n8n/stores'; import { STORES } from '@n8n/stores';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
@@ -57,14 +57,6 @@ describe('FixedCollectionParameter.vue', () => {
}; };
const renderComponent = createComponentRenderer(FixedCollectionParameter, { props }); const renderComponent = createComponentRenderer(FixedCollectionParameter, { props });
beforeEach(() => {
createAppModals();
});
afterEach(() => {
cleanupAppModals();
});
it('renders the component', () => { it('renders the component', () => {
const { getByTestId } = renderComponent(); const { getByTestId } = renderComponent();
expect(getByTestId('fixed-collection-rules')).toBeInTheDocument(); expect(getByTestId('fixed-collection-rules')).toBeInTheDocument();

View File

@@ -5,8 +5,6 @@ import { faker } from '@faker-js/faker';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { createProjectListItem, createProjectSharingData } from '@/__tests__/data/projects'; import { createProjectListItem, createProjectSharingData } from '@/__tests__/data/projects';
import { import {
cleanupAppModals,
createAppModals,
getDropdownItems, getDropdownItems,
getSelectedDropdownValue, getSelectedDropdownValue,
mockedStore, mockedStore,
@@ -140,7 +138,6 @@ const mockEventBus = {
describe('MoveToFolderModal', () => { describe('MoveToFolderModal', () => {
beforeEach(() => { beforeEach(() => {
createAppModals();
createTestingPinia(); createTestingPinia();
uiStore = mockedStore(useUIStore); uiStore = mockedStore(useUIStore);
uiStore.modalsById = { uiStore.modalsById = {
@@ -183,7 +180,6 @@ describe('MoveToFolderModal', () => {
}); });
afterEach(() => { afterEach(() => {
cleanupAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });

View File

@@ -2,7 +2,7 @@ import { createComponentRenderer } from '@/__tests__/render';
import ImportCurlModal from './ImportCurlModal.vue'; import ImportCurlModal from './ImportCurlModal.vue';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
import { IMPORT_CURL_MODAL_KEY } from '@/constants'; import { IMPORT_CURL_MODAL_KEY } from '@/constants';
import { cleanupAppModals, createAppModals, mockedStore } from '@/__tests__/utils'; import { mockedStore } from '@/__tests__/utils';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { useUIStore } from '@/stores/ui.store'; import { useUIStore } from '@/stores/ui.store';
import { useNDVStore } from '@/stores/ndv.store'; import { useNDVStore } from '@/stores/ndv.store';
@@ -43,11 +43,6 @@ const testNode = {
describe('ImportCurlModal', () => { describe('ImportCurlModal', () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
createAppModals();
});
afterEach(() => {
cleanupAppModals();
}); });
it('should show empty input when no curl command exists for active node', async () => { it('should show empty input when no curl command exists for active node', async () => {

View File

@@ -17,7 +17,6 @@ import {
defaultNodeDescriptions, defaultNodeDescriptions,
mockNodes, mockNodes,
} from '@/__tests__/mocks'; } from '@/__tests__/mocks';
import { cleanupAppModals, createAppModals } from '@/__tests__/utils';
vi.mock('vue-router', () => { vi.mock('vue-router', () => {
return { return {
@@ -68,12 +67,7 @@ describe('NodeDetailsView', () => {
server = setupServer(); server = setupServer();
}); });
beforeEach(() => {
createAppModals();
});
afterEach(() => { afterEach(() => {
cleanupAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });

View File

@@ -17,7 +17,6 @@ import {
defaultNodeDescriptions, defaultNodeDescriptions,
mockNodes, mockNodes,
} from '@/__tests__/mocks'; } from '@/__tests__/mocks';
import { cleanupAppModals, createAppModals } from '@/__tests__/utils';
vi.mock('vue-router', () => { vi.mock('vue-router', () => {
return { return {
@@ -74,12 +73,7 @@ describe('NodeDetailsViewV2', () => {
server = setupServer(); server = setupServer();
}); });
beforeEach(() => {
createAppModals();
});
afterEach(() => { afterEach(() => {
cleanupAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });

View File

@@ -8,7 +8,7 @@ import { waitFor, within } from '@testing-library/vue';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import type { useNodeTypesStore } from '@/stores/nodeTypes.store'; import type { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useSettingsStore } from '@/stores/settings.store'; import { useSettingsStore } from '@/stores/settings.store';
import { cleanupAppModals, createAppModals, mockedStore } from '@/__tests__/utils'; import { mockedStore } from '@/__tests__/utils';
import { createEventBus } from '@n8n/utils/event-bus'; import { createEventBus } from '@n8n/utils/event-bus';
import { createMockEnterpriseSettings } from '@/__tests__/mocks'; import { createMockEnterpriseSettings } from '@/__tests__/mocks';
import { useWorkflowsStore } from '@/stores/workflows.store'; import { useWorkflowsStore } from '@/stores/workflows.store';
@@ -51,7 +51,6 @@ beforeEach(() => {
mockNdvState = getNdvStateMock(); mockNdvState = getNdvStateMock();
mockNodeTypesState = getNodeTypesStateMock(); mockNodeTypesState = getNodeTypesStateMock();
mockCompletionResult = {}; mockCompletionResult = {};
createAppModals();
}); });
vi.mock('@/stores/ndv.store', () => { vi.mock('@/stores/ndv.store', () => {
@@ -117,12 +116,10 @@ describe('ParameterInput.vue', () => {
allNodeTypes: [], allNodeTypes: [],
getNodeType: vi.fn().mockReturnValue(null), getNodeType: vi.fn().mockReturnValue(null),
}; };
createAppModals();
settingsStore.settings.enterprise = createMockEnterpriseSettings(); settingsStore.settings.enterprise = createMockEnterpriseSettings();
}); });
afterEach(() => { afterEach(() => {
cleanupAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });

View File

@@ -3,7 +3,6 @@ import type { useNDVStore } from '@/stores/ndv.store';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
import type { useNodeTypesStore } from '@/stores/nodeTypes.store'; import type { useNodeTypesStore } from '@/stores/nodeTypes.store';
import type { useSettingsStore } from '@/stores/settings.store'; import type { useSettingsStore } from '@/stores/settings.store';
import { cleanupAppModals, createAppModals } from '@/__tests__/utils';
import ParameterInputFull from './ParameterInputFull.vue'; import ParameterInputFull from './ParameterInputFull.vue';
import { FROM_AI_AUTO_GENERATED_MARKER } from 'n8n-workflow'; import { FROM_AI_AUTO_GENERATED_MARKER } from 'n8n-workflow';
import { fireEvent } from '@testing-library/vue'; import { fireEvent } from '@testing-library/vue';
@@ -44,7 +43,6 @@ beforeEach(() => {
} as never, } as never,
isEnterpriseFeatureEnabled: { externalSecrets: false } as never, isEnterpriseFeatureEnabled: { externalSecrets: false } as never,
}; };
createAppModals();
}); });
vi.mock('@/stores/ndv.store', () => { vi.mock('@/stores/ndv.store', () => {
@@ -81,11 +79,6 @@ const renderComponent = createComponentRenderer(ParameterInputFull, {
describe('ParameterInputFull.vue', () => { describe('ParameterInputFull.vue', () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
createAppModals();
});
afterEach(() => {
cleanupAppModals();
}); });
it('should render basic parameter', async () => { it('should render basic parameter', async () => {

View File

@@ -3,7 +3,6 @@ import { createComponentRenderer } from '@/__tests__/render';
import ParameterOverrideSelectableList from './ParameterOverrideSelectableList.vue'; import ParameterOverrideSelectableList from './ParameterOverrideSelectableList.vue';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
import { ref } from 'vue'; import { ref } from 'vue';
import { createAppModals } from '@/__tests__/utils';
import { STORES } from '@n8n/stores'; import { STORES } from '@n8n/stores';
import { waitFor } from '@testing-library/vue'; import { waitFor } from '@testing-library/vue';
@@ -18,10 +17,6 @@ vi.mock('vue-router', () => {
}; };
}); });
beforeEach(() => {
createAppModals();
});
const renderComponent = createComponentRenderer(ParameterOverrideSelectableList, { const renderComponent = createComponentRenderer(ParameterOverrideSelectableList, {
pinia: createTestingPinia({ pinia: createTestingPinia({
initialState: { initialState: {

View File

@@ -2,7 +2,7 @@ import { renderComponent } from '@/__tests__/render';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
import ParameterInputWrapper from './ParameterInputWrapper.vue'; import ParameterInputWrapper from './ParameterInputWrapper.vue';
import { STORES } from '@n8n/stores'; import { STORES } from '@n8n/stores';
import { cleanupAppModals, createAppModals, SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils'; import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
import { waitFor } from '@testing-library/vue'; import { waitFor } from '@testing-library/vue';
vi.mock('@/composables/useWorkflowHelpers', () => { vi.mock('@/composables/useWorkflowHelpers', () => {
@@ -10,14 +10,6 @@ vi.mock('@/composables/useWorkflowHelpers', () => {
}); });
describe('ParameterInputWrapper.vue', () => { describe('ParameterInputWrapper.vue', () => {
beforeEach(() => {
createAppModals();
});
afterEach(() => {
cleanupAppModals();
});
test('should resolve expression', async () => { test('should resolve expression', async () => {
const { getByTestId } = renderComponent(ParameterInputWrapper, { const { getByTestId } = renderComponent(ParameterInputWrapper, {
pinia: createTestingPinia({ pinia: createTestingPinia({

View File

@@ -5,7 +5,7 @@ import {
} from './__tests__/utils/ResourceMapper.utils'; } from './__tests__/utils/ResourceMapper.utils';
import { useNodeTypesStore } from '@/stores/nodeTypes.store'; import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import type { MockedStore } from '@/__tests__/utils'; import type { MockedStore } from '@/__tests__/utils';
import { cleanupAppModals, createAppModals, mockedStore, waitAllPromises } from '@/__tests__/utils'; import { mockedStore, waitAllPromises } from '@/__tests__/utils';
import ResourceMapper from '@/components/ResourceMapper/ResourceMapper.vue'; import ResourceMapper from '@/components/ResourceMapper/ResourceMapper.vue';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
@@ -28,14 +28,12 @@ describe('ResourceMapper.vue', () => {
}); });
beforeEach(() => { beforeEach(() => {
createAppModals();
projectsStore = mockedStore(useProjectsStore); projectsStore = mockedStore(useProjectsStore);
projectsStore.currentProjectId = 'aProjectId'; projectsStore.currentProjectId = 'aProjectId';
}); });
afterEach(() => { afterEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
cleanupAppModals();
}); });
it('renders default configuration properly', async () => { it('renders default configuration properly', async () => {

View File

@@ -129,7 +129,6 @@ describe('RunDataJsonActions', () => {
beforeEach(cleanup); beforeEach(cleanup);
beforeAll(() => { beforeAll(() => {
document.body.innerHTML = '<div id="app-grid"></div>';
server = setupServer(); server = setupServer();
}); });

View File

@@ -2,12 +2,7 @@ import userEvent from '@testing-library/user-event';
import { createTestingPinia } from '@pinia/testing'; import { createTestingPinia } from '@pinia/testing';
import { waitFor, screen } from '@testing-library/vue'; import { waitFor, screen } from '@testing-library/vue';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { import { mockedStore, type MockedStore } from '@/__tests__/utils';
cleanupAppModals,
createAppModals,
mockedStore,
type MockedStore,
} from '@/__tests__/utils';
import { useUIStore } from '@/stores/ui.store'; import { useUIStore } from '@/stores/ui.store';
import { WHATS_NEW_MODAL_KEY, VERSIONS_MODAL_KEY } from '@/constants'; import { WHATS_NEW_MODAL_KEY, VERSIONS_MODAL_KEY } from '@/constants';
import { useVersionsStore } from '@/stores/versions.store'; import { useVersionsStore } from '@/stores/versions.store';
@@ -63,7 +58,6 @@ const currentVersion: Version = {
describe('WhatsNewModal', () => { describe('WhatsNewModal', () => {
beforeEach(() => { beforeEach(() => {
createAppModals();
createTestingPinia(); createTestingPinia();
uiStore = mockedStore(useUIStore); uiStore = mockedStore(useUIStore);
uiStore.modalsById = { uiStore.modalsById = {
@@ -132,7 +126,6 @@ describe('WhatsNewModal', () => {
}); });
afterEach(() => { afterEach(() => {
cleanupAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });

View File

@@ -9,7 +9,7 @@ import WorkflowSelectorParameterInput, {
type Props, type Props,
} from '@/components/WorkflowSelectorParameterInput/WorkflowSelectorParameterInput.vue'; } from '@/components/WorkflowSelectorParameterInput/WorkflowSelectorParameterInput.vue';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { cleanupAppModals, createAppModals, mockedStore } from '@/__tests__/utils'; import { mockedStore } from '@/__tests__/utils';
import { useProjectsStore } from '@/stores/projects.store'; import { useProjectsStore } from '@/stores/projects.store';
import { useWorkflowsStore } from '@/stores/workflows.store'; import { useWorkflowsStore } from '@/stores/workflows.store';
@@ -56,7 +56,6 @@ const workflowsStore = mockedStore(useWorkflowsStore);
describe('WorkflowSelectorParameterInput', () => { describe('WorkflowSelectorParameterInput', () => {
beforeEach(() => { beforeEach(() => {
createAppModals();
// Mock store methods to prevent unhandled errors // Mock store methods to prevent unhandled errors
workflowsStore.fetchWorkflowsPage.mockResolvedValue([]); workflowsStore.fetchWorkflowsPage.mockResolvedValue([]);
workflowsStore.totalWorkflowCount = 0; workflowsStore.totalWorkflowCount = 0;
@@ -71,7 +70,6 @@ describe('WorkflowSelectorParameterInput', () => {
}); });
afterEach(() => { afterEach(() => {
cleanupAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });

View File

@@ -1,6 +1,5 @@
import { createTestNode } from '@/__tests__/mocks'; import { createTestNode } from '@/__tests__/mocks';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { cleanupAppModals, createAppModals } from '@/__tests__/utils';
import { SET_NODE_TYPE } from '@/constants'; import { SET_NODE_TYPE } from '@/constants';
import { useNDVStore } from '@/stores/ndv.store'; import { useNDVStore } from '@/stores/ndv.store';
import { useNodeTypesStore } from '@/stores/nodeTypes.store'; import { useNodeTypesStore } from '@/stores/nodeTypes.store';
@@ -51,11 +50,6 @@ describe('ExperimentalNodeDetailsDrawer', () => {
}, },
]); ]);
ndvStore = useNDVStore(); ndvStore = useNDVStore();
createAppModals();
});
afterEach(() => {
cleanupAppModals();
}); });
it('should show updated parameter after closing NDV', async () => { it('should show updated parameter after closing NDV', async () => {

View File

@@ -13,7 +13,6 @@ describe('useToast', () => {
let toast: ReturnType<typeof useToast>; let toast: ReturnType<typeof useToast>;
beforeEach(() => { beforeEach(() => {
document.body.innerHTML = '<div id="app-grid"></div>';
createTestingPinia(); createTestingPinia();
toast = useToast(); toast = useToast();

View File

@@ -357,8 +357,6 @@ describe('Resolution-based completions', () => {
describe('secrets', () => { describe('secrets', () => {
const { $input } = mockProxy; const { $input } = mockProxy;
beforeEach(() => {});
test('should return completions for: {{ $secrets.| }}', () => { test('should return completions for: {{ $secrets.| }}', () => {
const provider = 'infisical'; const provider = 'infisical';
const secrets = ['SECRET']; const secrets = ['SECRET'];

View File

@@ -3,7 +3,7 @@ import { createTestingPinia } from '@pinia/testing';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import EvaluationsView from '@/views/Evaluations.ee/EvaluationsView.vue'; import EvaluationsView from '@/views/Evaluations.ee/EvaluationsView.vue';
import { cleanupAppModals, createAppModals, mockedStore } from '@/__tests__/utils'; import { mockedStore } from '@/__tests__/utils';
import { useEvaluationStore } from '@/stores/evaluation.store.ee'; import { useEvaluationStore } from '@/stores/evaluation.store.ee';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import type { TestRunRecord } from '@/api/evaluation.ee'; import type { TestRunRecord } from '@/api/evaluation.ee';
@@ -49,12 +49,10 @@ describe('EvaluationsView', () => {
beforeEach(() => { beforeEach(() => {
createTestingPinia(); createTestingPinia();
createAppModals();
}); });
afterEach(() => { afterEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
cleanupAppModals();
}); });
describe('Test Runs functionality', () => { describe('Test Runs functionality', () => {

View File

@@ -13,12 +13,7 @@ import {
import SettingsUsersView from '@/views/SettingsUsersView.vue'; import SettingsUsersView from '@/views/SettingsUsersView.vue';
import { createComponentRenderer } from '@/__tests__/render'; import { createComponentRenderer } from '@/__tests__/render';
import { useEmitters } from '@/__tests__/utils'; import { useEmitters } from '@/__tests__/utils';
import { import { mockedStore, type MockedStore } from '@/__tests__/utils';
cleanupAppModals,
createAppModals,
mockedStore,
type MockedStore,
} from '@/__tests__/utils';
import { useUsersStore } from '@/stores/users.store'; import { useUsersStore } from '@/stores/users.store';
import { useUIStore } from '@/stores/ui.store'; import { useUIStore } from '@/stores/ui.store';
import { useSettingsStore } from '@/stores/settings.store'; import { useSettingsStore } from '@/stores/settings.store';
@@ -112,7 +107,6 @@ let ssoStore: MockedStore<typeof useSSOStore>;
describe('SettingsUsersView', () => { describe('SettingsUsersView', () => {
beforeEach(() => { beforeEach(() => {
createAppModals();
renderComponent = createComponentRenderer(SettingsUsersView, { renderComponent = createComponentRenderer(SettingsUsersView, {
pinia: createTestingPinia(), pinia: createTestingPinia(),
}); });
@@ -151,7 +145,6 @@ describe('SettingsUsersView', () => {
}); });
afterEach(() => { afterEach(() => {
cleanupAppModals();
vi.clearAllMocks(); vi.clearAllMocks();
}); });