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 ```js
async resetFilter() { async resetFilter() {
await this.waitForResponse({ 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'], httpMethodsToMatch: ['DELETE'],
requestUrlPathToMatch: '/api/v1/db/meta/filters/', 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. - Open `Summary` tab in the CI workflow in github actions.
- Scroll down to `Artifacts` section. - 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. - 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", "lru-cache": "^6.0.0",
"mailersend": "^1.1.0", "mailersend": "^1.1.0",
"minio": "^7.0.18", "minio": "^7.0.18",
"mkdirp": "^0.5.5", "mkdirp": "^2.1.3",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"mssql": "^6.2.0", "mssql": "^6.2.0",
"multer": "^1.4.2", "multer": "^1.4.2",
@ -4259,6 +4259,18 @@
"run-queue": "^1.0.0" "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": { "node_modules/copy-descriptor": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
@ -10321,6 +10333,17 @@
"node": ">8 <=18" "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": { "node_modules/minio/node_modules/through2": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
@ -10510,14 +10533,17 @@
} }
}, },
"node_modules/mkdirp": { "node_modules/mkdirp": {
"version": "0.5.6", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.3.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "integrity": "sha512-sjAkg21peAG9HS+Dkx7hlG9Ztx7HLeKnvB3NQRcu/mltCVmvkF0pisbiTSfDVYTT86XEfZrTUosLdZLStquZUw==",
"dependencies": {
"minimist": "^1.2.6"
},
"bin": { "bin": {
"mkdirp": "bin/cmd.js" "mkdirp": "dist/cjs/src/bin.js"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/mocha": { "node_modules/mocha": {
@ -10874,6 +10900,18 @@
"run-queue": "^1.0.3" "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": { "node_modules/move-file": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/move-file/-/move-file-1.2.0.tgz", "resolved": "https://registry.npmjs.org/move-file/-/move-file-1.2.0.tgz",
@ -11021,6 +11059,17 @@
"node": ">= 0.10.0" "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": { "node_modules/multi-stage-sourcemap": {
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/multi-stage-sourcemap/-/multi-stage-sourcemap-0.3.1.tgz", "resolved": "https://registry.npmjs.org/multi-stage-sourcemap/-/multi-stage-sourcemap-0.3.1.tgz",
@ -17153,6 +17202,17 @@
"node": ">= 0.12.0" "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": { "node_modules/utils-merge": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@ -18318,6 +18378,18 @@
"node": ">=0.10.0" "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": { "node_modules/webpack/node_modules/readable-stream": {
"version": "2.3.7", "version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@ -22371,6 +22443,17 @@
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"rimraf": "^2.5.4", "rimraf": "^2.5.4",
"run-queue": "^1.0.0" "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": { "copy-descriptor": {
@ -27047,6 +27130,14 @@
"xml2js": "^0.4.15" "xml2js": "^0.4.15"
}, },
"dependencies": { "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": { "through2": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
@ -27213,12 +27304,9 @@
} }
}, },
"mkdirp": { "mkdirp": {
"version": "0.5.6", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.3.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "integrity": "sha512-sjAkg21peAG9HS+Dkx7hlG9Ztx7HLeKnvB3NQRcu/mltCVmvkF0pisbiTSfDVYTT86XEfZrTUosLdZLStquZUw=="
"requires": {
"minimist": "^1.2.6"
}
}, },
"mocha": { "mocha": {
"version": "10.1.0", "version": "10.1.0",
@ -27489,6 +27577,17 @@
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"rimraf": "^2.5.4", "rimraf": "^2.5.4",
"run-queue": "^1.0.3" "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": { "move-file": {
@ -27612,6 +27711,16 @@
"on-finished": "^2.3.0", "on-finished": "^2.3.0",
"type-is": "^1.6.4", "type-is": "^1.6.4",
"xtend": "^4.0.0" "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": { "multi-stage-sourcemap": {
@ -32428,6 +32537,16 @@
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"mz": "^2.7.0", "mz": "^2.7.0",
"unescape": "^1.0.1" "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": { "utils-merge": {
@ -33032,6 +33151,15 @@
"to-regex": "^3.0.2" "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": { "readable-stream": {
"version": "2.3.7", "version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "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", "lru-cache": "^6.0.0",
"mailersend": "^1.1.0", "mailersend": "^1.1.0",
"minio": "^7.0.18", "minio": "^7.0.18",
"mkdirp": "^0.5.5", "mkdirp": "^2.1.3",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"mssql": "^6.2.0", "mssql": "^6.2.0",
"multer": "^1.4.2", "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 newPassword.fill(newPass);
await confirmPassword.fill(repeatPass); 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) { if (networkValidation) {
await this.waitForResponse({ await this.waitForResponse({
uiAction: submitChangePassword, uiAction: submitChangePassword,
@ -37,7 +38,7 @@ export class ChangePasswordPage extends BasePage {
requestUrlPathToMatch: 'api/v1/auth/password/change', requestUrlPathToMatch: 'api/v1/auth/password/change',
}); });
} else { } 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 // A function that takes the response body and returns true if the response is the one we are looking for
responseJsonMatcher, responseJsonMatcher,
}: { }: {
uiAction: Promise<any>; uiAction: () => Promise<any>;
requestUrlPathToMatch: string; requestUrlPathToMatch: string;
httpMethodsToMatch?: string[]; httpMethodsToMatch?: string[];
responseJsonMatcher?: ResponseSelector; responseJsonMatcher?: ResponseSelector;
}) { }) {
await Promise.all([ const waitForResposePromise = this.rootPage.waitForResponse(async res => {
this.rootPage.waitForResponse(async res => { let isResJsonMatched = true;
let isResJsonMatched = true; if (responseJsonMatcher) {
if (responseJsonMatcher) { try {
try { isResJsonMatched = responseJsonMatcher(await res.json());
isResJsonMatched = responseJsonMatcher(await res.json()); } catch (e) {
} catch (e) { return false;
return false;
}
} }
}
return ( return (
res.request().url().includes(requestUrlPathToMatch) && res.request().url().includes(requestUrlPathToMatch) &&
httpMethodsToMatch.includes(res.request().method()) && httpMethodsToMatch.includes(res.request().method()) &&
isResJsonMatched isResJsonMatched
); );
}), });
uiAction,
]); const uiActionPromise = uiAction();
await Promise.all([waitForResposePromise, uiActionPromise]);
} }
async attachFile({ filePickUIAction, filePath }: { filePickUIAction: Promise<any>; filePath: string[] }) { 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(); await dropdownList.locator('.ant-dropdown-menu-item:has-text("Save & Stay")').click();
} }
const saveRowAction = saveAndExitMode const saveRowAction = () =>
? this.get().locator('button:has-text("Save & Exit")').click() saveAndExitMode
: this.get().locator('button:has-text("Save & Stay")').click(); ? this.get().locator('button:has-text("Save & Exit")').click()
: this.get().locator('button:has-text("Save & Stay")').click();
if (waitForRowsData) { if (waitForRowsData) {
await this.waitForResponse({ 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 }) { 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({ await this.waitForResponse({
requestUrlPathToMatch: '/exclude', requestUrlPathToMatch: '/exclude',
httpMethodsToMatch: ['GET'], httpMethodsToMatch: ['GET'],

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

@ -330,7 +330,7 @@ export class ColumnPageObject extends BasePage {
} }
await this.waitForResponse({ 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', requestUrlPathToMatch: 'api/v1/db/meta/views',
httpMethodsToMatch: ['PATCH'], httpMethodsToMatch: ['PATCH'],
}); });
@ -340,7 +340,7 @@ export class ColumnPageObject extends BasePage {
async save({ isUpdated }: { isUpdated?: boolean } = {}) { async save({ isUpdated }: { isUpdated?: boolean } = {}) {
await this.waitForResponse({ 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/', requestUrlPathToMatch: 'api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'], httpMethodsToMatch: ['GET'],
responseJsonMatcher: json => json['pageInfo'], 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(); await this.grid.get().locator(`th[data-title="${title}"] .nc-ui-dt-dropdown`).click();
let menuOption; let menuOption;
if (direction === 'desc') { 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 { } 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({ 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 }); await this._fillRow({ index, columnHeader, value: rowValue });
const clickOnColumnHeaderToSave = this.get() const clickOnColumnHeaderToSave = () =>
.locator(`[data-title="${columnHeader}"]`) this.get().locator(`[data-title="${columnHeader}"]`).locator(`span[title="${columnHeader}"]`).click();
.locator(`span[title="${columnHeader}"]`)
.click();
if (networkValidation) { if (networkValidation) {
await this.waitForResponse({ await this.waitForResponse({
@ -92,6 +90,7 @@ export class GridPage extends BasePage {
responseJsonMatcher: resJson => resJson?.[columnHeader] === rowValue, responseJsonMatcher: resJson => resJson?.[columnHeader] === rowValue,
}); });
} else { } else {
await clickOnColumnHeaderToSave();
await this.rootPage.waitForTimeout(300); await this.rootPage.waitForTimeout(300);
} }
@ -111,10 +110,8 @@ export class GridPage extends BasePage {
}) { }) {
await this._fillRow({ index, columnHeader, value }); await this._fillRow({ index, columnHeader, value });
const clickOnColumnHeaderToSave = this.get() const clickOnColumnHeaderToSave = () =>
.locator(`[data-title="${columnHeader}"]`) this.get().locator(`[data-title="${columnHeader}"]`).locator(`span[title="${columnHeader}"]`).click();
.locator(`span[title="${columnHeader}"]`)
.click();
if (networkValidation) { if (networkValidation) {
await this.waitForResponse({ await this.waitForResponse({
@ -128,6 +125,7 @@ export class GridPage extends BasePage {
responseJsonMatcher: resJson => resJson?.[columnHeader] === value, responseJsonMatcher: resJson => resJson?.[columnHeader] === value,
}); });
} else { } else {
await clickOnColumnHeaderToSave();
await this.rootPage.waitForTimeout(300); await this.rootPage.waitForTimeout(300);
} }
@ -231,7 +229,7 @@ export class GridPage extends BasePage {
async clickPagination({ page }: { page: string }) { async clickPagination({ page }: { page: string }) {
await this.waitForResponse({ await this.waitForResponse({
uiAction: (await this.pagination({ page })).click(), uiAction: async () => (await this.pagination({ page })).click(),
httpMethodsToMatch: ['GET'], httpMethodsToMatch: ['GET'],
requestUrlPathToMatch: '/views/', requestUrlPathToMatch: '/views/',
responseJsonMatcher: resJson => resJson?.pageInfo, responseJsonMatcher: resJson => resJson?.pageInfo,

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

@ -65,7 +65,7 @@ export class ImportTemplatePage extends BasePage {
await this.waitForResponse({ await this.waitForResponse({
requestUrlPathToMatch: '/api/v1/db/data/noco/', requestUrlPathToMatch: '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'], 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({ await this.dashboard.waitForTabRender({
title: tblList[0], title: tblList[0],

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

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

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

@ -14,7 +14,7 @@ export class MiscSettingsPage extends BasePage {
} }
async clickShowM2MTables() { 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({ await this.waitForResponse({
uiAction: clickAction, uiAction: clickAction,
requestUrlPathToMatch: 'tables?includeM2M', requestUrlPathToMatch: 'tables?includeM2M',

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

@ -54,7 +54,7 @@ export class TreeViewPage extends BasePage {
if (networkResponse === true) { if (networkResponse === true) {
await this.waitForResponse({ 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'], httpMethodsToMatch: ['GET'],
requestUrlPathToMatch: `/api/v1/db/data/noco/`, requestUrlPathToMatch: `/api/v1/db/data/noco/`,
responseJsonMatcher: json => json.pageInfo, 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.dashboard.get().getByPlaceholder('Enter table name').fill(title);
await this.waitForResponse({ 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'], httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: `/api/v1/db/meta/projects/`, requestUrlPathToMatch: `/api/v1/db/meta/projects/`,
responseJsonMatcher: json => json.title === title && json.type === 'table', 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.dashboard.get().locator('div.nc-project-menu-item:has-text("Delete")').click();
await this.waitForResponse({ 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'], httpMethodsToMatch: ['DELETE'],
requestUrlPathToMatch: `/api/v1/db/meta/tables/`, 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 }) { private async createView({ title, locator }: { title: string; locator: Locator }) {
await locator.click(); await locator.click();
await this.rootPage.locator('input[id="form_item_title"]:visible').fill(title); await this.rootPage.locator('input[id="form_item_title"]:visible').fill(title);
const submitAction = this.rootPage const submitAction = () =>
.locator('.ant-modal-content') this.rootPage.locator('.ant-modal-content').locator('button:has-text("Submit"):visible').click();
.locator('button:has-text("Submit"):visible')
.click();
await this.waitForResponse({ await this.waitForResponse({
httpMethodsToMatch: ['POST'], httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: '/api/v1/db/meta/tables/', requestUrlPathToMatch: '/api/v1/db/meta/tables/',
@ -128,10 +126,8 @@ export class ViewSidebarPage extends BasePage {
.locator(`[data-testid="view-sidebar-view-actions-${title}"]`) .locator(`[data-testid="view-sidebar-view-actions-${title}"]`)
.locator('.nc-view-copy-icon') .locator('.nc-view-copy-icon')
.click(); .click();
const submitAction = this.rootPage const submitAction = () =>
.locator('.ant-modal-content') this.rootPage.locator('.ant-modal-content').locator('button:has-text("Submit"):visible').click();
.locator('button:has-text("Submit"):visible')
.click();
await this.waitForResponse({ await this.waitForResponse({
httpMethodsToMatch: ['POST'], httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: '/api/v1/db/meta/tables/', 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() { async save() {
const saveAction = this.saveButton.click(); const saveAction = () => this.saveButton.click();
await this.waitForResponse({ await this.waitForResponse({
uiAction: saveAction, uiAction: saveAction,
requestUrlPathToMatch: '/hooks', 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 }) { async select({ index, columnHeader, rating }: { index?: number; columnHeader: string; rating: number }) {
await this.waitForResponse({ 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'], httpMethodsToMatch: ['POST', 'PATCH'],
requestUrlPathToMatch: 'api/v1/db/data/noco/', 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) { async inCellExpand({ index, columnHeader }: CellProps) {
await this.get({ index, columnHeader }).hover(); await this.get({ index, columnHeader }).hover();
await this.waitForResponse({ 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/', requestUrlPathToMatch: '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'], 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 // todo: Click and toggle are similar method. Remove one of them
async toggle({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) { async toggle({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) {
await this.toolbar.clickFields(); await this.toolbar.clickFields();
const toggleColumn = this.get() const toggleColumn = () =>
.locator(`[data-testid="nc-fields-menu-${title}"]`) this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]').click();
.locator('input[type="checkbox"]')
.click();
await this.waitForResponse({ await this.waitForResponse({
uiAction: toggleColumn, uiAction: toggleColumn,
@ -43,7 +41,8 @@ export class ToolbarFieldsPage extends BasePage {
async click({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) { async click({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) {
await this.waitForResponse({ 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/', requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'], httpMethodsToMatch: ['GET'],
}); });
@ -53,7 +52,7 @@ export class ToolbarFieldsPage extends BasePage {
async hideAll({ isLocallySaved }: { isLocallySaved?: boolean } = {}) { async hideAll({ isLocallySaved }: { isLocallySaved?: boolean } = {}) {
await this.toolbar.clickFields(); await this.toolbar.clickFields();
await this.waitForResponse({ 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/', requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'], httpMethodsToMatch: ['GET'],
}); });
@ -63,7 +62,7 @@ export class ToolbarFieldsPage extends BasePage {
async showAll({ isLocallySaved }: { isLocallySaved?: boolean } = {}) { async showAll({ isLocallySaved }: { isLocallySaved?: boolean } = {}) {
await this.toolbar.clickFields(); await this.toolbar.clickFields();
await this.waitForResponse({ 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/', requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'], httpMethodsToMatch: ['GET'],
}); });
@ -73,7 +72,7 @@ export class ToolbarFieldsPage extends BasePage {
async toggleShowSystemFields({ isLocallySaved }: { isLocallySaved?: boolean } = {}) { async toggleShowSystemFields({ isLocallySaved }: { isLocallySaved?: boolean } = {}) {
await this.toolbar.clickFields(); await this.toolbar.clickFields();
await this.waitForResponse({ 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/', requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'], httpMethodsToMatch: ['GET'],
}); });

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

@ -137,7 +137,7 @@ export class ToolbarFilterPage extends BasePage {
} }
break; break;
default: 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({ await this.waitForResponse({
uiAction: fillFilter, uiAction: fillFilter,
httpMethodsToMatch: ['GET'], httpMethodsToMatch: ['GET'],
@ -154,7 +154,7 @@ export class ToolbarFilterPage extends BasePage {
await this.toolbar.clickFilter(); await this.toolbar.clickFilter();
if (networkValidation) { if (networkValidation) {
await this.waitForResponse({ 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'], httpMethodsToMatch: ['DELETE'],
requestUrlPathToMatch: '/api/v1/db/meta/filters/', 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.toolbar.parent.dashboard.waitForLoaderToDisappear();
await this.rootPage.locator('.nc-sort-dir-select').last().click(); await this.rootPage.locator('.nc-sort-dir-select').last().click();
const selectSortDirection = this.rootPage const selectSortDirection = () =>
.locator('.nc-dropdown-sort-dir') this.rootPage
.locator('.ant-select-item') .locator('.nc-dropdown-sort-dir')
.nth(isAscending ? 0 : 1) .locator('.ant-select-item')
.click(); .nth(isAscending ? 0 : 1)
.click();
await this.waitForResponse({ await this.waitForResponse({
uiAction: selectSortDirection, uiAction: selectSortDirection,

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

@ -82,10 +82,10 @@ export class ToolbarPage extends BasePage {
}: { networkValidation?: boolean } = {}) { }: { networkValidation?: boolean } = {}) {
const menuOpen = await this.filter.get().isVisible(); 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 // Wait for the menu to close
if (menuOpen) { if (menuOpen) {
await clickFilterAction; await clickFilterAction();
await this.filter.get().waitFor({ state: 'hidden' }); await this.filter.get().waitFor({ state: 'hidden' });
} else { } else {
if (networkValidation) { if (networkValidation) {
@ -96,7 +96,7 @@ export class ToolbarPage extends BasePage {
httpMethodsToMatch: ['GET'], httpMethodsToMatch: ['GET'],
}); });
} else { } 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(`.nc-metadb-project-name`).waitFor();
await this.rootPage.locator(`input.nc-metadb-project-name`).fill(name); 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({ await this.waitForResponse({
uiAction: createProjectSubmitAction, uiAction: createProjectSubmitAction,
httpMethodsToMatch: ['POST'], httpMethodsToMatch: ['POST'],
@ -42,7 +42,7 @@ export class ProjectsPage extends BasePage {
} }
async reloadProjects() { 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({ await this.waitForResponse({
uiAction: reloadUiAction, uiAction: reloadUiAction,
requestUrlPathToMatch: '/api/v1/db/meta/projects', 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(); 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({ await this.waitForResponse({
uiAction: deleteProjectAction, uiAction: deleteProjectAction,
httpMethodsToMatch: ['DELETE'], httpMethodsToMatch: ['DELETE'],
@ -149,7 +149,7 @@ export class ProjectsPage extends BasePage {
await project.locator('input.nc-metadb-project-name').fill(newTitle); await project.locator('input.nc-metadb-project-name').fill(newTitle);
// press enter to save // 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({ await this.waitForResponse({
uiAction: submitAction, uiAction: submitAction,
requestUrlPathToMatch: 'api/v1/db/meta/projects/', requestUrlPathToMatch: 'api/v1/db/meta/projects/',

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

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

Loading…
Cancel
Save