-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7c9d811
commit 586705f
Showing
8 changed files
with
304 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
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, 'selection', 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, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters