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.
238 lines
8.4 KiB
238 lines
8.4 KiB
import { expect } from '@playwright/test'; |
|
import BasePage from '../../../Base'; |
|
import { ToolbarFieldsPage } from './Fields'; |
|
import { ToolbarSortPage } from './Sort'; |
|
import { ToolbarFilterPage } from './Filter'; |
|
import { ToolbarShareViewPage } from './ShareView'; |
|
import { ToolbarViewMenuPage } from './ViewMenu'; |
|
import * as fs from 'fs'; |
|
import { GridPage } from '../../Grid'; |
|
import { ToolbarActionsPage } from './Actions'; |
|
import { GalleryPage } from '../../Gallery'; |
|
import { KanbanPage } from '../../Kanban'; |
|
import { FormPage } from '../../Form'; |
|
import { ToolbarStackbyPage } from './StackBy'; |
|
import { ToolbarAddEditStackPage } from './AddEditKanbanStack'; |
|
import { ToolbarSearchDataPage } from './SearchData'; |
|
import { RowHeight } from './RowHeight'; |
|
import { MapPage } from '../../Map'; |
|
import { getTextExcludeIconText } from '../../../../tests/utils/general'; |
|
|
|
export class ToolbarPage extends BasePage { |
|
readonly parent: GridPage | GalleryPage | FormPage | KanbanPage | MapPage; |
|
readonly fields: ToolbarFieldsPage; |
|
readonly sort: ToolbarSortPage; |
|
readonly filter: ToolbarFilterPage; |
|
readonly shareView: ToolbarShareViewPage; |
|
readonly viewsMenu: ToolbarViewMenuPage; |
|
readonly actions: ToolbarActionsPage; |
|
readonly stackBy: ToolbarStackbyPage; |
|
readonly addEditStack: ToolbarAddEditStackPage; |
|
readonly searchData: ToolbarSearchDataPage; |
|
readonly rowHeight: RowHeight; |
|
|
|
constructor(parent: GridPage | GalleryPage | FormPage | KanbanPage | MapPage) { |
|
super(parent.rootPage); |
|
this.parent = parent; |
|
this.fields = new ToolbarFieldsPage(this); |
|
this.sort = new ToolbarSortPage(this); |
|
this.filter = new ToolbarFilterPage(this); |
|
this.shareView = new ToolbarShareViewPage(this); |
|
this.viewsMenu = new ToolbarViewMenuPage(this); |
|
this.actions = new ToolbarActionsPage(this); |
|
this.stackBy = new ToolbarStackbyPage(this); |
|
this.addEditStack = new ToolbarAddEditStackPage(this); |
|
this.searchData = new ToolbarSearchDataPage(this); |
|
this.rowHeight = new RowHeight(this); |
|
} |
|
|
|
get() { |
|
return this.rootPage.locator(`.nc-table-toolbar`); |
|
} |
|
|
|
async clickActions() { |
|
const menuOpen = await this.actions.get().isVisible(); |
|
|
|
await this.get().locator(`button.nc-actions-menu-btn`).click(); |
|
|
|
// Wait for the menu to close |
|
if (menuOpen) await this.fields.get().waitFor({ state: 'hidden' }); |
|
} |
|
|
|
async clickFields() { |
|
const menuOpen = await this.fields.get().isVisible(); |
|
|
|
await this.get().locator(`button.nc-fields-menu-btn`).click(); |
|
|
|
// Wait for the menu to close |
|
if (menuOpen) await this.fields.get().waitFor({ state: 'hidden' }); |
|
} |
|
|
|
async clickFindRowByScanButton() { |
|
await this.get().locator(`button.nc-btn-find-row-by-scan`).click(); |
|
} |
|
|
|
async clickSort() { |
|
const menuOpen = await this.sort.get().isVisible(); |
|
|
|
await this.get().locator(`button.nc-sort-menu-btn`).click(); |
|
|
|
// Wait for the menu to close |
|
if (menuOpen) await this.sort.get().waitFor({ state: 'hidden' }); |
|
} |
|
|
|
async verifyFieldsButtonIsVisibleWithTextAndIcon() { |
|
await expect(this.get().locator(`button.nc-fields-menu-btn`)).toBeVisible(); |
|
|
|
// menu text |
|
const fieldLocator = await this.get().locator(`button.nc-fields-menu-btn`); |
|
const fieldText = await getTextExcludeIconText(fieldLocator); |
|
await expect(fieldText).toBe('Fields'); |
|
|
|
// icons count within fields menu button |
|
expect(await this.get().locator(`button.nc-fields-menu-btn`).locator(`.material-symbols-outlined`).count()).toBe(2); |
|
} |
|
|
|
async verifyFieldsButtonIsVisibleWithoutTextButIcon() { |
|
await expect(this.get().locator(`button.nc-fields-menu-btn`)).toBeVisible(); |
|
|
|
// menu text |
|
const fieldLocator = await this.get().locator(`button.nc-fields-menu-btn`); |
|
const fieldText = await getTextExcludeIconText(fieldLocator); |
|
await expect(fieldText).not.toBe('Fields'); |
|
|
|
// icons count within fields menu button |
|
expect(await this.get().locator(`button.nc-fields-menu-btn`).locator(`.material-symbols-outlined`).count()).toBe(2); |
|
} |
|
|
|
async clickFilter({ |
|
// `networkValidation` is used to verify that api calls are made when the button is clicked |
|
// which happens when the filter is opened for the first time |
|
networkValidation, |
|
}: { networkValidation?: boolean } = {}) { |
|
const menuOpen = await this.filter.get().isVisible(); |
|
|
|
const clickFilterAction = () => this.get().locator(`button.nc-filter-menu-btn`).click(); |
|
// Wait for the menu to close |
|
if (menuOpen) { |
|
await clickFilterAction(); |
|
await this.filter.get().waitFor({ state: 'hidden' }); |
|
} else { |
|
if (networkValidation) { |
|
// Since on opening filter menu, api is called to fetch filter options, and will rerender the menu |
|
await this.waitForResponse({ |
|
uiAction: clickFilterAction, |
|
requestUrlPathToMatch: '/api/v1/db', |
|
httpMethodsToMatch: ['GET'], |
|
}); |
|
} else { |
|
await clickFilterAction(); |
|
} |
|
} |
|
} |
|
|
|
async clickShareView() { |
|
const menuOpen = await this.shareView.get().isVisible(); |
|
await this.get().locator(`button.nc-btn-share-view `).click(); |
|
|
|
// Wait for the menu to close |
|
if (menuOpen) await this.shareView.get().waitFor({ state: 'hidden' }); |
|
} |
|
|
|
async clickStackByField() { |
|
await this.get().locator(`.nc-toolbar-btn.nc-kanban-stacked-by-menu-btn`).click(); |
|
} |
|
|
|
async clickAddNewRow() { |
|
await this.get().locator(`.nc-toolbar-btn.nc-add-new-row-btn`).click(); |
|
} |
|
|
|
async clickDownload(type: string, verificationFile = 'expectedData.txt') { |
|
await this.get().locator(`.nc-toolbar-btn.nc-actions-menu-btn`).click(); |
|
|
|
const [download] = await Promise.all([ |
|
// Start waiting for the download |
|
this.rootPage.waitForEvent('download'), |
|
// Perform the action that initiates download |
|
this.rootPage |
|
.locator(`.nc-dropdown-actions-menu`) |
|
.locator(`li.ant-dropdown-menu-item:has-text("${type}")`) |
|
.click(), |
|
]); |
|
|
|
// Save downloaded file somewhere |
|
await download.saveAs('./output/at.txt'); |
|
|
|
// verify downloaded content against expected content |
|
const expectedData = fs.readFileSync(`./fixtures/${verificationFile}`, 'utf8').replace(/\r/g, '').split('\n'); |
|
const file = fs.readFileSync('./output/at.txt', 'utf8').replace(/\r/g, '').split('\n'); |
|
await expect(file).toEqual(expectedData); |
|
} |
|
|
|
async clickRowHeight() { |
|
// ant-btn nc-height-menu-btn nc-toolbar-btn |
|
await this.get().locator(`.nc-toolbar-btn.nc-height-menu-btn`).click(); |
|
} |
|
|
|
async verifyStackByButton({ title }: { title: string }) { |
|
await this.get().locator(`.nc-toolbar-btn.nc-kanban-stacked-by-menu-btn`).waitFor({ state: 'visible' }); |
|
await expect( |
|
await this.get().locator(`.nc-toolbar-btn.nc-kanban-stacked-by-menu-btn:has-text("${title}")`) |
|
).toBeVisible(); |
|
} |
|
|
|
async verifyDownloadDisabled() { |
|
await this.get().locator(`.nc-toolbar-btn.nc-actions-menu-btn`).waitFor({ state: 'hidden' }); |
|
} |
|
|
|
async clickAddEditStack() { |
|
await this.get().locator(`.nc-kanban-add-edit-stack-menu-btn`).click(); |
|
} |
|
|
|
async validateViewsMenu(param: { role: string; mode?: string }) { |
|
let menuItems = { |
|
creator: ['Download', 'Upload', 'Shared View List', 'Webhooks', 'Get API Snippet', 'ERD View'], |
|
editor: ['Download', 'Upload', 'Get API Snippet', 'ERD View'], |
|
commenter: ['Download as CSV', 'Download as XLSX'], |
|
viewer: ['Download as CSV', 'Download as XLSX'], |
|
}; |
|
|
|
if (param.mode === 'shareBase') { |
|
menuItems = { |
|
creator: [], |
|
editor: ['Download', 'Upload', 'ERD View'], |
|
commenter: [], |
|
viewer: ['Download as CSV', 'Download as XLSX'], |
|
}; |
|
} |
|
|
|
const vMenu = await this.rootPage.locator('.nc-dropdown-actions-menu:visible'); |
|
|
|
for (const item of menuItems[param.role]) { |
|
await expect(vMenu).toContainText(item); |
|
} |
|
} |
|
|
|
async validateRoleAccess(param: { role: string; mode?: string }) { |
|
await this.clickActions(); |
|
await this.validateViewsMenu({ |
|
role: param.role, |
|
mode: param.mode, |
|
}); |
|
|
|
const menuItems = { |
|
creator: ['Fields', 'Filter', 'Sort', 'Share View'], |
|
editor: ['Fields', 'Filter', 'Sort'], |
|
commenter: ['Fields', 'Filter', 'Sort', 'Download'], |
|
viewer: ['Fields', 'Filter', 'Sort', 'Download'], |
|
}; |
|
|
|
for (const item of menuItems[param.role]) { |
|
await expect(this.get()).toContainText(item); |
|
} |
|
|
|
await expect(this.get().locator('.nc-add-new-row-btn')).toHaveCount( |
|
param.role === 'creator' || param.role === 'editor' ? 1 : 0 |
|
); |
|
} |
|
}
|
|
|