Browse Source

Merge pull request #5118 from nocodb/chore/playwright-test

chore(test): playwright test
pull/5129/head
Raju Udava 2 years ago committed by GitHub
parent
commit
0b20b525f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      packages/noco-docs/content/en/engineering/playwright.md
  2. 156
      packages/nocodb/package-lock.json
  3. 2
      packages/nocodb/package.json
  4. 5
      tests/playwright/pages/Account/ChangePassword.ts
  5. 37
      tests/playwright/pages/Base.ts
  6. 7
      tests/playwright/pages/Dashboard/ExpandedForm/index.ts
  7. 2
      tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts
  8. 8
      tests/playwright/pages/Dashboard/Grid/Column/index.ts
  9. 16
      tests/playwright/pages/Dashboard/Grid/index.ts
  10. 2
      tests/playwright/pages/Dashboard/Import/ImportTemplate.ts
  11. 2
      tests/playwright/pages/Dashboard/Settings/Acl.ts
  12. 2
      tests/playwright/pages/Dashboard/Settings/Miscellaneous.ts
  13. 6
      tests/playwright/pages/Dashboard/TreeView.ts
  14. 12
      tests/playwright/pages/Dashboard/ViewSidebar/index.ts
  15. 2
      tests/playwright/pages/Dashboard/WebhookForm/index.ts
  16. 2
      tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts
  17. 2
      tests/playwright/pages/Dashboard/common/Cell/index.ts
  18. 15
      tests/playwright/pages/Dashboard/common/Toolbar/Fields.ts
  19. 4
      tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts
  20. 11
      tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts
  21. 6
      tests/playwright/pages/Dashboard/common/Toolbar/index.ts
  22. 8
      tests/playwright/pages/ProjectsPage/index.ts
  23. 2
      tests/playwright/pages/SharedForm/index.ts

4
packages/noco-docs/content/en/engineering/playwright.md

