mirror of https://github.com/nocodb/nocodb
Muhammed Mustafa
2 years ago
21 changed files with 1138 additions and 5 deletions
@ -0,0 +1,4 @@
|
||||
node_modules/ |
||||
/test-results/ |
||||
/playwright-report/ |
||||
/playwright/.cache/ |
@ -0,0 +1,119 @@
|
||||
{ |
||||
"name": "playwright", |
||||
"version": "1.0.0", |
||||
"lockfileVersion": 2, |
||||
"requires": true, |
||||
"packages": { |
||||
"": { |
||||
"name": "playwright", |
||||
"version": "1.0.0", |
||||
"license": "ISC", |
||||
"devDependencies": { |
||||
"@playwright/test": "^1.26.1", |
||||
"axios": "^0.24.0" |
||||
} |
||||
}, |
||||
"node_modules/@playwright/test": { |
||||
"version": "1.26.1", |
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.26.1.tgz", |
||||
"integrity": "sha512-bNxyZASVt2adSZ9gbD7NCydzcb5JaI0OR9hc7s+nmPeH604gwp0zp17NNpwXY4c8nvuBGQQ9oGDx72LE+cUWvw==", |
||||
"dev": true, |
||||
"dependencies": { |
||||
"@types/node": "*", |
||||
"playwright-core": "1.26.1" |
||||
}, |
||||
"bin": { |
||||
"playwright": "cli.js" |
||||
}, |
||||
"engines": { |
||||
"node": ">=14" |
||||
} |
||||
}, |
||||
"node_modules/@types/node": { |
||||
"version": "18.8.1", |
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.1.tgz", |
||||
"integrity": "sha512-vuYaNuEIbOYLTLUAJh50ezEbvxrD43iby+lpUA2aa148Nh5kX/AVO/9m1Ahmbux2iU5uxJTNF9g2Y+31uml7RQ==", |
||||
"dev": true |
||||
}, |
||||
"node_modules/axios": { |
||||
"version": "0.24.0", |
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", |
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", |
||||
"dev": true, |
||||
"dependencies": { |
||||
"follow-redirects": "^1.14.4" |
||||
} |
||||
}, |
||||
"node_modules/follow-redirects": { |
||||
"version": "1.15.2", |
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", |
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", |
||||
"dev": true, |
||||
"funding": [ |
||||
{ |
||||
"type": "individual", |
||||
"url": "https://github.com/sponsors/RubenVerborgh" |
||||
} |
||||
], |
||||
"engines": { |
||||
"node": ">=4.0" |
||||
}, |
||||
"peerDependenciesMeta": { |
||||
"debug": { |
||||
"optional": true |
||||
} |
||||
} |
||||
}, |
||||
"node_modules/playwright-core": { |
||||
"version": "1.26.1", |
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.26.1.tgz", |
||||
"integrity": "sha512-hzFchhhxnEiPc4qVPs9q2ZR+5eKNifY2hQDHtg1HnTTUuphYCBP8ZRb2si+B1TR7BHirgXaPi48LIye5SgrLAA==", |
||||
"dev": true, |
||||
"bin": { |
||||
"playwright": "cli.js" |
||||
}, |
||||
"engines": { |
||||
"node": ">=14" |
||||
} |
||||
} |
||||
}, |
||||
"dependencies": { |
||||
"@playwright/test": { |
||||
"version": "1.26.1", |
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.26.1.tgz", |
||||
"integrity": "sha512-bNxyZASVt2adSZ9gbD7NCydzcb5JaI0OR9hc7s+nmPeH604gwp0zp17NNpwXY4c8nvuBGQQ9oGDx72LE+cUWvw==", |
||||
"dev": true, |
||||
"requires": { |
||||
"@types/node": "*", |
||||
"playwright-core": "1.26.1" |
||||
} |
||||
}, |
||||
"@types/node": { |
||||
"version": "18.8.1", |
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.1.tgz", |
||||
"integrity": "sha512-vuYaNuEIbOYLTLUAJh50ezEbvxrD43iby+lpUA2aa148Nh5kX/AVO/9m1Ahmbux2iU5uxJTNF9g2Y+31uml7RQ==", |
||||
"dev": true |
||||
}, |
||||
"axios": { |
||||
"version": "0.24.0", |
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", |
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", |
||||
"dev": true, |
||||
"requires": { |
||||
"follow-redirects": "^1.14.4" |
||||
} |
||||
}, |
||||
"follow-redirects": { |
||||
"version": "1.15.2", |
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", |
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", |
||||
"dev": true |
||||
}, |
||||
"playwright-core": { |
||||
"version": "1.26.1", |
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.26.1.tgz", |
||||
"integrity": "sha512-hzFchhhxnEiPc4qVPs9q2ZR+5eKNifY2hQDHtg1HnTTUuphYCBP8ZRb2si+B1TR7BHirgXaPi48LIye5SgrLAA==", |
||||
"dev": true |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,14 @@
|
||||
{ |
||||
"name": "playwright", |
||||
"version": "1.0.0", |
||||
"description": "", |
||||
"main": "index.js", |
||||
"scripts": {}, |
||||
"keywords": [], |
||||
"author": "", |
||||
"license": "ISC", |
||||
"devDependencies": { |
||||
"@playwright/test": "^1.26.1", |
||||
"axios": "^0.24.0" |
||||
} |
||||
} |
@ -0,0 +1,19 @@
|
||||
// playwright-dev-page.ts
|
||||
import { Page, expect } from '@playwright/test'; |
||||
|
||||
export class BasePage { |
||||
readonly page: Page; |
||||
|
||||
constructor(page: Page) { |
||||
this.page = page; |
||||
} |
||||
|
||||
async toastWait({message}: {message: string}) { |
||||
const toast = await this.page.locator('.ant-message .ant-message-notice-content', {hasText: message}).last(); |
||||
await toast.waitFor({state: 'visible'}); |
||||
|
||||
// todo: text of toastr shows old one in the test assertion
|
||||
await toast.last().textContent() |
||||
.then((text) => expect(text).toContain(message)); |
||||
} |
||||
} |
@ -0,0 +1,70 @@
|
||||
import { expect } from "@playwright/test"; |
||||
import { CellPageObject } from "."; |
||||
|
||||
export class SelectOptionCellPageObject { |
||||
readonly cell: CellPageObject; |
||||
constructor(cell: CellPageObject) { |
||||
this.cell = cell; |
||||
} |
||||
|
||||
async select({index, columnHeader, option, multiSelect}: {index: number, columnHeader: string, option: string, multiSelect?: boolean}) { |
||||
await this.cell.get({index, columnHeader}).click(); |
||||
const count = await this.cell.page.locator('.rc-virtual-list-holder .ant-select-item-option-content', {hasText: option}).count(); |
||||
|
||||
for(let i = 0; i < count; i++) { |
||||
if(await this.cell.page.locator('.rc-virtual-list-holder .ant-select-item-option-content', {hasText: option}).nth(i).isVisible()) { |
||||
await this.cell.page.locator('.rc-virtual-list-holder .ant-select-item-option-content', {hasText: option}).nth(i).click(); |
||||
} |
||||
} |
||||
|
||||
if(multiSelect) await this.cell.get({index, columnHeader}).click(); |
||||
|
||||
await this.cell.page.locator(`.nc-dropdown-single-select-cell`).nth(index).waitFor({state: 'hidden'}); |
||||
} |
||||
|
||||
async clear({index, columnHeader, multiSelect}: {index: number, columnHeader: string, multiSelect?: boolean}) { |
||||
if(multiSelect){ |
||||
await this.cell.get({index, columnHeader}).click(); |
||||
await this.cell.get({index, columnHeader}).click(); |
||||
|
||||
const optionCount = await this.cell.get({index, columnHeader}).locator('.ant-tag').count(); |
||||
|
||||
for(let i = 0; i < optionCount; i++) { |
||||
await this.cell.get({index, columnHeader}).locator('.ant-tag > .ant-tag-close-icon').first().click(); |
||||
await this.cell.page.waitForTimeout(200); |
||||
} |
||||
return |
||||
} |
||||
|
||||
await this.cell.get({index, columnHeader}).click(); |
||||
await this.cell.page.locator('.ant-select-single > .ant-select-clear').click(); |
||||
await this.cell.get({index, columnHeader}).click(); |
||||
await this.cell.page.locator(`.nc-dropdown-single-select-cell`).waitFor({state: 'hidden'}); |
||||
} |
||||
|
||||
async verify({index, columnHeader, option, multiSelect}: {index: number, columnHeader: string, option: string, multiSelect?: boolean}) { |
||||
if(multiSelect) { |
||||
return expect( |
||||
this.cell.get({index, columnHeader})).toContainText(option, {useInnerText: true}); |
||||
} |
||||
return expect(this.cell.get({index, columnHeader}).locator('.ant-select-selection-item > .ant-tag')).toHaveText(option, {useInnerText: true}); |
||||
} |
||||
|
||||
async verifyNoOptionsSelected({index, columnHeader}: {index: number, columnHeader: string}) { |
||||
return expect(this.cell.get({index, columnHeader}).locator('.ant-select-selection-item > .ant-tag')).toBeHidden(); |
||||
} |
||||
|
||||
async verifyOptions({index, columnHeader, options}: {index: number, columnHeader: string, options: string[]}) { |
||||
await this.cell.get({index, columnHeader}).click(); |
||||
|
||||
let counter = 0; |
||||
for (const option of options) { |
||||
const optionInDom = await this.cell.page.locator(`div.ant-select-item-option`).nth(counter) |
||||
.evaluate((node) => (node as HTMLElement).innerText) |
||||
expect(optionInDom).toBe(option); |
||||
counter++; |
||||
} |
||||
await this.cell.click({index, columnHeader}); |
||||
await this.cell.page.locator(`.nc-dropdown-single-select-cell`).nth(index).waitFor({state: 'hidden'}); |
||||
} |
||||
} |
@ -0,0 +1,20 @@
|
||||
import { Page, Locator } from "@playwright/test"; |
||||
import { SelectOptionCellPageObject } from "./SelectOptionCell"; |
||||
|
||||
export class CellPageObject { |
||||
readonly page: Page; |
||||
readonly selectOption: SelectOptionCellPageObject; |
||||
|
||||
constructor(page: Page) { |
||||
this.page = page; |
||||
this.selectOption = new SelectOptionCellPageObject(this); |
||||
} |
||||
|
||||
get({index, columnHeader}: {index: number, columnHeader: string}): Locator { |
||||
return this.page.locator(`tr.nc-grid-row:nth-child(${index + 1}) > [data-title="${columnHeader}"]`); |
||||
} |
||||
|
||||
async click({index, columnHeader}: {index: number, columnHeader: string}) { |
||||
return this.get({index, columnHeader}).click(); |
||||
} |
||||
} |
@ -0,0 +1,53 @@
|
||||
import { Page } from "@playwright/test"; |
||||
import { ColumnPageObject } from "."; |
||||
|
||||
export class SelectOptionColumnPageObject { |
||||
readonly column: ColumnPageObject; |
||||
|
||||
constructor(column: ColumnPageObject) { |
||||
this.column = column; |
||||
} |
||||
|
||||
async addOption({index, columnTitle,option, skipColumnModal}: {index: number, option: string, skipColumnModal?: boolean, columnTitle?: string}) { |
||||
if(!skipColumnModal && columnTitle) await this.column.openEdit({title: columnTitle}); |
||||
|
||||
await this.column.page.locator('button:has-text("Add option")').click(); |
||||
|
||||
// Fill text=Select options can't be nullAdd option >> input[type="text"]
|
||||
await this.column.page.locator(`input[data-pw="select-column-option-input-${index}"]`).click(); |
||||
await this.column.page.locator(`input[data-pw="select-column-option-input-${index}"]`).fill(option); |
||||
|
||||
if(!skipColumnModal && columnTitle) await this.column.save({isUpdated: true}); |
||||
} |
||||
|
||||
async editOption({columnTitle, index, newOption}: {index: number, columnTitle: string, newOption: string}) { |
||||
await this.column.openEdit({title: columnTitle}); |
||||
|
||||
await this.column.page.locator(`input[data-pw="select-column-option-input-${index}"]`).click(); |
||||
await this.column.page.locator(`input[data-pw="select-column-option-input-${index}"]`).fill(newOption); |
||||
|
||||
await this.column.save({isUpdated: true}); |
||||
} |
||||
|
||||
async deleteOption({columnTitle, index}: {index: number, columnTitle: string}) { |
||||
await this.column.openEdit({title: columnTitle}); |
||||
|
||||
await this.column.page.locator(`svg[data-pw="select-column-option-remove-${index}"]`).click(); |
||||
|
||||
await this.column.save({isUpdated: true}); |
||||
} |
||||
|
||||
async reorderOption({columnTitle, sourceOption, destinationOption}: {columnTitle: string, sourceOption: string, destinationOption: string}) { |
||||
await this.column.openEdit({title: columnTitle}); |
||||
|
||||
await this.column.page.waitForTimeout(150); |
||||
|
||||
await this.column.page.dragAndDrop(`svg[data-pw="select-option-column-handle-icon-${sourceOption}"]`, `svg[data-pw="select-option-column-handle-icon-${destinationOption}"]`, { |
||||
force: true, |
||||
}); |
||||
|
||||
await this.column.page.waitForTimeout(150); |
||||
|
||||
await this.column.save({isUpdated: true}); |
||||
} |
||||
} |
@ -0,0 +1,69 @@
|
||||
import { Page, expect } from "@playwright/test"; |
||||
import { BasePage } from "../Base"; |
||||
import {SelectOptionColumnPageObject} from "./SelectOptionColumn"; |
||||
|
||||
export class ColumnPageObject { |
||||
readonly page: Page; |
||||
readonly basePage: BasePage; |
||||
readonly selectOption: SelectOptionColumnPageObject; |
||||
|
||||
constructor(page: Page) { |
||||
this.page = page; |
||||
this.selectOption = new SelectOptionColumnPageObject(this); |
||||
this.basePage = new BasePage(this.page); |
||||
} |
||||
|
||||
async create({title, type}: {title: string, type: string}) { |
||||
await this.page.locator('.nc-column-add').click(); |
||||
|
||||
await this.page.locator('form[data-pw="add-or-edit-column"]').waitFor(); |
||||
|
||||
// Click span:has-text("SingleLineText") >> nth=1
|
||||
await this.page.locator('span:has-text("SingleLineText")').click(); |
||||
|
||||
// Fill text=Column Type SingleLineText >> input[role="combobox"]
|
||||
await this.page.locator('text=Column Type SingleLineText >> input[role="combobox"]').fill(type); |
||||
|
||||
// Select column type
|
||||
await this.page.locator(`text=${type}`).nth(1).click(); |
||||
|
||||
switch (type) { |
||||
case 'SingleSelect': |
||||
case 'MultiSelect': |
||||
await this.selectOption.addOption({index: 0, option: 'Option 1', skipColumnModal: true}); |
||||
await this.selectOption.addOption({index: 1, option: 'Option 2', skipColumnModal: true}); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
|
||||
await this.page.locator('.nc-column-name-input').fill(title); |
||||
|
||||
await this.save(); |
||||
} |
||||
|
||||
async delete({title}: {title: string}) { |
||||
await this.page.locator(`text=#Title${title} >> svg >> nth=3`).click(); |
||||
await this.page.locator('li[role="menuitem"]:has-text("Delete")').waitFor() |
||||
await this.page.locator('li[role="menuitem"]:has-text("Delete")').click(); |
||||
|
||||
await this.page.locator('button:has-text("Delete")').click(); |
||||
|
||||
// wait till modal is closed
|
||||
await this.page.locator('.nc-modal-column-delete').waitFor({state: 'hidden'}); |
||||
} |
||||
|
||||
async openEdit({title}: {title: string}) { |
||||
await this.page.locator(`text=#Title${title} >> svg >> nth=3`).click(); |
||||
await this.page.locator('li[role="menuitem"]:has-text("Edit")').waitFor() |
||||
await this.page.locator('li[role="menuitem"]:has-text("Edit")').click(); |
||||
|
||||
await this.page.locator('form[data-pw="add-or-edit-column"]').waitFor(); |
||||
} |
||||
|
||||
async save({isUpdated}: {isUpdated?: boolean} = {}) { |
||||
await this.page.locator('button:has-text("Save")').click(); |
||||
|
||||
await this.basePage.toastWait({message: isUpdated ? 'Column updated' : 'Column created'}); |
||||
} |
||||
} |
@ -0,0 +1,36 @@
|
||||
// playwright-dev-page.ts
|
||||
import { expect, Locator, Page } from '@playwright/test'; |
||||
|
||||
export class DashboardPage { |
||||
readonly project: any; |
||||
readonly page: Page; |
||||
readonly tablesSideBar: Locator; |
||||
readonly tabBar: Locator; |
||||
|
||||
constructor(page: Page, project: any) { |
||||
this.page = page; |
||||
this.project = project; |
||||
this.tablesSideBar = page.locator('.nc-treeview-container'); |
||||
this.tabBar = page.locator('.nc-tab-bar'); |
||||
} |
||||
|
||||
async goto() { |
||||
await this.page.goto(`http://localhost:3000/#/nc/${this.project.id}/auth`); |
||||
} |
||||
|
||||
async openTable({ title }: { title: string }) { |
||||
await this.tablesSideBar.locator(`.nc-project-tree-tbl-${title}`).click(); |
||||
await this.tabBar.textContent().then((text) => expect(text).toContain(title)); |
||||
} |
||||
|
||||
async createTable({ title }: { title: string }) { |
||||
await this.tablesSideBar.locator('.nc-add-new-table').click(); |
||||
|
||||
await this.page.locator('.ant-modal-body').waitFor() |
||||
|
||||
await this.page.locator('[placeholder="Enter table name"]').fill(title);
|
||||
await this.page.locator('button:has-text("Submit")').click(); |
||||
|
||||
await expect(this.page).toHaveURL(`http://localhost:3000/#/nc/${this.project.id}/table/${title}`); |
||||
} |
||||
} |
@ -0,0 +1,46 @@
|
||||
// playwright-dev-page.ts
|
||||
import { Locator, Page, expect } from '@playwright/test'; |
||||
import { CellPageObject } from './Cell'; |
||||
import { ColumnPageObject } from './Column'; |
||||
|
||||
export class GridPage { |
||||
readonly page: Page; |
||||
readonly addNewTableButton: Locator; |
||||
readonly column: ColumnPageObject; |
||||
readonly cell: CellPageObject; |
||||
|
||||
constructor(page: Page) { |
||||
this.page = page; |
||||
this.addNewTableButton = page.locator('.nc-add-new-table'); |
||||
this.column = new ColumnPageObject(page); |
||||
this.cell = new CellPageObject(page); |
||||
} |
||||
|
||||
async addNewRow({index = 0, title}: {index?: number, title?: string} = {}) { |
||||
await this.page.locator('.nc-grid-add-new-cell').click(); |
||||
|
||||
// Double click td >> nth=1
|
||||
await this.page.locator('td[data-title="Title"]').nth(index).dblclick(); |
||||
|
||||
|
||||
// Fill text=1Add new row >> input >> nth=1
|
||||
await this.page.locator(`div[data-pw="cell-Title-${index}"] >> input`).fill(title ?? `Row ${index}`); |
||||
|
||||
await this.page.locator('span[title="Title"]').click(); |
||||
await this.page.locator('.nc-grid-wrapper').click(); |
||||
} |
||||
|
||||
async verifyRowDoesNotExist({index}: {index: number}) { |
||||
return expect(await this.page.locator(`td[data-pw="cell-Title-${index}"]`)).toBeHidden(); |
||||
} |
||||
|
||||
async deleteRow(index: number) { |
||||
await this.page.locator(`td[data-pw="cell-Title-${index}"]`).click({ |
||||
button: 'right' |
||||
}); |
||||
|
||||
// Click text=Delete Row
|
||||
await this.page.locator('text=Delete Row').click(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,107 @@
|
||||
import type { PlaywrightTestConfig } from '@playwright/test'; |
||||
import { devices } from '@playwright/test'; |
||||
|
||||
/** |
||||
* Read environment variables from file. |
||||
* https://github.com/motdotla/dotenv
|
||||
*/ |
||||
// require('dotenv').config();
|
||||
|
||||
/** |
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/ |
||||
const config: PlaywrightTestConfig = { |
||||
testDir: './tests', |
||||
/* Maximum time one test can run for. */ |
||||
timeout: 30 * 1000, |
||||
expect: { |
||||
/** |
||||
* Maximum time expect() should wait for the condition to be met. |
||||
* For example in `await expect(locator).toHaveText();` |
||||
*/ |
||||
timeout: 5000 |
||||
}, |
||||
/* Run tests in files in parallel */ |
||||
fullyParallel: true, |
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */ |
||||
forbidOnly: !!process.env.CI, |
||||
/* Retry on CI only */ |
||||
retries: process.env.CI ? 2 : 0, |
||||
/* Opt out of parallel tests on CI. */ |
||||
workers: process.env.CI ? 1 : undefined, |
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ |
||||
reporter: 'html', |
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ |
||||
use: { |
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ |
||||
actionTimeout: 0, |
||||
/* Base URL to use in actions like `await page.goto('/')`. */ |
||||
baseURL: 'http://localhost:3000', |
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ |
||||
trace: 'on-first-retry', |
||||
}, |
||||
|
||||
/* Configure projects for major browsers */ |
||||
projects: [ |
||||
{ |
||||
name: 'chromium', |
||||
use: { |
||||
...devices['Desktop Chrome'], |
||||
}, |
||||
}, |
||||
|
||||
{ |
||||
name: 'firefox', |
||||
use: { |
||||
...devices['Desktop Firefox'], |
||||
}, |
||||
}, |
||||
|
||||
{ |
||||
name: 'webkit', |
||||
use: { |
||||
...devices['Desktop Safari'], |
||||
}, |
||||
}, |
||||
|
||||
/* Test against mobile viewports. */ |
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: {
|
||||
// ...devices['Pixel 5'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: {
|
||||
// ...devices['iPhone 12'],
|
||||
// },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */ |
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: {
|
||||
// channel: 'msedge',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: {
|
||||
// channel: 'chrome',
|
||||
// },
|
||||
// },
|
||||
], |
||||
|
||||
/* Folder for test artifacts such as screenshots, videos, traces, etc. */ |
||||
// outputDir: 'test-results/',
|
||||
|
||||
/* Run your local dev server before starting the tests */ |
||||
// webServer: {
|
||||
// command: 'npm run start',
|
||||
// port: 3000,
|
||||
// },
|
||||
}; |
||||
|
||||
export default config; |
@ -0,0 +1,29 @@
|
||||
import { Page } from '@playwright/test'; |
||||
import axios from 'axios'; |
||||
import { DashboardPage } from '../pages/Dashboard'; |
||||
|
||||
const setup = async ({page}: {page: Page}) => { |
||||
const response = await axios.get('http://localhost:8080/api/v1/meta/test/reset'); |
||||
const token = response.data.token; |
||||
|
||||
await page.addInitScript(async ({token}) => { |
||||
try { |
||||
window.localStorage.setItem('nocodb-gui-v2', JSON.stringify({ |
||||
token: token, |
||||
})); |
||||
} catch (e) { |
||||
window.console.log(e); |
||||
} |
||||
}, { token: token }); |
||||
|
||||
const project = response.data.projects.find((project) => project.title === 'externalREST'); |
||||
|
||||
await page.goto(`/#/nc/${project.id}/auth`); |
||||
|
||||
const dashboardPage = new DashboardPage(page, project); |
||||
await dashboardPage.openTable({title: "Country"}) |
||||
|
||||
return { project, token }; |
||||
} |
||||
|
||||
export default setup; |
@ -0,0 +1,4 @@
|
||||
{ |
||||
"cookies": [], |
||||
"origins": [] |
||||
} |
@ -0,0 +1,398 @@
|
||||
import { test, expect, type Page } from '@playwright/test'; |
||||
|
||||
test.beforeEach(async ({ page }) => { |
||||
await page.goto('https://demo.playwright.dev/todomvc'); |
||||
}); |
||||
|
||||
const TODO_ITEMS = [ |
||||
'buy some cheese', |
||||
'feed the cat', |
||||
'book a doctors appointment' |
||||
]; |
||||
|
||||
test.describe('New Todo', () => { |
||||
test('should allow me to add todo items', async ({ page }) => { |
||||
// Create 1st todo.
|
||||
await page.locator('.new-todo').fill(TODO_ITEMS[0]); |
||||
await page.locator('.new-todo').press('Enter'); |
||||
|
||||
// Make sure the list only has one todo item.
|
||||
await expect(page.locator('.view label')).toHaveText([ |
||||
TODO_ITEMS[0] |
||||
]); |
||||
|
||||
// Create 2nd todo.
|
||||
await page.locator('.new-todo').fill(TODO_ITEMS[1]); |
||||
await page.locator('.new-todo').press('Enter'); |
||||
|
||||
// Make sure the list now has two todo items.
|
||||
await expect(page.locator('.view label')).toHaveText([ |
||||
TODO_ITEMS[0], |
||||
TODO_ITEMS[1] |
||||
]); |
||||
|
||||
await checkNumberOfTodosInLocalStorage(page, 2); |
||||
}); |
||||
|
||||
test('should clear text input field when an item is added', async ({ page }) => { |
||||
// Create one todo item.
|
||||
await page.locator('.new-todo').fill(TODO_ITEMS[0]); |
||||
await page.locator('.new-todo').press('Enter'); |
||||
|
||||
// Check that input is empty.
|
||||
await expect(page.locator('.new-todo')).toBeEmpty(); |
||||
await checkNumberOfTodosInLocalStorage(page, 1); |
||||
}); |
||||
|
||||
test('should append new items to the bottom of the list', async ({ page }) => { |
||||
// Create 3 items.
|
||||
await createDefaultTodos(page); |
||||
|
||||
// Check test using different methods.
|
||||
await expect(page.locator('.todo-count')).toHaveText('3 items left'); |
||||
await expect(page.locator('.todo-count')).toContainText('3'); |
||||
await expect(page.locator('.todo-count')).toHaveText(/3/); |
||||
|
||||
// Check all items in one call.
|
||||
await expect(page.locator('.view label')).toHaveText(TODO_ITEMS); |
||||
await checkNumberOfTodosInLocalStorage(page, 3); |
||||
}); |
||||
|
||||
test('should show #main and #footer when items added', async ({ page }) => { |
||||
await page.locator('.new-todo').fill(TODO_ITEMS[0]); |
||||
await page.locator('.new-todo').press('Enter'); |
||||
|
||||
await expect(page.locator('.main')).toBeVisible(); |
||||
await expect(page.locator('.footer')).toBeVisible(); |
||||
await checkNumberOfTodosInLocalStorage(page, 1); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('Mark all as completed', () => { |
||||
test.beforeEach(async ({ page }) => { |
||||
await createDefaultTodos(page); |
||||
await checkNumberOfTodosInLocalStorage(page, 3); |
||||
}); |
||||
|
||||
test.afterEach(async ({ page }) => { |
||||
await checkNumberOfTodosInLocalStorage(page, 3); |
||||
}); |
||||
|
||||
test('should allow me to mark all items as completed', async ({ page }) => { |
||||
// Complete all todos.
|
||||
await page.locator('.toggle-all').check(); |
||||
|
||||
// Ensure all todos have 'completed' class.
|
||||
await expect(page.locator('.todo-list li')).toHaveClass(['completed', 'completed', 'completed']); |
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3); |
||||
}); |
||||
|
||||
test('should allow me to clear the complete state of all items', async ({ page }) => { |
||||
// Check and then immediately uncheck.
|
||||
await page.locator('.toggle-all').check(); |
||||
await page.locator('.toggle-all').uncheck(); |
||||
|
||||
// Should be no completed classes.
|
||||
await expect(page.locator('.todo-list li')).toHaveClass(['', '', '']); |
||||
}); |
||||
|
||||
test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => { |
||||
const toggleAll = page.locator('.toggle-all'); |
||||
await toggleAll.check(); |
||||
await expect(toggleAll).toBeChecked(); |
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3); |
||||
|
||||
// Uncheck first todo.
|
||||
const firstTodo = page.locator('.todo-list li').nth(0); |
||||
await firstTodo.locator('.toggle').uncheck(); |
||||
|
||||
// Reuse toggleAll locator and make sure its not checked.
|
||||
await expect(toggleAll).not.toBeChecked(); |
||||
|
||||
await firstTodo.locator('.toggle').check(); |
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3); |
||||
|
||||
// Assert the toggle all is checked again.
|
||||
await expect(toggleAll).toBeChecked(); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('Item', () => { |
||||
|
||||
test('should allow me to mark items as complete', async ({ page }) => { |
||||
// Create two items.
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) { |
||||
await page.locator('.new-todo').fill(item); |
||||
await page.locator('.new-todo').press('Enter'); |
||||
} |
||||
|
||||
// Check first item.
|
||||
const firstTodo = page.locator('.todo-list li').nth(0); |
||||
await firstTodo.locator('.toggle').check(); |
||||
await expect(firstTodo).toHaveClass('completed'); |
||||
|
||||
// Check second item.
|
||||
const secondTodo = page.locator('.todo-list li').nth(1); |
||||
await expect(secondTodo).not.toHaveClass('completed'); |
||||
await secondTodo.locator('.toggle').check(); |
||||
|
||||
// Assert completed class.
|
||||
await expect(firstTodo).toHaveClass('completed'); |
||||
await expect(secondTodo).toHaveClass('completed'); |
||||
}); |
||||
|
||||
test('should allow me to un-mark items as complete', async ({ page }) => { |
||||
// Create two items.
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) { |
||||
await page.locator('.new-todo').fill(item); |
||||
await page.locator('.new-todo').press('Enter'); |
||||
} |
||||
|
||||
const firstTodo = page.locator('.todo-list li').nth(0); |
||||
const secondTodo = page.locator('.todo-list li').nth(1); |
||||
await firstTodo.locator('.toggle').check(); |
||||
await expect(firstTodo).toHaveClass('completed'); |
||||
await expect(secondTodo).not.toHaveClass('completed'); |
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1); |
||||
|
||||
await firstTodo.locator('.toggle').uncheck(); |
||||
await expect(firstTodo).not.toHaveClass('completed'); |
||||
await expect(secondTodo).not.toHaveClass('completed'); |
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 0); |
||||
}); |
||||
|
||||
test('should allow me to edit an item', async ({ page }) => { |
||||
await createDefaultTodos(page); |
||||
|
||||
const todoItems = page.locator('.todo-list li'); |
||||
const secondTodo = todoItems.nth(1); |
||||
await secondTodo.dblclick(); |
||||
await expect(secondTodo.locator('.edit')).toHaveValue(TODO_ITEMS[1]); |
||||
await secondTodo.locator('.edit').fill('buy some sausages'); |
||||
await secondTodo.locator('.edit').press('Enter'); |
||||
|
||||
// Explicitly assert the new text value.
|
||||
await expect(todoItems).toHaveText([ |
||||
TODO_ITEMS[0], |
||||
'buy some sausages', |
||||
TODO_ITEMS[2] |
||||
]); |
||||
await checkTodosInLocalStorage(page, 'buy some sausages'); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('Editing', () => { |
||||
test.beforeEach(async ({ page }) => { |
||||
await createDefaultTodos(page); |
||||
await checkNumberOfTodosInLocalStorage(page, 3); |
||||
}); |
||||
|
||||
test('should hide other controls when editing', async ({ page }) => { |
||||
const todoItem = page.locator('.todo-list li').nth(1); |
||||
await todoItem.dblclick(); |
||||
await expect(todoItem.locator('.toggle')).not.toBeVisible(); |
||||
await expect(todoItem.locator('label')).not.toBeVisible(); |
||||
await checkNumberOfTodosInLocalStorage(page, 3); |
||||
}); |
||||
|
||||
test('should save edits on blur', async ({ page }) => { |
||||
const todoItems = page.locator('.todo-list li'); |
||||
await todoItems.nth(1).dblclick(); |
||||
await todoItems.nth(1).locator('.edit').fill('buy some sausages'); |
||||
await todoItems.nth(1).locator('.edit').dispatchEvent('blur'); |
||||
|
||||
await expect(todoItems).toHaveText([ |
||||
TODO_ITEMS[0], |
||||
'buy some sausages', |
||||
TODO_ITEMS[2], |
||||
]); |
||||
await checkTodosInLocalStorage(page, 'buy some sausages'); |
||||
}); |
||||
|
||||
test('should trim entered text', async ({ page }) => { |
||||
const todoItems = page.locator('.todo-list li'); |
||||
await todoItems.nth(1).dblclick(); |
||||
await todoItems.nth(1).locator('.edit').fill(' buy some sausages '); |
||||
await todoItems.nth(1).locator('.edit').press('Enter'); |
||||
|
||||
await expect(todoItems).toHaveText([ |
||||
TODO_ITEMS[0], |
||||
'buy some sausages', |
||||
TODO_ITEMS[2], |
||||
]); |
||||
await checkTodosInLocalStorage(page, 'buy some sausages'); |
||||
}); |
||||
|
||||
test('should remove the item if an empty text string was entered', async ({ page }) => { |
||||
const todoItems = page.locator('.todo-list li'); |
||||
await todoItems.nth(1).dblclick(); |
||||
await todoItems.nth(1).locator('.edit').fill(''); |
||||
await todoItems.nth(1).locator('.edit').press('Enter'); |
||||
|
||||
await expect(todoItems).toHaveText([ |
||||
TODO_ITEMS[0], |
||||
TODO_ITEMS[2], |
||||
]); |
||||
}); |
||||
|
||||
test('should cancel edits on escape', async ({ page }) => { |
||||
const todoItems = page.locator('.todo-list li'); |
||||
await todoItems.nth(1).dblclick(); |
||||
await todoItems.nth(1).locator('.edit').press('Escape'); |
||||
await expect(todoItems).toHaveText(TODO_ITEMS); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('Counter', () => { |
||||
test('should display the current number of todo items', async ({ page }) => { |
||||
await page.locator('.new-todo').fill(TODO_ITEMS[0]); |
||||
await page.locator('.new-todo').press('Enter'); |
||||
await expect(page.locator('.todo-count')).toContainText('1'); |
||||
|
||||
await page.locator('.new-todo').fill(TODO_ITEMS[1]); |
||||
await page.locator('.new-todo').press('Enter'); |
||||
await expect(page.locator('.todo-count')).toContainText('2'); |
||||
|
||||
await checkNumberOfTodosInLocalStorage(page, 2); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('Clear completed button', () => { |
||||
test.beforeEach(async ({ page }) => { |
||||
await createDefaultTodos(page); |
||||
}); |
||||
|
||||
test('should display the correct text', async ({ page }) => { |
||||
await page.locator('.todo-list li .toggle').first().check(); |
||||
await expect(page.locator('.clear-completed')).toHaveText('Clear completed'); |
||||
}); |
||||
|
||||
test('should remove completed items when clicked', async ({ page }) => { |
||||
const todoItems = page.locator('.todo-list li'); |
||||
await todoItems.nth(1).locator('.toggle').check(); |
||||
await page.locator('.clear-completed').click(); |
||||
await expect(todoItems).toHaveCount(2); |
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]); |
||||
}); |
||||
|
||||
test('should be hidden when there are no items that are completed', async ({ page }) => { |
||||
await page.locator('.todo-list li .toggle').first().check(); |
||||
await page.locator('.clear-completed').click(); |
||||
await expect(page.locator('.clear-completed')).toBeHidden(); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('Persistence', () => { |
||||
test('should persist its data', async ({ page }) => { |
||||
for (const item of TODO_ITEMS.slice(0, 2)) { |
||||
await page.locator('.new-todo').fill(item); |
||||
await page.locator('.new-todo').press('Enter'); |
||||
} |
||||
|
||||
const todoItems = page.locator('.todo-list li'); |
||||
await todoItems.nth(0).locator('.toggle').check(); |
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]); |
||||
await expect(todoItems).toHaveClass(['completed', '']); |
||||
|
||||
// Ensure there is 1 completed item.
|
||||
checkNumberOfCompletedTodosInLocalStorage(page, 1); |
||||
|
||||
// Now reload.
|
||||
await page.reload(); |
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]); |
||||
await expect(todoItems).toHaveClass(['completed', '']); |
||||
}); |
||||
}); |
||||
|
||||
test.describe('Routing', () => { |
||||
test.beforeEach(async ({ page }) => { |
||||
await createDefaultTodos(page); |
||||
// make sure the app had a chance to save updated todos in storage
|
||||
// before navigating to a new view, otherwise the items can get lost :(
|
||||
// in some frameworks like Durandal
|
||||
await checkTodosInLocalStorage(page, TODO_ITEMS[0]); |
||||
}); |
||||
|
||||
test('should allow me to display active items', async ({ page }) => { |
||||
await page.locator('.todo-list li .toggle').nth(1).check(); |
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1); |
||||
await page.locator('.filters >> text=Active').click(); |
||||
await expect(page.locator('.todo-list li')).toHaveCount(2); |
||||
await expect(page.locator('.todo-list li')).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]); |
||||
}); |
||||
|
||||
test('should respect the back button', async ({ page }) => { |
||||
await page.locator('.todo-list li .toggle').nth(1).check(); |
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1); |
||||
|
||||
await test.step('Showing all items', async () => { |
||||
await page.locator('.filters >> text=All').click(); |
||||
await expect(page.locator('.todo-list li')).toHaveCount(3); |
||||
}); |
||||
|
||||
await test.step('Showing active items', async () => { |
||||
await page.locator('.filters >> text=Active').click(); |
||||
}); |
||||
|
||||
await test.step('Showing completed items', async () => { |
||||
await page.locator('.filters >> text=Completed').click(); |
||||
}); |
||||
|
||||
await expect(page.locator('.todo-list li')).toHaveCount(1); |
||||
await page.goBack(); |
||||
await expect(page.locator('.todo-list li')).toHaveCount(2); |
||||
await page.goBack(); |
||||
await expect(page.locator('.todo-list li')).toHaveCount(3); |
||||
}); |
||||
|
||||
test('should allow me to display completed items', async ({ page }) => { |
||||
await page.locator('.todo-list li .toggle').nth(1).check(); |
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1); |
||||
await page.locator('.filters >> text=Completed').click(); |
||||
await expect(page.locator('.todo-list li')).toHaveCount(1); |
||||
}); |
||||
|
||||
test('should allow me to display all items', async ({ page }) => { |
||||
await page.locator('.todo-list li .toggle').nth(1).check(); |
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1); |
||||
await page.locator('.filters >> text=Active').click(); |
||||
await page.locator('.filters >> text=Completed').click(); |
||||
await page.locator('.filters >> text=All').click(); |
||||
await expect(page.locator('.todo-list li')).toHaveCount(3); |
||||
}); |
||||
|
||||
test('should highlight the currently applied filter', async ({ page }) => { |
||||
await expect(page.locator('.filters >> text=All')).toHaveClass('selected'); |
||||
await page.locator('.filters >> text=Active').click(); |
||||
// Page change - active items.
|
||||
await expect(page.locator('.filters >> text=Active')).toHaveClass('selected'); |
||||
await page.locator('.filters >> text=Completed').click(); |
||||
// Page change - completed items.
|
||||
await expect(page.locator('.filters >> text=Completed')).toHaveClass('selected'); |
||||
}); |
||||
}); |
||||
|
||||
async function createDefaultTodos(page: Page) { |
||||
for (const item of TODO_ITEMS) { |
||||
await page.locator('.new-todo').fill(item); |
||||
await page.locator('.new-todo').press('Enter'); |
||||
} |
||||
} |
||||
|
||||
async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) { |
||||
return await page.waitForFunction(e => { |
||||
return JSON.parse(localStorage['react-todos']).length === e; |
||||
}, expected); |
||||
} |
||||
|
||||
async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) { |
||||
return await page.waitForFunction(e => { |
||||
return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e; |
||||
}, expected); |
||||
} |
||||
|
||||
async function checkTodosInLocalStorage(page: Page, title: string) { |
||||
return await page.waitForFunction(t => { |
||||
return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t); |
||||
}, title); |
||||
} |
@ -0,0 +1,65 @@
|
||||
import { Page, test } from '@playwright/test'; |
||||
import { DashboardPage } from '../pages/Dashboard'; |
||||
import { GridPage } from '../pages/Grid'; |
||||
import setup from '../setup'; |
||||
|
||||
|
||||
test.describe.serial('Multi select', () => { |
||||
let dashboard: DashboardPage, grid: GridPage; |
||||
let context: any; |
||||
|
||||
test.beforeEach(async ({page}) => { |
||||
context = await setup({ page }); |
||||
dashboard = new DashboardPage(page, context.project); |
||||
await dashboard.createTable({ title: 'sheet1' }); |
||||
|
||||
grid = new GridPage(page); |
||||
await grid.column.create({ title: 'MultiSelect', type: 'MultiSelect' }); |
||||
await grid.addNewRow({index: 0, title: "Row 0"}); |
||||
}) |
||||
|
||||
test('Select and clear options and rename options', async () => { |
||||
await grid.cell.selectOption.select({index: 0, columnHeader: 'MultiSelect', option: 'Option 1', multiSelect: true}); |
||||
await grid.cell.selectOption.verify({index: 0, columnHeader: 'MultiSelect', option: 'Option 1', multiSelect: true}); |
||||
|
||||
await grid.cell.selectOption.select({index: 0, columnHeader: 'MultiSelect', option: 'Option 2', multiSelect: true}); |
||||
await grid.cell.selectOption.verify({index: 0, columnHeader: 'MultiSelect', option: 'Option 2', multiSelect: true}); |
||||
|
||||
await grid.addNewRow({index: 0, title: "Row 0"}); |
||||
await grid.cell.selectOption.select({index: 1, columnHeader: 'MultiSelect', option: 'Option 1', multiSelect: true}); |
||||
|
||||
await grid.cell.selectOption.clear({index: 0, columnHeader: 'MultiSelect', multiSelect: true}); |
||||
await grid.cell.click({index: 0, columnHeader: 'MultiSelect'}); |
||||
|
||||
await grid.column.selectOption.addOption({index: 2, option: 'Option 3', columnTitle: 'MultiSelect'}); |
||||
|
||||
await grid.cell.selectOption.select({index: 0, columnHeader: 'MultiSelect', option: 'Option 3', multiSelect: true}); |
||||
await grid.cell.selectOption.verify({index: 0, columnHeader: 'MultiSelect', option: 'Option 3', multiSelect: true}); |
||||
|
||||
await grid.column.selectOption.editOption({index: 2, columnTitle: 'MultiSelect', newOption: 'New Option 3'}); |
||||
await grid.cell.selectOption.verify({index: 0, columnHeader: 'MultiSelect', option: 'New Option 3', multiSelect: true}); |
||||
|
||||
await grid.cell.selectOption.verifyOptions({index: 0, columnHeader: 'MultiSelect', options: ['Option 1', 'Option 2', 'New Option 3']}); |
||||
|
||||
await grid.deleteRow(0); |
||||
await grid.deleteRow(0); |
||||
await grid.verifyRowDoesNotExist({index: 0}); |
||||
}); |
||||
|
||||
test('Remove a option, reorder option and delete the column', async () => { |
||||
await grid.cell.selectOption.select({index: 0, columnHeader: 'MultiSelect', option: 'Option 1', multiSelect: true}); |
||||
await grid.column.selectOption.addOption({index: 2, option: 'Option 3', columnTitle: 'MultiSelect'}); |
||||
|
||||
await grid.cell.selectOption.select({index: 0, columnHeader: 'MultiSelect', option: 'Option 3', multiSelect: true}); |
||||
await grid.cell.selectOption.verify({index: 0, columnHeader: 'MultiSelect', option: 'Option 3', multiSelect: true}); |
||||
|
||||
await grid.column.selectOption.deleteOption({index: 2, columnTitle: 'MultiSelect'}); |
||||
await grid.cell.selectOption.verifyNoOptionsSelected({index: 0, columnHeader: 'MultiSelect'}); |
||||
|
||||
await grid.column.selectOption.reorderOption({sourceOption: "Option 1", columnTitle: 'MultiSelect', destinationOption: "Option 2"}); |
||||
await grid.cell.selectOption.verifyOptions({index: 0, columnHeader: 'MultiSelect', options: ['Option 2', 'Option 1']}); |
||||
|
||||
await grid.column.delete({title: 'MultiSelect'}); |
||||
}); |
||||
|
||||
}); |
@ -0,0 +1,61 @@
|
||||
import { Page, test } from '@playwright/test'; |
||||
import { DashboardPage } from '../pages/Dashboard'; |
||||
import { GridPage } from '../pages/Grid'; |
||||
import setup from '../setup'; |
||||
|
||||
|
||||
test.describe.serial('Single select', () => { |
||||
let dashboard: DashboardPage, grid: GridPage; |
||||
let context: any; |
||||
|
||||
test.beforeEach(async ({page}) => { |
||||
context = await setup({ page }); |
||||
dashboard = new DashboardPage(page, context.project); |
||||
await dashboard.createTable({ title: 'sheet1' }); |
||||
|
||||
grid = new GridPage(page); |
||||
await grid.column.create({ title: 'SingleSelect', type: 'SingleSelect' }); |
||||
await grid.addNewRow({index: 0, title: "Row 0"}); |
||||
}) |
||||
|
||||
test('Select and clear options and rename options', async () => { |
||||
await grid.cell.selectOption.select({index: 0, columnHeader: 'SingleSelect', option: 'Option 1'}); |
||||
await grid.cell.selectOption.verify({index: 0, columnHeader: 'SingleSelect', option: 'Option 1'}); |
||||
|
||||
await grid.cell.selectOption.select({index: 0, columnHeader: 'SingleSelect', option: 'Option 2'}); |
||||
await grid.cell.selectOption.verify({index: 0, columnHeader: 'SingleSelect', option: 'Option 2'}); |
||||
|
||||
await grid.cell.selectOption.clear({index: 0, columnHeader: 'SingleSelect'}); |
||||
await grid.cell.click({index: 0, columnHeader: 'SingleSelect'}); |
||||
|
||||
await grid.column.selectOption.addOption({index: 2, option: 'Option 3', columnTitle: 'SingleSelect'}); |
||||
|
||||
await grid.cell.selectOption.select({index: 0, columnHeader: 'SingleSelect', option: 'Option 3'}); |
||||
await grid.cell.selectOption.verify({index: 0, columnHeader: 'SingleSelect', option: 'Option 3'}); |
||||
|
||||
await grid.column.selectOption.editOption({index: 2, columnTitle: 'SingleSelect', newOption: 'New Option 3'}); |
||||
await grid.cell.selectOption.verify({index: 0, columnHeader: 'SingleSelect', option: 'New Option 3'}); |
||||
|
||||
await grid.cell.selectOption.verifyOptions({index: 0, columnHeader: 'SingleSelect', options: ['Option 1', 'Option 2', 'New Option 3']}); |
||||
|
||||
await grid.deleteRow(0); |
||||
await grid.verifyRowDoesNotExist({index: 0}); |
||||
}); |
||||
|
||||
test('Remove a option, reorder option and delete the column', async () => { |
||||
await grid.cell.selectOption.select({index: 0, columnHeader: 'SingleSelect', option: 'Option 1'}); |
||||
await grid.column.selectOption.addOption({index: 2, option: 'Option 3', columnTitle: 'SingleSelect'}); |
||||
|
||||
await grid.cell.selectOption.select({index: 0, columnHeader: 'SingleSelect', option: 'Option 3'}); |
||||
await grid.cell.selectOption.verify({index: 0, columnHeader: 'SingleSelect', option: 'Option 3'}); |
||||
|
||||
await grid.column.selectOption.deleteOption({index: 2, columnTitle: 'SingleSelect'}); |
||||
await grid.cell.selectOption.verifyNoOptionsSelected({index: 0, columnHeader: 'SingleSelect'}); |
||||
|
||||
await grid.column.selectOption.reorderOption({sourceOption: "Option 1", columnTitle: 'SingleSelect', destinationOption: "Option 2"}); |
||||
await grid.cell.selectOption.verifyOptions({index: 0, columnHeader: 'SingleSelect', options: ['Option 2', 'Option 1']}); |
||||
|
||||
await grid.column.delete({title: 'SingleSelect'}); |
||||
}); |
||||
|
||||
}); |
Loading…
Reference in new issue