UNPKG

@linzjs/step-ag-grid

Version:

[![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release) > Reusable [ag-grid](https://www.ag-grid.com/) component for LINZ / Toitū te whenua.

296 lines (254 loc) 10.8 kB
import { act, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { isEqual } from 'lodash-es'; import { expect } from 'vitest'; import { findQuick, getAllQuick, getMatcher, getQuick, IQueryQuick, queryQuick } from './testQuick'; let user = userEvent; /** * allow external userEvent to be used * @param customisedUserEvent */ export const setUpUserEvent = (customisedUserEvent: any) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment user = customisedUserEvent; }; export const countRows = (within?: HTMLElement): number => { return getAllQuick({ tagName: `div[row-id]:not(:empty)` }, within).length; }; export const findRowByIndex = async (rowIndex: number | string, within?: HTMLElement): Promise<HTMLDivElement> => { await waitFor(() => { expect(getAllQuick({ classes: '.ag-row' }).length > 0).toBe(true); }); //if this is not wrapped in an act console errors are logged during testing let row!: HTMLDivElement; await act(async () => { row = await findQuick<HTMLDivElement>( { tagName: `.ag-center-cols-container div[row-index='${rowIndex}']:not(:empty)` }, within, ); let combineChildren = [...Array.from(row.children)]; const leftCols = queryQuick<HTMLDivElement>( { tagName: `.ag-pinned-left-cols-container div[row-index='${rowIndex}']` }, within, ); if (leftCols) { combineChildren = [...Array.from(leftCols.children), ...combineChildren]; } const rightCols = queryQuick<HTMLDivElement>( { tagName: `.ag-pinned-right-cols-container div[row-index='${rowIndex}']` }, within, ); if (rightCols) { combineChildren = [...Array.from(rightCols.children), ...combineChildren]; } row.replaceChildren(...combineChildren); }); return row; }; export const findRow = async (rowId: number | string, within?: HTMLElement): Promise<HTMLDivElement> => { await waitFor(() => { expect(getAllQuick({ classes: '.ag-row' }).length > 0).toBe(true); }); //if this is not wrapped in an act console errors are logged during testing let row!: HTMLDivElement; await act(async () => { row = await findQuick<HTMLDivElement>( { tagName: `.ag-center-cols-container div[row-id='${rowId}']:not(:empty)` }, within, ); let combineChildren = [...Array.from(row.children)]; const leftCols = queryQuick<HTMLDivElement>( { tagName: `.ag-pinned-left-cols-container div[row-id='${rowId}']` }, within, ); if (leftCols) { combineChildren = [...Array.from(leftCols.children), ...combineChildren]; } const rightCols = queryQuick<HTMLDivElement>( { tagName: `.ag-pinned-right-cols-container div[row-id='${rowId}']` }, within, ); if (rightCols) { combineChildren = [...Array.from(rightCols.children), ...combineChildren]; } row.replaceChildren(...combineChildren); }); return row; }; export const queryRow = (rowId: number | string, within?: HTMLElement): HTMLDivElement | null => { return queryQuick<HTMLDivElement>({ tagName: `div[row-id='${rowId}']:not(:empty)` }, within); }; const _selectRow = async ( select: 'select' | 'deselect' | 'toggle', rowId: string | number, within?: HTMLElement, ): Promise<void> => { const row = await findRow(rowId, within); const isSelected = row.className.includes('ag-row-selected'); if (select === 'toggle' || (select === 'select' && !isSelected) || (select === 'deselect' && isSelected)) { const cell = await findCell(rowId, 'ag-Grid-SelectionColumn', within); await user.click(cell); await waitFor(async () => { const row = await findRow(rowId, within); const nowSelected = row.className.includes('ag-row-selected'); if (nowSelected === isSelected) throw `Row ${rowId} won't select`; }); } }; export const selectRow = async (rowId: string | number, within?: HTMLElement): Promise<void> => _selectRow('select', rowId, within); export const deselectRow = async (rowId: string | number, within?: HTMLElement): Promise<void> => _selectRow('deselect', rowId, within); export const findCell = async (rowId: number | string, colId: string, within?: HTMLElement): Promise<HTMLElement> => { const row = await findRow(rowId, within); return await findQuick({ tagName: `[col-id='${colId}']` }, row); }; export const findCellContains = async ( rowId: number | string, colId: string, text: string | RegExp, within?: HTMLElement, ) => { return await waitFor( async () => { const row = await findRow(rowId, within); return getQuick({ tagName: `[col-id='${colId}']`, text }, row); }, { timeout: 10000 }, ); }; export const selectCell = async (rowId: string | number, colId: string, within?: HTMLElement): Promise<void> => { const cell = await findCell(rowId, colId, within); await user.click(cell); }; export const editCell = async (rowId: number | string, colId: string, within?: HTMLElement): Promise<void> => { await waitFor( async () => { const cell = await findCell(rowId, colId, within); await user.dblClick(cell); await waitFor(findOpenPopover, { timeout: 1000 }); }, { timeout: 10000 }, ); }; export const isCellReadOnly = async (rowId: number | string, colId: string, within?: HTMLElement): Promise<boolean> => { const cell = await findCell(rowId, colId, within); return cell.className.includes('GridCell-readonly'); }; export const findOpenPopover = () => findQuick({ classes: '.szh-menu--state-open' }); export const queryMenuOption = async (menuOptionText: string | RegExp): Promise<HTMLElement | null> => { const openMenu = await findOpenPopover(); const els = await within(openMenu).findAllByRole('menuitem'); const matcher = getMatcher(menuOptionText); const result = els.find(matcher); return result ?? null; }; export const findMenuOption = async (menuOptionText: string | RegExp): Promise<HTMLElement> => { return await waitFor( async () => { const menuOption = await queryMenuOption(menuOptionText); if (menuOption == null) { throw Error(`Unable to find menu option ${menuOptionText}`); } return menuOption; }, { timeout: 5000 }, ); }; export const validateMenuOptions = async ( rowId: number | string, colId: string, expectedMenuOptions: Array<string>, ): Promise<boolean> => { await editCell(rowId, colId); const openMenu = await findOpenPopover(); const actualOptions = (await within(openMenu).findAllByRole('menuitem')).map((menuItem) => menuItem.textContent); return isEqual(actualOptions, expectedMenuOptions); }; export const clickMenuOption = async (menuOptionText: string | RegExp): Promise<void> => { const menuOption = await findMenuOption(menuOptionText); await user.click(menuOption); }; export const openAndClickMenuOption = async ( rowId: number | string, colId: string, menuOptionText: string | RegExp, within?: HTMLElement, ): Promise<void> => { await editCell(rowId, colId, within); await clickMenuOption(menuOptionText); }; export const openAndFindMenuOption = async ( rowId: number | string, colId: string, menuOptionText: string | RegExp, within?: HTMLElement, ): Promise<HTMLElement> => { await editCell(rowId, colId, within); return await findMenuOption(menuOptionText); }; export const getMultiSelectOptions = async () => { const openMenu = await findOpenPopover(); return getAllQuick<HTMLInputElement>({ role: 'menuitem', child: { tagName: 'input,textarea' } }, openMenu).map( (input) => { return { v: input.value, c: input.checked ?? true, }; }, ); }; export const findMultiSelectOption = async (value: string): Promise<HTMLElement> => { const openMenu = await findOpenPopover(); return getQuick({ role: 'menuitem', child: { tagName: `input[value='${value}']` } }, openMenu); }; export const clickMultiSelectOption = async (value: string): Promise<void> => { const menuItem = await findMultiSelectOption(value); menuItem.parentElement && (await user.click(menuItem.parentElement)); }; const typeInput = async (value: string, filter: IQueryQuick): Promise<void> => { const openMenu = await findOpenPopover(); const input = await findQuick(filter, openMenu); await user.clear(input); //'typing' an empty string will cause a console error, and it's also unnecessary after the previous clear call if (value.length > 0) { await user.type(input, value); } }; export const typeOnlyInput = async (value: string): Promise<void> => typeInput(value, { child: { tagName: "input[type='text'], textarea" } }); export const typeInputByLabel = async (value: string, labelText: string): Promise<void> => { const labels = getAllQuick({ child: { tagName: 'label' } }).filter((l) => l.textContent === labelText); if (labels.length === 0) { throw Error(`Label not found for text: ${labelText}`); } if (labels.length > 1) { throw Error(`Multiple labels found for text: ${labelText}`); } const inputId = labels[0].getAttribute('for'); await typeInput(value, { child: { tagName: `input[id='${inputId}'], textarea[id='${inputId}']` } }); }; export const typeInputByPlaceholder = async (value: string, placeholder: string): Promise<void> => typeInput(value, { child: { tagName: `input[placeholder='${placeholder}'], textarea[placeholder='${placeholder}']` }, }); export const typeOtherInput = async (value: string): Promise<void> => typeInput(value, { classes: '.subComponent', child: { tagName: "input[type='text']" } }); export const typeOtherTextArea = async (value: string): Promise<void> => typeInput(value, { classes: '.subComponent', child: { tagName: 'textarea' } }); export const closeMenu = () => user.click(document.body); export const closePopover = () => user.click(document.body); export const findActionButton = (text: string, container?: HTMLElement): Promise<HTMLElement> => findQuick({ tagName: 'button', child: { classes: '.ActionButton-minimalAreaDisplay', text: text } }, container); export const clickActionButton = async (text: string, container?: HTMLElement): Promise<void> => { const button = await findActionButton(text, container); await user.click(button); }; export const waitForGridReady = async (props?: { grid?: HTMLElement; timeout?: number }) => waitFor(() => expect(getAllQuick({ classes: '.Grid-ready' }, props?.grid)).toBeDefined(), { timeout: props?.timeout ?? 5000, }); export const waitForGridRows = async (props?: { grid?: HTMLElement; timeout?: number }) => waitFor(() => expect(getAllQuick({ classes: '.ag-row' }, props?.grid).length > 0).toBe(true), { timeout: props?.timeout ?? 5000, });