@ -189,7 +189,7 @@ This a method which will reset/clear all the filters. Since this is an action me
```js
async resetFilter() {
await this.waitForResponse({
uiAction: this.get().locator('.nc-filter-item-remove-btn').click(),
uiAction: () => this.get().locator('.nc-filter-item-remove-btn').click(),
httpMethodsToMatch: ['DELETE'],
requestUrlPathToMatch: '/api/v1/db/meta/filters/',
});
@ -221,4 +221,4 @@ async verifyFilter({ title }: { title: string }) {
- Open `Summary` tab in the CI workflow in github actions.
- Scroll down to `Artifacts` section.
- Access reports which suffixed with the db type and shard number(corresponding to the CI workerflow name). i.e `playwright-report-mysql-2` is for `playwright-mysql-2` workflow.
- Download it and run `npm install -D @playwright/test && npx playwright show-report ./` inside the downloaded folder.
- Download it and run `npm install -D @playwright/test && npx playwright show-report ./` inside the downloaded folder.

156
packages/nocodb/package-lock.json generated

@ -58,7 +58,7 @@
"lru-cache": "^6.0.0",
"mailersend": "^1.1.0",
"minio": "^7.0.18",
"mkdirp": "^0.5.5",
"mkdirp": "^2.1.3",
"morgan": "^1.10.0",
"mssql": "^6.2.0",
"multer": "^1.4.2",
@ -4259,6 +4259,18 @@
"run-queue": "^1.0.0"
}
},
"node_modules/copy-concurrently/node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/copy-descriptor": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
@ -10321,6 +10333,17 @@
"node": ">8 <=18"
}
},
"node_modules/minio/node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/minio/node_modules/through2": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
@ -10510,14 +10533,17 @@
}
},
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dependencies": {
"minimist": "^1.2.6"
},
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.3.tgz",
"integrity": "sha512-sjAkg21peAG9HS+Dkx7hlG9Ztx7HLeKnvB3NQRcu/mltCVmvkF0pisbiTSfDVYTT86XEfZrTUosLdZLStquZUw==",
"bin": {
"mkdirp": "bin/cmd.js"
"mkdirp": "dist/cjs/src/bin.js"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/mocha": {
@ -10874,6 +10900,18 @@
"run-queue": "^1.0.3"
}
},
"node_modules/move-concurrently/node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/move-file": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/move-file/-/move-file-1.2.0.tgz",
@ -11021,6 +11059,17 @@
"node": ">= 0.10.0"
}
},
"node_modules/multer/node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/multi-stage-sourcemap": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/multi-stage-sourcemap/-/multi-stage-sourcemap-0.3.1.tgz",
@ -17153,6 +17202,17 @@
"node": ">= 0.12.0"
}
},
"node_modules/utility/node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@ -18318,6 +18378,18 @@
"node": ">=0.10.0"
}
},
"node_modules/webpack/node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/webpack/node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@ -22371,6 +22443,17 @@
"mkdirp": "^0.5.1",
"rimraf": "^2.5.4",
"run-queue": "^1.0.0"
},
"dependencies": {
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"requires": {
"minimist": "^1.2.6"
}
}
}
},
"copy-descriptor": {
@ -27047,6 +27130,14 @@
"xml2js": "^0.4.15"
},
"dependencies": {
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"requires": {
"minimist": "^1.2.6"
}
},
"through2": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
@ -27213,12 +27304,9 @@
}
},
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"requires": {
"minimist": "^1.2.6"
}
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.3.tgz",
"integrity": "sha512-sjAkg21peAG9HS+Dkx7hlG9Ztx7HLeKnvB3NQRcu/mltCVmvkF0pisbiTSfDVYTT86XEfZrTUosLdZLStquZUw=="
},
"mocha": {
"version": "10.1.0",
@ -27489,6 +27577,17 @@
"mkdirp": "^0.5.1",
"rimraf": "^2.5.4",
"run-queue": "^1.0.3"
},
"dependencies": {
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"requires": {
"minimist": "^1.2.6"
}
}
}
},
"move-file": {
@ -27612,6 +27711,16 @@
"on-finished": "^2.3.0",
"type-is": "^1.6.4",
"xtend": "^4.0.0"
},
"dependencies": {
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"requires": {
"minimist": "^1.2.6"
}
}
}
},
"multi-stage-sourcemap": {
@ -32428,6 +32537,16 @@
"mkdirp": "^0.5.1",
"mz": "^2.7.0",
"unescape": "^1.0.1"
},
"dependencies": {
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"requires": {
"minimist": "^1.2.6"
}
}
}
},
"utils-merge": {
@ -33032,6 +33151,15 @@
"to-regex": "^3.0.2"
}
},
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"requires": {
"minimist": "^1.2.6"
}
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",

2
packages/nocodb/package.json

@ -98,7 +98,7 @@
"lru-cache": "^6.0.0",
"mailersend": "^1.1.0",
"minio": "^7.0.18",
"mkdirp": "^0.5.5",
"mkdirp": "^2.1.3",
"morgan": "^1.10.0",
"mssql": "^6.2.0",
"multer": "^1.4.2",

5
tests/playwright/pages/Account/ChangePassword.ts

@ -29,7 +29,8 @@ export class ChangePasswordPage extends BasePage {
await newPassword.fill(newPass);
await confirmPassword.fill(repeatPass);
const submitChangePassword = this.get().locator('button[data-testid="nc-user-settings-form__submit"]').click();
const submitChangePassword = () =>
this.get().locator('button[data-testid="nc-user-settings-form__submit"]').click();
if (networkValidation) {
await this.waitForResponse({
uiAction: submitChangePassword,
@ -37,7 +38,7 @@ export class ChangePasswordPage extends BasePage {
requestUrlPathToMatch: 'api/v1/auth/password/change',
});
} else {
await submitChangePassword;
await submitChangePassword();
}
}

37
tests/playwright/pages/Base.ts

@ -23,30 +23,31 @@ export default abstract class BasePage {
// A function that takes the response body and returns true if the response is the one we are looking for
responseJsonMatcher,
}: {
uiAction: Promise<any>;
uiAction: () => Promise<any>;
requestUrlPathToMatch: string;
httpMethodsToMatch?: string[];
responseJsonMatcher?: ResponseSelector;
}) {
await Promise.all([
this.rootPage.waitForResponse(async res => {
let isResJsonMatched = true;
if (responseJsonMatcher) {
try {
isResJsonMatched = responseJsonMatcher(await res.json());
} catch (e) {
return false;
}
const waitForResposePromise = this.rootPage.waitForResponse(async res => {
let isResJsonMatched = true;
if (responseJsonMatcher) {
try {
isResJsonMatched = responseJsonMatcher(await res.json());
} catch (e) {
return false;
}
}
return (
res.request().url().includes(requestUrlPathToMatch) &&
httpMethodsToMatch.includes(res.request().method()) &&
isResJsonMatched
);
}),
uiAction,
]);
return (
res.request().url().includes(requestUrlPathToMatch) &&
httpMethodsToMatch.includes(res.request().method()) &&
isResJsonMatched
);
});
const uiActionPromise = uiAction();
await Promise.all([waitForResposePromise, uiActionPromise]);
}
async attachFile({ filePickUIAction, filePath }: { filePickUIAction: Promise<any>; filePath: string[] }) {

7
tests/playwright/pages/Dashboard/ExpandedForm/index.ts

@ -92,9 +92,10 @@ export class ExpandedFormPage extends BasePage {
await dropdownList.locator('.ant-dropdown-menu-item:has-text("Save & Stay")').click();
}
const saveRowAction = saveAndExitMode
? this.get().locator('button:has-text("Save & Exit")').click()
: this.get().locator('button:has-text("Save & Stay")').click();
const saveRowAction = () =>
saveAndExitMode
? this.get().locator('button:has-text("Save & Exit")').click()
: this.get().locator('button:has-text("Save & Stay")').click();
if (waitForRowsData) {
await this.waitForResponse({

2
tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts

@ -50,7 +50,7 @@ export class ChildList extends BasePage {
}
async openLinkRecord({ linkTableTitle }: { linkTableTitle: string }) {
const openActions = this.get().locator(`text=/Link to '.*${linkTableTitle}'/i`).click();
const openActions = () => this.get().locator(`text=/Link to '.*${linkTableTitle}'/i`).click();
await this.waitForResponse({
requestUrlPathToMatch: '/exclude',
httpMethodsToMatch: ['GET'],

8
tests/playwright/pages/Dashboard/Grid/Column/index.ts

@ -330,7 +330,7 @@ export class ColumnPageObject extends BasePage {
}
await this.waitForResponse({
uiAction: this.rootPage.locator('li[role="menuitem"]:has-text("Hide Field"):visible').click(),
uiAction: () => this.rootPage.locator('li[role="menuitem"]:has-text("Hide Field"):visible').click(),
requestUrlPathToMatch: 'api/v1/db/meta/views',
httpMethodsToMatch: ['PATCH'],
});
@ -340,7 +340,7 @@ export class ColumnPageObject extends BasePage {
async save({ isUpdated }: { isUpdated?: boolean } = {}) {
await this.waitForResponse({
uiAction: this.get().locator('button:has-text("Save")').click(),
uiAction: () => this.get().locator('button:has-text("Save")').click(),
requestUrlPathToMatch: 'api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'],
responseJsonMatcher: json => json['pageInfo'],
@ -375,9 +375,9 @@ export class ColumnPageObject extends BasePage {
await this.grid.get().locator(`th[data-title="${title}"] .nc-ui-dt-dropdown`).click();
let menuOption;
if (direction === 'desc') {
menuOption = this.rootPage.locator('li[role="menuitem"]:has-text("Sort Descending"):visible').click();
menuOption = () => this.rootPage.locator('li[role="menuitem"]:has-text("Sort Descending"):visible').click();
} else {
menuOption = this.rootPage.locator('li[role="menuitem"]:has-text("Sort Ascending"):visible').click();
menuOption = () => this.rootPage.locator('li[role="menuitem"]:has-text("Sort Ascending"):visible').click();
}
await this.waitForResponse({

16
tests/playwright/pages/Dashboard/Grid/index.ts

@ -79,10 +79,8 @@ export class GridPage extends BasePage {
await this._fillRow({ index, columnHeader, value: rowValue });
const clickOnColumnHeaderToSave = this.get()
.locator(`[data-title="${columnHeader}"]`)
.locator(`span[title="${columnHeader}"]`)
.click();
const clickOnColumnHeaderToSave = () =>
this.get().locator(`[data-title="${columnHeader}"]`).locator(`span[title="${columnHeader}"]`).click();
if (networkValidation) {
await this.waitForResponse({
@ -92,6 +90,7 @@ export class GridPage extends BasePage {
responseJsonMatcher: resJson => resJson?.[columnHeader] === rowValue,
});
} else {
await clickOnColumnHeaderToSave();
await this.rootPage.waitForTimeout(300);
}
@ -111,10 +110,8 @@ export class GridPage extends BasePage {
}) {
await this._fillRow({ index, columnHeader, value });
const clickOnColumnHeaderToSave = this.get()
.locator(`[data-title="${columnHeader}"]`)
.locator(`span[title="${columnHeader}"]`)
.click();
const clickOnColumnHeaderToSave = () =>
this.get().locator(`[data-title="${columnHeader}"]`).locator(`span[title="${columnHeader}"]`).click();
if (networkValidation) {
await this.waitForResponse({
@ -128,6 +125,7 @@ export class GridPage extends BasePage {
responseJsonMatcher: resJson => resJson?.[columnHeader] === value,
});
} else {
await clickOnColumnHeaderToSave();
await this.rootPage.waitForTimeout(300);
}
@ -231,7 +229,7 @@ export class GridPage extends BasePage {
async clickPagination({ page }: { page: string }) {
await this.waitForResponse({
uiAction: (await this.pagination({ page })).click(),
uiAction: async () => (await this.pagination({ page })).click(),
httpMethodsToMatch: ['GET'],
requestUrlPathToMatch: '/views/',
responseJsonMatcher: resJson => resJson?.pageInfo,

2
tests/playwright/pages/Dashboard/Import/ImportTemplate.ts

@ -65,7 +65,7 @@ export class ImportTemplatePage extends BasePage {
await this.waitForResponse({
requestUrlPathToMatch: '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'],
uiAction: this.get().locator('button:has-text("Import"):visible').click(),
uiAction: () => this.get().locator('button:has-text("Import"):visible').click(),
});
await this.dashboard.waitForTabRender({
title: tblList[0],

2
tests/playwright/pages/Dashboard/Settings/Acl.ts

@ -19,7 +19,7 @@ export class AclPage extends BasePage {
async save() {
await this.waitForResponse({
uiAction: this.get().locator(`button:has-text("Save")`).click(),
uiAction: () => this.get().locator(`button:has-text("Save")`).click(),
httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: '/visibility-rules',
});

2
tests/playwright/pages/Dashboard/Settings/Miscellaneous.ts

@ -14,7 +14,7 @@ export class MiscSettingsPage extends BasePage {
}
async clickShowM2MTables() {
const clickAction = this.get().locator('input[type="checkbox"]').first().click();
const clickAction = () => this.get().locator('input[type="checkbox"]').first().click();
await this.waitForResponse({
uiAction: clickAction,
requestUrlPathToMatch: 'tables?includeM2M',

6
tests/playwright/pages/Dashboard/TreeView.ts

@ -54,7 +54,7 @@ export class TreeViewPage extends BasePage {
if (networkResponse === true) {
await this.waitForResponse({
uiAction: this.get().locator(`.nc-project-tree-tbl-${title}`).click(),
uiAction: () => this.get().locator(`.nc-project-tree-tbl-${title}`).click(),
httpMethodsToMatch: ['GET'],
requestUrlPathToMatch: `/api/v1/db/data/noco/`,
responseJsonMatcher: json => json.pageInfo,
@ -74,7 +74,7 @@ export class TreeViewPage extends BasePage {
await this.dashboard.get().getByPlaceholder('Enter table name').fill(title);
await this.waitForResponse({
uiAction: this.dashboard.get().locator('button:has-text("Submit")').click(),
uiAction: () => this.dashboard.get().locator('button:has-text("Submit")').click(),
httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: `/api/v1/db/meta/projects/`,
responseJsonMatcher: json => json.title === title && json.type === 'table',
@ -101,7 +101,7 @@ export class TreeViewPage extends BasePage {
await this.dashboard.get().locator('div.nc-project-menu-item:has-text("Delete")').click();
await this.waitForResponse({
uiAction: this.dashboard.get().locator('button:has-text("Yes")').click(),
uiAction: () => this.dashboard.get().locator('button:has-text("Yes")').click(),
httpMethodsToMatch: ['DELETE'],
requestUrlPathToMatch: `/api/v1/db/meta/tables/`,
});

12
tests/playwright/pages/Dashboard/ViewSidebar/index.ts

@ -38,10 +38,8 @@ export class ViewSidebarPage extends BasePage {
private async createView({ title, locator }: { title: string; locator: Locator }) {
await locator.click();
await this.rootPage.locator('input[id="form_item_title"]:visible').fill(title);
const submitAction = this.rootPage
.locator('.ant-modal-content')
.locator('button:has-text("Submit"):visible')
.click();
const submitAction = () =>
this.rootPage.locator('.ant-modal-content').locator('button:has-text("Submit"):visible').click();
await this.waitForResponse({
httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: '/api/v1/db/meta/tables/',
@ -128,10 +126,8 @@ export class ViewSidebarPage extends BasePage {
.locator(`[data-testid="view-sidebar-view-actions-${title}"]`)
.locator('.nc-view-copy-icon')
.click();
const submitAction = this.rootPage
.locator('.ant-modal-content')
.locator('button:has-text("Submit"):visible')
.click();
const submitAction = () =>
this.rootPage.locator('.ant-modal-content').locator('button:has-text("Submit"):visible').click();
await this.waitForResponse({
httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: '/api/v1/db/meta/tables/',

2
tests/playwright/pages/Dashboard/WebhookForm/index.ts

@ -105,7 +105,7 @@ export class WebhookFormPage extends BasePage {
}
async save() {
const saveAction = this.saveButton.click();
const saveAction = () => this.saveButton.click();
await this.waitForResponse({
uiAction: saveAction,
requestUrlPathToMatch: '/hooks',

2
tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts

@ -16,7 +16,7 @@ export class RatingCellPageObject extends BasePage {
async select({ index, columnHeader, rating }: { index?: number; columnHeader: string; rating: number }) {
await this.waitForResponse({
uiAction: this.get({ index, columnHeader }).locator('.ant-rate-star > div').nth(rating).click(),
uiAction: () => this.get({ index, columnHeader }).locator('.ant-rate-star > div').nth(rating).click(),
httpMethodsToMatch: ['POST', 'PATCH'],
requestUrlPathToMatch: 'api/v1/db/data/noco/',
});

2
tests/playwright/pages/Dashboard/common/Cell/index.ts

@ -75,7 +75,7 @@ export class CellPageObject extends BasePage {
async inCellExpand({ index, columnHeader }: CellProps) {
await this.get({ index, columnHeader }).hover();
await this.waitForResponse({
uiAction: this.get({ index, columnHeader }).locator('.nc-action-icon >> nth=0').click(),
uiAction: () => this.get({ index, columnHeader }).locator('.nc-action-icon >> nth=0').click(),
requestUrlPathToMatch: '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'],
});

15
tests/playwright/pages/Dashboard/common/Toolbar/Fields.ts

@ -17,10 +17,8 @@ export class ToolbarFieldsPage extends BasePage {
// todo: Click and toggle are similar method. Remove one of them
async toggle({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) {
await this.toolbar.clickFields();
const toggleColumn = this.get()
.locator(`[data-testid="nc-fields-menu-${title}"]`)
.locator('input[type="checkbox"]')
.click();
const toggleColumn = () =>
this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]').click();
await this.waitForResponse({
uiAction: toggleColumn,
@ -43,7 +41,8 @@ export class ToolbarFieldsPage extends BasePage {
async click({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) {
await this.waitForResponse({
uiAction: this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]').click(),
uiAction: () =>
this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]').click(),
requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'],
});
@ -53,7 +52,7 @@ export class ToolbarFieldsPage extends BasePage {
async hideAll({ isLocallySaved }: { isLocallySaved?: boolean } = {}) {
await this.toolbar.clickFields();
await this.waitForResponse({
uiAction: this.get().locator(`button:has-text("Hide all")`).click(),
uiAction: () => this.get().locator(`button:has-text("Hide all")`).click(),
requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'],
});
@ -63,7 +62,7 @@ export class ToolbarFieldsPage extends BasePage {
async showAll({ isLocallySaved }: { isLocallySaved?: boolean } = {}) {
await this.toolbar.clickFields();
await this.waitForResponse({
uiAction: this.get().locator(`button:has-text("Show all")`).click(),
uiAction: () => this.get().locator(`button:has-text("Show all")`).click(),
requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'],
});
@ -73,7 +72,7 @@ export class ToolbarFieldsPage extends BasePage {
async toggleShowSystemFields({ isLocallySaved }: { isLocallySaved?: boolean } = {}) {
await this.toolbar.clickFields();
await this.waitForResponse({
uiAction: this.get().locator(`.nc-fields-show-system-fields`).click(),
uiAction: () => this.get().locator(`.nc-fields-show-system-fields`).click(),
requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'],
});

4
tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts

@ -137,7 +137,7 @@ export class ToolbarFilterPage extends BasePage {
}
break;
default:
fillFilter = this.rootPage.locator('.nc-filter-value-select > input').last().fill(value);
fillFilter = () => this.rootPage.locator('.nc-filter-value-select > input').last().fill(value);
await this.waitForResponse({
uiAction: fillFilter,
httpMethodsToMatch: ['GET'],
@ -154,7 +154,7 @@ export class ToolbarFilterPage extends BasePage {
await this.toolbar.clickFilter();
if (networkValidation) {
await this.waitForResponse({
uiAction: this.get().locator('.nc-filter-item-remove-btn').click(),
uiAction: () => this.get().locator('.nc-filter-item-remove-btn').click(),
httpMethodsToMatch: ['DELETE'],
requestUrlPathToMatch: '/api/v1/db/meta/filters/',
});

11
tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts

@ -61,11 +61,12 @@ export class ToolbarSortPage extends BasePage {
// await this.toolbar.parent.dashboard.waitForLoaderToDisappear();
await this.rootPage.locator('.nc-sort-dir-select').last().click();
const selectSortDirection = this.rootPage
.locator('.nc-dropdown-sort-dir')
.locator('.ant-select-item')
.nth(isAscending ? 0 : 1)
.click();
const selectSortDirection = () =>
this.rootPage
.locator('.nc-dropdown-sort-dir')
.locator('.ant-select-item')
.nth(isAscending ? 0 : 1)
.click();
await this.waitForResponse({
uiAction: selectSortDirection,

6
tests/playwright/pages/Dashboard/common/Toolbar/index.ts

@ -82,10 +82,10 @@ export class ToolbarPage extends BasePage {
}: { networkValidation?: boolean } = {}) {
const menuOpen = await this.filter.get().isVisible();
const clickFilterAction = this.get().locator(`button.nc-filter-menu-btn`).click();
const clickFilterAction = () => this.get().locator(`button.nc-filter-menu-btn`).click();
// Wait for the menu to close
if (menuOpen) {
await clickFilterAction;
await clickFilterAction();
await this.filter.get().waitFor({ state: 'hidden' });
} else {
if (networkValidation) {
@ -96,7 +96,7 @@ export class ToolbarPage extends BasePage {
httpMethodsToMatch: ['GET'],
});
} else {
await clickFilterAction;
await clickFilterAction();
}
}
}

8
tests/playwright/pages/ProjectsPage/index.ts

@ -26,7 +26,7 @@ export class ProjectsPage extends BasePage {
await this.rootPage.locator(`.nc-metadb-project-name`).waitFor();
await this.rootPage.locator(`input.nc-metadb-project-name`).fill(name);
const createProjectSubmitAction = this.rootPage.locator(`button:has-text("Create")`).click();
const createProjectSubmitAction = () => this.rootPage.locator(`button:has-text("Create")`).click();
await this.waitForResponse({
uiAction: createProjectSubmitAction,
httpMethodsToMatch: ['POST'],
@ -42,7 +42,7 @@ export class ProjectsPage extends BasePage {
}
async reloadProjects() {
const reloadUiAction = this.get().locator('[data-testid="projects-reload-button"]').click();
const reloadUiAction = () => this.get().locator('[data-testid="projects-reload-button"]').click();
await this.waitForResponse({
uiAction: reloadUiAction,
requestUrlPathToMatch: '/api/v1/db/meta/projects',
@ -119,7 +119,7 @@ export class ProjectsPage extends BasePage {
await this.get().locator(`[data-testid="delete-project-${title}"]`).click();
const deleteProjectAction = this.rootPage.locator(`button:has-text("Yes")`).click();
const deleteProjectAction = () => this.rootPage.locator(`button:has-text("Yes")`).click();
await this.waitForResponse({
uiAction: deleteProjectAction,
httpMethodsToMatch: ['DELETE'],
@ -149,7 +149,7 @@ export class ProjectsPage extends BasePage {
await project.locator('input.nc-metadb-project-name').fill(newTitle);
// press enter to save
const submitAction = project.locator('input.nc-metadb-project-name').press('Enter');
const submitAction = () => project.locator('input.nc-metadb-project-name').press('Enter');
await this.waitForResponse({
uiAction: submitAction,
requestUrlPathToMatch: 'api/v1/db/meta/projects/',

2
tests/playwright/pages/SharedForm/index.ts

@ -16,7 +16,7 @@ export class SharedFormPage extends BasePage {
async submit() {
await this.waitForResponse({
uiAction: this.get().getByTestId('shared-form-submit-button').click(),
uiAction: () => this.get().getByTestId('shared-form-submit-button').click(),
httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: '/rows',
});

Loading…
Cancel
Save