cypress-ag-grid
Version:
Cypress plugin to interact with ag grid
612 lines (553 loc) • 20.5 kB
JavaScript
/// <reference types="cypress" />
import {
extractAgGridData,
extractAgGridElements,
filterOperator,
filterTab,
sort,
waitForAgGridAnimation,
} from "@kpmck/ag-grid-core";
function getSingleAgGridRootElement(agGridElement) {
const rootElements = agGridElement.get();
if (rootElements.length < 1) {
throw new Error(`Couldn't find the element ${agGridElement}`);
}
if (rootElements.length > 1) {
throw new Error(
`Selector "${agGridElement.selector}" returned more than 1 element.`
);
}
return rootElements[0];
}
export const agGridWaitForAnimation = async (agGridElement) => {
await waitForAgGridAnimation(getSingleAgGridRootElement(agGridElement));
return agGridElement;
};
/**
* Retrieves the values from the *displayed* page in ag grid and assigns each value to its respective column name.
* @param agGridElement The get() selector for which ag grid table you wish to retrieve.
* @param options Provide an array of columns you wish to exclude from the table retrieval.
*/
export const getAgGridData = async (agGridElement, options = {}) => {
return extractAgGridData(getSingleAgGridRootElement(agGridElement), options);
};
/**
* Retrieves the values from the *displayed* page in ag grid and assigns each value to its respective column name.
* @param agGridElement The get() selector for which ag grid table you wish to retrieve.
* @param options Provide an array of columns you wish to exclude from the table retrieval.
*/
export const getAgGridElements = async (agGridElement, options = {}) => {
return extractAgGridElements(
getSingleAgGridRootElement(agGridElement),
options
);
};
/**
* Retrieve the ag grid column header element based on its column name value
* @param columnName The name of the column's header to retrieve.
*/
function getColumnHeaderElement(agGridElement, columnName) {
return cy
.get(agGridElement)
.find(".ag-header-cell-text")
.contains(new RegExp("^" + columnName + "$", "g"));
}
/**
* * Performs sorting operation on the specified column
* @param {*} agGridElement The get() selector for which ag grid table you wish to retrieve.
* @param columnName The name of the column you wish to sort
* @param sortDirection sort enum value
* @returns
*/
export function sortColumnBy(agGridElement, columnName, sortDirection) {
if (sortDirection.toLowerCase() === "ascending") {
sortDirection = "asc";
} else if (sortDirection.toLowerCase() === "descending") {
sortDirection = "desc";
}
if (sortDirection === sort.ascending || sortDirection === sort.descending) {
return getColumnHeaderElement(agGridElement, columnName)
.parents(".ag-header-cell .ag-cell-label-container")
.invoke("attr", "class")
.then((value) => {
cy.log(`sort: ${sortDirection}`);
if (!value.includes(`ag-header-cell-sorted-${sortDirection}`)) {
getColumnHeaderElement(agGridElement, columnName).click();
sortColumnBy(agGridElement, columnName, sortDirection);
}
});
} else {
throw new Error("sortDirection must be either 'asc' or 'desc'.");
}
}
function getMenuTabElement(agGridElement, tab) {
return cy
.get(agGridElement)
.find(".ag-tab")
.find(`.ag-icon-${tab}`)
.filter(":visible");
}
/**
* Will select the specified filter tab if it is not already selected
* @param tab
*/
function selectMenuTab(agGridElement, tab) {
cy.get(agGridElement).then((agGr) => {
if (agGr.find('.ag-menu-list').length > 0) {
cy.log('Menu uses a list, not tabs');
} else {
getMenuTabElement(agGridElement, tab).then(($ele) => {
cy.wrap($ele)
.parent("span")
.invoke("attr", "class")
.then(($attr) => {
if (!$attr.includes("selected")) {
cy.wrap($ele).click();
}
});
});
}
})
}
/**
* Returns the filter button element for a specified column
* @param columnName
*/
function getFilterColumnButtonElement(
agGridElement,
columnName,
isFloatingFilter = false
) {
if (isFloatingFilter) {
return getColumnHeaderElement(agGridElement, columnName)
.parents(".ag-header-cell")
.then(($headerCell) => {
const columnIndex = $headerCell.attr("aria-colindex");
const visibleHeaderCells = $headerCell
.closest(".ag-header")
.find(".ag-header-row-column .ag-header-cell:visible");
const headerPosition = visibleHeaderCells.index($headerCell);
return cy.get(agGridElement).then(($gridElement) => {
const usesV35FloatingFilterRow =
$gridElement.find(".ag-header-row-filter").length > 0;
let floatingFilterButton;
if (usesV35FloatingFilterRow) {
floatingFilterButton = $gridElement.find(
`.ag-header-row-filter .ag-header-cell[aria-colindex="${columnIndex}"] .ag-floating-filter-button:visible`
);
if (!floatingFilterButton.length && headerPosition > -1) {
floatingFilterButton = $gridElement
.find(".ag-header-row-filter .ag-floating-filter-button:visible")
.eq(headerPosition);
}
} else {
floatingFilterButton = $gridElement.find(
`.ag-header-row-column-filter .ag-header-cell[aria-colindex="${columnIndex}"] .ag-floating-filter-button-button:visible`
);
if (!floatingFilterButton.length && headerPosition > -1) {
floatingFilterButton = $gridElement
.find(
".ag-header-row-column-filter .ag-floating-filter-button-button:visible"
)
.eq(headerPosition);
}
}
return cy.wrap(floatingFilterButton.first());
});
});
} else {
return getColumnHeaderElement(agGridElement, columnName)
.parent()
.siblings(".ag-header-cell-filter-button");
}
}
/**
*
* @param filterValue value to input into the filter textbox
* @param operator (optional) use if using a search operator (i.e. Less Than, Equals, etc...use filterOperator.enum values)
* @param noMenuTabs (optional) boolean indicating if the menu has tabs.
*/
function filterBySearchTerm(agGridElement, options) {
const filterValue = options.searchCriteria.filterValue;
const operator = options.searchCriteria.operator;
const searchInputIndex = options.searchCriteria.searchInputIndex || 0;
const operatorIndex =
options.searchCriteria.operatorIndex ??
(operator === filterOperator.inRange ? 0 : searchInputIndex);
const isMultiFilter = options.searchCriteria.isMultiFilter;
const noMenuTabs = options.noMenuTabs;
// Navigate to the filter tab
// if (!noMenuTabs) {
// selectMenuTab(agGridElement, filterTab.filter);
// }
if (operator) {
const elem = cy
.get(agGridElement)
.find(".ag-filter")
.find(".ag-picker-field-wrapper")
.filter(":visible")
.eq(operatorIndex);
cy.get(agGridElement).agGridWaitForAnimation();
elem.click();
cy.get(agGridElement)
.find(".ag-popup .ag-list")
.find("span")
.contains(operator)
.click();
}
// Input filter term and allow grid a moment to render the results
if (
operator !== filterOperator.blank &&
operator !== filterOperator.notBlank
) {
cy.get(agGridElement)
.find(".ag-popup-child")
.find("input")
.filter(":visible")
.as("filterInput");
}
// If it's a multi filter, de-select the 'select-all' checkbox
if (isMultiFilter) {
const selectAllText = options.selectAllLocaleText || "(Select All)";
toggleColumnCheckboxFilter(agGridElement, selectAllText, false, true);
}
// Get the saved filter input and enter the search term
if (
operator !== filterOperator.blank &&
operator !== filterOperator.notBlank
) {
cy.get("@filterInput").then(($ele) => {
cy.wrap($ele).eq(searchInputIndex).clear().type(filterValue + '{enter}');
});
}
// Finally, if a multi-filter, select the filter value's checkbox
if (isMultiFilter) {
toggleColumnCheckboxFilter(agGridElement, filterValue, true, true);
}
}
function applyColumnFilter(agGridElement, hasApplyButton, noMenuTabs) {
if (hasApplyButton) {
cy.get(agGridElement)
.find(".ag-filter-apply-panel-button")
.contains("Apply")
.click();
}
if (!noMenuTabs) {
cy.get(agGridElement).then((agGr) => {
if (agGr.find('.ag-tab').length === 0) {
cy.log('Menu uses a list, not tabs');
cy.get(agGridElement).agGridWaitForAnimation();
} else {
getMenuTabElement(agGridElement, filterTab.filter).click();
}
})
}
}
/**
* Either toggle
* @param filterValue
* @param doSelect
* @param hasTabs
*/
function toggleColumnCheckboxFilter(
agGridElement,
filterValue,
doSelect,
noMenuTabs = false
) {
// if (!noMenuTabs) {
// selectMenuTab(agGridElement, filterTab.filter);
// }
cy.get(agGridElement)
.find(".ag-input-field-label")
.contains(filterValue)
.siblings("div")
.find("input")
.then(($ele) => {
if (doSelect) cy.wrap($ele).check();
else cy.wrap($ele).uncheck();
});
}
function populateSearchCriteria(
searchCriteria,
hasApplyButton = false,
noMenuTabs = false,
selectAllLocaleText = "(Select All)"
) {
const options = {};
options.searchCriteria = { ...searchCriteria };
options.selectAllLocaleText = selectAllLocaleText;
options.hasApplyButton = hasApplyButton;
options.noMenuTabs = noMenuTabs;
return options;
}
/**
* Will add or remove a column from ag grid.
* @param columnName The column name to add/remove
* @param pin 'left', 'right' or null
*/
export function pinColumn(agGridElement, columnName, pin) {
getColumnHeaderElement(agGridElement, columnName)
.parent()
.siblings(".ag-header-cell-menu-button")
.click();
selectMenuTab(agGridElement, filterTab.general);
cy.get(agGridElement).find(".ag-menu-option").contains("Pin Column").click();
var selectedOption;
switch (pin) {
case "left":
selectedOption = "Pin Left";
break;
case "right":
selectedOption = "Pin Right";
break;
default:
selectedOption = "No Pin";
break;
}
cy.get(agGridElement)
.find(".ag-menu-option")
.contains(selectedOption)
.click();
}
/**
* * Performs a filter operation on the specified column via the context menu using plain text search
* @param agGridElement The get() selector for which ag grid table you wish to retrieve.
* @param {{searchCriteria:[{columnName:string,filterValue:string,operator?:string}], hasApplyButton?:boolean}} options JSON with search properties
* @param options.searchCriteria JSON with search properties
* @param options.searchCriteria.columnName [REQUIRED] name of the column to filter
* @param options.searchCriteria.filterValue [REQUIRED] value to input into the filter textbox
* @param options.searchCriteria.operator [Optional] Use if using a search operator (i.e. Less Than, Equals, etc...use filterOperator.enum values).
* @param options.hasApplyButton [Optional] True if "Apply" button is used, false if filters by text input automatically.
* @param options.noMenuTabs [Optional] True if you use for example the community edition of ag-grid, which has no menu tabs
*/
export function filterBySearchTextColumnMenu(agGridElement, options) {
// Check if there are multiple search criteria provided by attempting to access the columnName
if (!options.searchCriteria.columnName) {
options.searchCriteria.forEach((_searchCriteria) => {
const _options = populateSearchCriteria(
_searchCriteria,
options.hasApplyButton,
options.noMenuTabs,
options.isMultiFilter
);
_filterBySearchTextColumnMenu(agGridElement, _options);
});
} else {
_filterBySearchTextColumnMenu(agGridElement, options);
}
}
function _filterBySearchTextColumnMenu(agGridElement, options) {
// Get the header's menu element
getFilterColumnButtonElement(
agGridElement,
options.searchCriteria.columnName
).click();
filterBySearchTerm(agGridElement, options);
applyColumnFilter(agGridElement, options.hasApplyButton, options.noMenuTabs);
}
/**
* * Performs a filter operation on the specified column via the column's floating filter field using plain text search
* @param agGridElement The get() selector for which ag grid table you wish to retrieve.
* @param {{searchCriteria:[{columnName:string,filterValue:string,operator?:string}], hasApplyButton?:boolean, noMenuTab?:boolean, selectAllLocaleText:string}} options JSON with search properties
* @param options.searchCriteria JSON with search properties and options
* @param options.searchCriteria.columnName name of the column to filter
* @param options.searchCriteria.filterValue value to input into the filter textbox
* @param options.searchCriteria.searchInputIndex [Optional] Uses 0 by default. Index of which filter box to use in event of having multiple search conditionals
* @param options.searchCriteria.operator [Optional] Use if using a search operator (i.e. Less Than, Equals, etc...use filterOperator.enum values).
* @param options.hasApplyButton [Optional] True if "Apply" button is used, false if filters by text input automatically.
* @param options.noMenuTabs [Optional] True if you use, for example, the community edition of ag-grid, which has no menu tabs
* @param options.selectAllLocaleText [Optional] Pass in the locale text value of "(Select All)" for when you are filtering by checkbox - this wil first deselect the "(Select All)" option before selecting your filter value
*/
export function filterBySearchTextColumnFloatingFilter(agGridElement, options) {
// Check if there are multiple search criteria provided by attempting to access the columnName
if (!options.searchCriteria.columnName) {
groupFloatingFilterSearchCriteria(
normalizeFloatingFilterSearchCriteria(options.searchCriteria)
).forEach((searchCriteriaGroup) => {
const criteriaOptions = searchCriteriaGroup.map((_searchCriteria) =>
populateSearchCriteria(
_searchCriteria,
options.hasApplyButton,
options.noMenuTabs
)
);
if (criteriaOptions.length === 1) {
_filterBySearchTextColumnFloatingFilter(agGridElement, criteriaOptions[0]);
return;
}
_filterBySearchTextColumnFloatingFilterGroup(agGridElement, criteriaOptions);
});
} else {
_filterBySearchTextColumnFloatingFilter(agGridElement, options);
}
}
function normalizeFloatingFilterSearchCriteria(searchCriteria) {
const betweenInputIndexes = new Map();
return searchCriteria.map((criteria) => {
if (
criteria.operator !== filterOperator.inRange ||
criteria.searchInputIndex !== undefined
) {
return criteria;
}
const criteriaKey = `${criteria.columnName}::${criteria.operator}`;
const nextInputIndex = betweenInputIndexes.get(criteriaKey) || 0;
betweenInputIndexes.set(criteriaKey, nextInputIndex + 1);
return {
...criteria,
searchInputIndex: nextInputIndex,
};
});
}
function groupFloatingFilterSearchCriteria(searchCriteria) {
const groupedCriteria = [];
searchCriteria.forEach((criteria) => {
const lastGroup = groupedCriteria[groupedCriteria.length - 1];
if (
criteria.operator === filterOperator.inRange &&
lastGroup &&
lastGroup[0].columnName === criteria.columnName &&
lastGroup[0].operator === criteria.operator
) {
lastGroup.push(criteria);
return;
}
groupedCriteria.push([criteria]);
});
return groupedCriteria;
}
function _filterBySearchTextColumnFloatingFilter(agGridElement, options) {
cy.get(agGridElement).then((agGridElement) => {
getFilterColumnButtonElement(
agGridElement,
options.searchCriteria.columnName,
true
).click();
filterBySearchTerm(agGridElement, options);
applyColumnFilter(
agGridElement,
options.hasApplyButton,
options.noMenuTabs
);
});
}
function _filterBySearchTextColumnFloatingFilterGroup(
agGridElement,
criteriaOptions
) {
cy.get(agGridElement).then((agGridElement) => {
getFilterColumnButtonElement(
agGridElement,
criteriaOptions[0].searchCriteria.columnName,
true
).click();
criteriaOptions.forEach((criteriaOption, index) => {
const searchCriteria =
index === 0
? criteriaOption.searchCriteria
: { ...criteriaOption.searchCriteria, operator: undefined };
filterBySearchTerm(agGridElement, {
...criteriaOption,
searchCriteria,
});
});
applyColumnFilter(
agGridElement,
criteriaOptions[0].hasApplyButton,
criteriaOptions[0].noMenuTabs
);
});
}
/**
* * Performs a filter operation on the specified column and selects only the provided filterValue
* @param agGridElement The get() selector for which ag grid table you wish to retrieve.
* @param {{searchCriteria:[{columnName:string,filterValue:string], hasApplyButton?:boolean}} options JSON with search values and options
* @param options.searchCriteria [REQUIRED] JSON with search properties
* @param options.searchCriteria.columnName [REQUIRED] name of the column to filter
* @param options.searchCriteria.filterValue [REQUIRED] value to input into the filter textbox
* @param options.hasApplyButton [Optional] True if "Apply" button is used, false if filters by text input automatically.
* @param options.noMenuTabs [Optional] True if you use for example the community edition of ag-grid, which has no menu tabs
*/
export function filterByCheckboxColumnMenu(agGridElement, options) {
// Check if there are multiple search criteria provided by attempting to access the columnName
if (!options.searchCriteria.columnName) {
options.searchCriteria.forEach((_searchCriteria) => {
const _options = populateSearchCriteria(
_searchCriteria,
options.hasApplyButton,
options.noMenuTabs,
options.selectAllLocaleText
);
_filterByCheckboxColumnMenu(agGridElement, _options);
});
} else {
_filterByCheckboxColumnMenu(agGridElement, options);
}
}
function _filterByCheckboxColumnMenu(agGridElement, options) {
cy.get(agGridElement).then((agGridElement) => {
getFilterColumnButtonElement(
agGridElement,
options.searchCriteria.columnName
).click();
const selectAllText = options.selectAllLocaleText || "(Select All)";
toggleColumnCheckboxFilter(
agGridElement,
selectAllText,
false,
options.noMenuTabs
);
toggleColumnCheckboxFilter(
agGridElement,
options.searchCriteria.filterValue,
true,
options.noMenuTabs
);
applyColumnFilter(
agGridElement,
options.hasApplyButton,
options.noMenuTabs
);
});
}
/**
* Will perform a filter for all search criteria provided, then selects all found entries in the grid
* @param searchCriteria a "\^" delimited string of all columns and searchCriteria to search for in the grid (i.e. "Name=John Smith^Rate Plan=Standard"
*/
export function filterGridEntriesBySearchText(
agGridElement,
searchCriteria,
isFloatingFilter = false
) {
if (isFloatingFilter) {
filterBySearchTextColumnFloatingFilter(agGridElement, searchCriteria);
} else {
filterBySearchTextColumnMenu(agGridElement, searchCriteria);
}
}
/**
* Will add or remove a column from ag grid.
* @param columnName The column name to add/remove
* @param doRemove true will remove the column. false will add the column.
*/
export function toggleColumnFromSideBar(agGridElement, columnName, doRemove) {
cy.get(agGridElement)
.find(".ag-column-select-header-filter-wrapper")
.find("input")
.then(($columnFilterInputField) => {
if (!$columnFilterInputField.is(":visible")) {
cy.get(".ag-side-buttons").find("span").contains("Columns").click();
}
cy.get(agGridElement).agGridWaitForAnimation();
cy.wrap($columnFilterInputField).clear().type(columnName);
cy.get(".ag-column-select-column-label")
.contains(columnName)
.parent()
.find("input")
.then(($columnCheckbox) => {
if (doRemove) cy.wrap($columnCheckbox).uncheck();
else cy.wrap($columnCheckbox).check();
});
});
}