n8n/packages/editor-ui/src/__tests__/utils.ts

141 lines
4 KiB
TypeScript

import { within, waitFor } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import type { ISettingsState } from '@/Interface';
import { UserManagementAuthenticationMethod } from '@/Interface';
import { defaultSettings } from './defaults';
import { APP_MODALS_ELEMENT_ID } from '@/constants';
import type { Mock } from 'vitest';
import type { Store, StoreDefinition } from 'pinia';
import type { ComputedRef } from 'vue';
/**
* Retries the given assertion until it passes or the timeout is reached
*
* @example
* await retry(
* () => expect(screen.getByText('Hello')).toBeInTheDocument()
* );
*/
export const retry = async (assertion: () => void, { interval = 20, timeout = 1000 } = {}) => {
return await new Promise((resolve, reject) => {
const startTime = Date.now();
const tryAgain = () => {
setTimeout(() => {
try {
resolve(assertion());
} catch (error) {
if (Date.now() - startTime > timeout) {
reject(error);
} else {
tryAgain();
}
}
}, interval);
};
tryAgain();
});
};
export const waitAllPromises = async () => await new Promise((resolve) => setTimeout(resolve));
export const SETTINGS_STORE_DEFAULT_STATE: ISettingsState = {
initialized: true,
settings: defaultSettings,
userManagement: {
showSetupOnFirstLoad: false,
smtpSetup: false,
authenticationMethod: UserManagementAuthenticationMethod.Email,
quota: defaultSettings.userManagement.quota,
},
templatesEndpointHealthy: false,
api: {
enabled: false,
latestVersion: 0,
path: '/',
swaggerUi: {
enabled: false,
},
},
ldap: {
loginLabel: '',
loginEnabled: false,
},
saml: {
loginLabel: '',
loginEnabled: false,
},
mfa: {
enabled: false,
},
saveDataErrorExecution: 'all',
saveDataSuccessExecution: 'all',
saveDataProgressExecution: false,
saveManualExecutions: false,
};
export const getDropdownItems = async (dropdownTriggerParent: HTMLElement) => {
await userEvent.click(within(dropdownTriggerParent).getByRole('combobox'));
const selectTrigger = dropdownTriggerParent.querySelector(
'.select-trigger[aria-describedby]',
) as HTMLElement;
await waitFor(() => expect(selectTrigger).toBeInTheDocument());
const selectDropdownId = selectTrigger.getAttribute('aria-describedby');
const selectDropdown = document.getElementById(selectDropdownId as string) as HTMLElement;
await waitFor(() => expect(selectDropdown).toBeInTheDocument());
return selectDropdown.querySelectorAll('.el-select-dropdown__item');
};
export const getSelectedDropdownValue = async (items: NodeListOf<Element>) => {
const selectedItem = Array.from(items).find((item) => item.classList.contains('selected'));
expect(selectedItem).toBeInTheDocument();
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
*
* @see https://pinia.vuejs.org/cookbook/testing.html#Mocking-the-returned-value-of-an-action
*/
export const mockedStore = <TStoreDef extends () => unknown>(
useStore: TStoreDef,
): TStoreDef extends StoreDefinition<infer Id, infer State, infer Getters, infer Actions>
? Store<
Id,
State,
Record<string, never>,
{
[K in keyof Actions]: Actions[K] extends (...args: infer Args) => infer ReturnT
? Mock<(...args: Args) => ReturnT>
: Actions[K];
}
> & {
[K in keyof Getters]: Getters[K] extends ComputedRef<infer T> ? T : never;
}
: ReturnType<TStoreDef> => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return useStore() as any;
};
export type MockedStore<T extends () => unknown> = ReturnType<typeof mockedStore<T>>;