import { HTMLElementDriver } from '@atomic-testing/component-driver-html';
import {
byCssClass,
byCssSelector,
byRole,
ComponentDriver,
ComponentDriverCtor,
IComponentDriverOption,
Interactor,
listHelper,
locatorUtil,
PartLocator,
ScenePart,
} from '@atomic-testing/core';
import { DataGridCellQuery } from './DataGridCellQuery';
import { DataGridHeaderRowDriver } from './DataGridHeaderRowDriver';
const parts = {
headerRow: {
locator: byCssClass('MuiDataGrid-columnHeaders').chain(byCssSelector('[role=row]:first-of-type')),
driver: DataGridHeaderRowDriver,
},
loading: {
locator: byRole('progressbar'),
driver: HTMLElementDriver,
},
} satisfies ScenePart;
const dataRowLocator = byCssSelector('[role=row][data-rowindex]');
/**
* Driver for Material UI v6 DataGridPro component.
* @see https://mui.com/x/react-data-grid/
*/
export class DataGridProDriver extends ComponentDriver {
constructor(locator: PartLocator, interactor: Interactor, option?: Partial) {
super(locator, interactor, {
...option,
parts,
});
}
/**
* Checks if the data grid is currently loading.
* @returns A promise that resolves to a boolean indicating if the data grid is loading.
*/
async isLoading(): Promise {
const result = await Promise.all([this.parts.loading.isVisible()]);
return result.some(v => v);
}
/**
* Waits for the data grid to exit the loading state.
* @param timeoutMs The maximum time to wait for the load to complete, in milliseconds.
*/
async waitForLoad(timeoutMs: number = 10000): Promise {
await this.parts.headerRow.waitUntilComponentState();
await this.waitUntil({ probeFn: () => this.isLoading(), terminateCondition: false, timeoutMs });
}
/**
* The number of columns currently displayed in the data grid, note that data grid pro
* uses virtualize rendering, therefore the column count heavily depends on the viewport size
* @returns The number of columns currently displayed in the data grid
*/
async getColumnCount(): Promise {
return this.parts.headerRow.getColumnCount();
}
/**
* The array text of the header row, note that columns not shown in the viewport may not be included because of virtualize rendering
* @returns The array of text of the header row
*/
async getHeaderText(): Promise {
return this.parts.headerRow.getRowText();
}
/**
* The number of rows currently displayed in the data grid, note that data grid pro
* uses virtualize rendering, therefore the row count heavily depends on the viewport size
* @returns The number of columns currently displayed in the data grid
*/
async getRowCount(): Promise {
const gridRowLocator = locatorUtil.append(this.locator, dataRowLocator);
let count = 0;
for await (const _ of listHelper.getListItemIterator(this, gridRowLocator, HTMLElementDriver)) {
count++;
}
return count;
}
/**
* Return the row driver for the row at the specified index, if the row does not exist, return null
* @param rowIndex
* @returns
*/
async getRow(rowIndex: number): Promise {
const rowLocator = locatorUtil.append(this.locator, byCssSelector(`[role=row][data-rowindex="${rowIndex}"]`));
const rowExists = await this.interactor.exists(rowLocator);
if (rowExists) {
return new DataGridHeaderRowDriver(rowLocator, this.interactor, this.commutableOption);
}
return null;
}
/**
* The array text of the specified row, note that columns not shown in the viewport may not be included because of virtualize rendering
* @param rowIndex The index of the row
* @returns The array of text of the specified row
*/
async getRowText(rowIndex: number): Promise {
const row = await this.getRow(rowIndex);
if (row != null) {
return row.getRowText();
}
throw new Error(`Row ${rowIndex} does not exist`);
}
/**
* Get the cell driver for the cell, if the cell does not exist, return null
* The cell driver is default to HTMLElementDriver, you can specify a different driver class
* @param query The query to locate the cell
* @param driverClass Optional, the driver class to use for the cell, default to HTMLElementDriver
* @returns
*/
async getCell(
query: DataGridCellQuery,
driverClass: ComponentDriverCtor = HTMLElementDriver as ComponentDriverCtor
): Promise {
await this.waitForLoad();
const rowDriver = await this.getRow(query.rowIndex);
if (rowDriver === null) {
return null;
}
if ('columnIndex' in query) {
return rowDriver.getCell(query.columnIndex, driverClass);
}
return rowDriver.getCell(query.columnField, driverClass);
}
/**
* Get the text content of the cell, if the cell does not exist, throw an error
* @param query The query to locate the cell
* @returns
*/
async getCellText(query: DataGridCellQuery): Promise {
const cell = await this.getCell(query);
if (cell != null) {
const text = await cell.getText();
return text!;
}
const columnIdentifier = 'columnIndex' in query ? query.columnIndex : query.columnField;
throw new Error(`Cell at row:${query.rowIndex} column:${columnIdentifier} does not exist`);
}
override get driverName(): string {
return 'MuiV6DataGridDriver';
}
}