mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
323 lines
9.7 KiB
323 lines
9.7 KiB
// playwright-dev-page.ts |
|
import { Locator, expect } from "@playwright/test"; |
|
import { DashboardPage } from ".."; |
|
import BasePage from "../../Base"; |
|
import { CellPageObject } from "../common/Cell"; |
|
import { ColumnPageObject } from "./Column"; |
|
import { ToolbarPage } from "../common/Toolbar"; |
|
import { ProjectMenuObject } from "../common/ProjectMenu"; |
|
|
|
export class GridPage extends BasePage { |
|
readonly dashboard: DashboardPage; |
|
readonly addNewTableButton: Locator; |
|
readonly dashboardPage: DashboardPage; |
|
readonly column: ColumnPageObject; |
|
readonly cell: CellPageObject; |
|
readonly toolbar: ToolbarPage; |
|
readonly projectMenu: ProjectMenuObject; |
|
|
|
constructor(dashboardPage: DashboardPage) { |
|
super(dashboardPage.rootPage); |
|
this.dashboard = dashboardPage; |
|
this.addNewTableButton = dashboardPage.get().locator(".nc-add-new-table"); |
|
this.column = new ColumnPageObject(this); |
|
this.cell = new CellPageObject(this); |
|
this.toolbar = new ToolbarPage(this); |
|
this.projectMenu = new ProjectMenuObject(this); |
|
} |
|
|
|
get() { |
|
return this.dashboard.get().locator('[pw-data="nc-grid-wrapper"]'); |
|
} |
|
|
|
row(index: number) { |
|
return this.get().locator(`tr[data-pw="grid-row-${index}"]`); |
|
} |
|
|
|
async rowCount() { |
|
return await this.get().locator(".nc-grid-row").count(); |
|
} |
|
|
|
async verifyRowCount({ count }: { count: number }) { |
|
return await expect(this.get().locator(".nc-grid-row")).toHaveCount(count); |
|
} |
|
|
|
private async _fillRow({ |
|
index, |
|
columnHeader, |
|
value, |
|
}: { |
|
index: number; |
|
columnHeader: string; |
|
value: string; |
|
}) { |
|
const cell = this.cell.get({ index, columnHeader }); |
|
await this.cell.dblclick({ |
|
index, |
|
columnHeader, |
|
}); |
|
|
|
await cell.locator("input").fill(value); |
|
} |
|
|
|
async addNewRow({ |
|
index = 0, |
|
columnHeader = "Title", |
|
value, |
|
networkValidation = true, |
|
}: { |
|
index?: number; |
|
columnHeader?: string; |
|
value?: string; |
|
networkValidation?: boolean; |
|
} = {}) { |
|
const rowValue = value ?? `Row ${index}`; |
|
const rowCount = await this.get().locator(".nc-grid-row").count(); |
|
await this.get().locator(".nc-grid-add-new-cell").click(); |
|
|
|
await expect(this.get().locator(".nc-grid-row")).toHaveCount(rowCount + 1); |
|
|
|
await this._fillRow({ index, columnHeader, value: rowValue }); |
|
|
|
const clickOnColumnHeaderToSave = this.get() |
|
.locator(`[data-title="${columnHeader}"]`) |
|
.locator(`span[title="${columnHeader}"]`) |
|
.click(); |
|
|
|
if (networkValidation) { |
|
await this.waitForResponse({ |
|
uiAction: clickOnColumnHeaderToSave, |
|
requestUrlPathToMatch: "api/v1/db/data/noco", |
|
httpMethodsToMatch: ["POST"], |
|
responseJsonMatcher: (resJson) => resJson?.[columnHeader] === value, |
|
}); |
|
} else { |
|
await this.rootPage.waitForTimeout(300); |
|
} |
|
|
|
await this.dashboard.waitForLoaderToDisappear(); |
|
} |
|
|
|
async editRow({ |
|
index = 0, |
|
columnHeader = "Title", |
|
value, |
|
networkValidation = true, |
|
}: { |
|
index?: number; |
|
columnHeader?: string; |
|
value: string; |
|
networkValidation?: boolean; |
|
}) { |
|
await this._fillRow({ index, columnHeader, value }); |
|
|
|
const clickOnColumnHeaderToSave = this.get() |
|
.locator(`[data-title="${columnHeader}"]`) |
|
.locator(`span[title="${columnHeader}"]`) |
|
.click(); |
|
|
|
if (networkValidation) { |
|
await this.waitForResponse({ |
|
uiAction: clickOnColumnHeaderToSave, |
|
requestUrlPathToMatch: "api/v1/db/data/noco", |
|
httpMethodsToMatch: ["PATCH"], |
|
responseJsonMatcher: (resJson) => resJson?.[columnHeader] === value, |
|
}); |
|
} else { |
|
await this.rootPage.waitForTimeout(300); |
|
} |
|
|
|
await this.dashboard.waitForLoaderToDisappear(); |
|
} |
|
|
|
async verifyRow({ index }: { index: number }) { |
|
await this.get() |
|
.locator(`td[data-pw="cell-Title-${index}"]`) |
|
.waitFor({ state: "visible" }); |
|
await expect(this.get().locator(`td[data-pw="cell-Title-${index}"]`)).toHaveCount(1); |
|
} |
|
|
|
async verifyRowDoesNotExist({ index }: { index: number }) { |
|
await this.get() |
|
.locator(`td[data-pw="cell-Title-${index}"]`) |
|
.waitFor({ state: "hidden" }); |
|
return await expect(this.get().locator(`td[data-pw="cell-Title-${index}"]`)).toHaveCount(0); |
|
} |
|
|
|
async deleteRow(index: number) { |
|
await this.get().locator(`td[data-pw="cell-Title-${index}"]`).click({ |
|
button: "right", |
|
}); |
|
|
|
// Click text=Delete Row |
|
await this.rootPage.locator("text=Delete Row").click(); |
|
// todo: improve selector |
|
await this.rootPage |
|
.locator("span.ant-dropdown-menu-title-content > nc-project-menu-item") |
|
.waitFor({ state: "hidden" }); |
|
|
|
await this.rootPage.waitForTimeout(300); |
|
await this.dashboard.waitForLoaderToDisappear(); |
|
} |
|
|
|
async addRowRightClickMenu(index: number) { |
|
const rowCount = await this.get().locator(".nc-grid-row").count(); |
|
await this.get().locator(`td[data-pw="cell-Title-${index}"]`).click({ |
|
button: "right", |
|
}); |
|
// Click text=Insert New Row |
|
await this.rootPage.locator("text=Insert New Row").click(); |
|
await expect(await this.get().locator(".nc-grid-row")).toHaveCount(rowCount + 1); |
|
} |
|
|
|
async openExpandedRow({ index }: { index: number }) { |
|
await this.row(index).locator(`td[pw-data="cell-Id-${index}"]`).hover(); |
|
await this.row(index).locator(`div[pw-data="nc-expand-${index}"]`).click(); |
|
await ( |
|
await this.rootPage.locator(".ant-drawer-body").elementHandle() |
|
)?.waitForElementState("stable"); |
|
} |
|
|
|
async selectAll() { |
|
await this.get().locator('[pw-data="nc-check-all"]').hover(); |
|
|
|
await this.get() |
|
.locator('[pw-data="nc-check-all"]') |
|
.locator('input[type="checkbox"]') |
|
.check({ |
|
force: true, |
|
}); |
|
|
|
const rowCount = await this.rowCount(); |
|
for (let i = 0; i < rowCount; i++) { |
|
await expect( |
|
this.row(i).locator(`[pw-data="cell-Id-${i}"]`).locator("span.ant-checkbox-checked") |
|
).toHaveCount(1); |
|
} |
|
await this.rootPage.waitForTimeout(300); |
|
} |
|
|
|
async deleteAll() { |
|
await this.selectAll(); |
|
await this.get().locator('[pw-data="nc-check-all"]').nth(0).click({ |
|
button: "right", |
|
}); |
|
await this.rootPage.locator("text=Delete Selected Rows").click(); |
|
await this.dashboard.waitForLoaderToDisappear(); |
|
} |
|
|
|
private async pagination({ page }: { page: string }) { |
|
await this.get().locator(`.nc-pagination`).waitFor(); |
|
|
|
if (page === "<") |
|
return this.get().locator(".nc-pagination > .ant-pagination-prev"); |
|
if (page === ">") |
|
return this.get().locator(".nc-pagination > .ant-pagination-next"); |
|
|
|
return this.get().locator( |
|
`.nc-pagination > .ant-pagination-item.ant-pagination-item-${page}` |
|
); |
|
} |
|
|
|
async clickPagination({ page }: { page: string }) { |
|
await this.waitForResponse({ |
|
uiAction: (await this.pagination({ page })).click(), |
|
httpMethodsToMatch: ["GET"], |
|
requestUrlPathToMatch: "/views/", |
|
responseJsonMatcher: (resJson) => resJson?.pageInfo, |
|
}); |
|
|
|
await this.waitLoading(); |
|
} |
|
|
|
async verifyActivePage({ page }: { page: string }) { |
|
await expect(await this.pagination({ page })).toHaveClass( |
|
/ant-pagination-item-active/ |
|
); |
|
} |
|
|
|
async waitLoading() { |
|
await this.dashboard |
|
.get() |
|
.locator('[pw-data="grid-load-spinner"]') |
|
.waitFor({ state: "hidden" }); |
|
} |
|
|
|
async verifyEditDisabled({ |
|
columnHeader = "Title", |
|
}: { columnHeader?: string } = {}) { |
|
// double click to toggle to edit mode |
|
const cell = this.cell.get({ index: 0, columnHeader: columnHeader }); |
|
await this.cell.dblclick({ |
|
index: 0, |
|
columnHeader: columnHeader, |
|
}); |
|
await expect(await cell.locator("input")).not.toBeVisible(); |
|
|
|
// right click menu |
|
await this.get().locator(`td[data-pw="cell-${columnHeader}-0"]`).click({ |
|
button: "right", |
|
}); |
|
await expect( |
|
await this.rootPage.locator("text=Insert New Row") |
|
).not.toBeVisible(); |
|
|
|
// in cell-add |
|
await this.cell.get({ index: 0, columnHeader: "City List" }).hover(); |
|
await expect( |
|
await this.cell |
|
.get({ index: 0, columnHeader: "City List" }) |
|
.locator(".nc-action-icon.nc-plus") |
|
).not.toBeVisible(); |
|
|
|
// expand row |
|
await this.cell.get({ index: 0, columnHeader: "City List" }).hover(); |
|
await expect( |
|
await this.cell |
|
.get({ index: 0, columnHeader: "City List" }) |
|
.locator(".nc-action-icon >> nth=0") |
|
).not.toBeVisible(); |
|
} |
|
|
|
async verifyEditEnabled({ |
|
columnHeader = "Title", |
|
}: { columnHeader?: string } = {}) { |
|
// double click to toggle to edit mode |
|
const cell = this.cell.get({ index: 0, columnHeader: columnHeader }); |
|
await this.cell.dblclick({ |
|
index: 0, |
|
columnHeader: columnHeader, |
|
}); |
|
await expect(await cell.locator("input")).toBeVisible(); |
|
|
|
// right click menu |
|
await this.get().locator(`td[data-pw="cell-${columnHeader}-0"]`).click({ |
|
button: "right", |
|
}); |
|
await expect(await this.rootPage.locator("text=Insert New Row")).toBeVisible(); |
|
|
|
// in cell-add |
|
await this.cell.get({ index: 0, columnHeader: "City List" }).hover(); |
|
await expect( |
|
await this.cell |
|
.get({ index: 0, columnHeader: "City List" }) |
|
.locator(".nc-action-icon.nc-plus") |
|
).toBeVisible(); |
|
|
|
// expand row |
|
await this.cell.get({ index: 0, columnHeader: "City List" }).hover(); |
|
await expect( |
|
await this.cell |
|
.get({ index: 0, columnHeader: "City List" }) |
|
.locator(".nc-action-icon.nc-arrow-expand") |
|
).toBeVisible(); |
|
} |
|
|
|
async validateRoleAccess(param: { role: string }) { |
|
await this.column.verifyRoleAccess(param); |
|
await this.cell.verifyRoleAccess(param); |
|
await expect(this.get().locator(".nc-grid-add-new-cell")).toHaveCount( |
|
param.role === "creator" || param.role === "editor" ? 1 : 0 |
|
); |
|
} |
|
} |