+
+
+
+
+
diff --git a/packages/nc-gui/components/dlg/AirtableImport.vue b/packages/nc-gui/components/dlg/AirtableImport.vue
index 73cb9905f9..d94329e29d 100644
--- a/packages/nc-gui/components/dlg/AirtableImport.vue
+++ b/packages/nc-gui/components/dlg/AirtableImport.vue
@@ -57,6 +57,7 @@ const syncSource = ref({
syncDirection: 'Airtable to NocoDB',
syncRetryCount: 1,
apiKey: '',
+ appId: '',
shareId: '',
syncSourceUrlOrId: '',
options: {
@@ -158,7 +159,8 @@ async function loadSyncSrc() {
if (srcs && srcs[0]) {
srcs[0].details = srcs[0].details || {}
syncSource.value = migrateSync(srcs[0])
- syncSource.value.details.syncSourceUrlOrId = srcs[0].details.shareId
+ syncSource.value.details.syncSourceUrlOrId =
+ srcs[0].details.appId && srcs[0].details.appId.length > 0 ? srcs[0].details.syncSourceUrlOrId : srcs[0].details.shareId
$jobs.subscribe({ syncId: syncSource.value.id }, onSubscribe, onStatus, onLog)
} else {
syncSource.value = {
@@ -169,6 +171,7 @@ async function loadSyncSrc() {
syncDirection: 'Airtable to NocoDB',
syncRetryCount: 1,
apiKey: '',
+ appId: '',
shareId: '',
syncSourceUrlOrId: '',
options: {
@@ -242,6 +245,8 @@ watch(
if (syncSource.value.details) {
const m = v && v.match(/(exp|shr).{14}/g)
syncSource.value.details.shareId = m ? m[0] : ''
+ const m2 = v && v.match(/(app).{14}/g)
+ syncSource.value.details.appId = m2 ? m2[0] : ''
}
},
)
@@ -296,7 +301,7 @@ onMounted(async () => {
diff --git a/packages/nc-gui/components/smartsheet/Gallery.vue b/packages/nc-gui/components/smartsheet/Gallery.vue
index 43b4703e23..40ba4a9a6c 100644
--- a/packages/nc-gui/components/smartsheet/Gallery.vue
+++ b/packages/nc-gui/components/smartsheet/Gallery.vue
@@ -39,6 +39,7 @@ const reloadViewDataHook = inject(ReloadViewDataHookInj)
const openNewRecordFormHook = inject(OpenNewRecordFormHookInj, createEventHook())
const { isViewDataLoading } = storeToRefs(useViewsStore())
+const { isSqlView, xWhere } = useSmartsheetStoreOrThrow()
const expandedFormDlg = ref(false)
const expandedFormRow = ref()
@@ -54,7 +55,7 @@ const {
addEmptyRow,
deleteRow,
navigateToSiblingRow,
-} = useViewData(meta, view)
+} = useViewData(meta, view, xWhere)
provide(IsFormInj, ref(false))
provide(IsGalleryInj, ref(true))
@@ -85,8 +86,6 @@ const isRowEmpty = (record: any, col: any) => {
return Array.isArray(val) && val.length === 0
}
-const { isSqlView } = useSmartsheetStoreOrThrow()
-
const { isUIAllowed } = useUIPermission()
const hasEditPermission = computed(() => isUIAllowed('xcDatatableEditable'))
// TODO: extract this code (which is duplicated in grid and gallery) into a separate component
diff --git a/packages/nc-gui/components/smartsheet/column/AdvancedOptions.vue b/packages/nc-gui/components/smartsheet/column/AdvancedOptions.vue
index 417c05c11a..05e9e7594b 100644
--- a/packages/nc-gui/components/smartsheet/column/AdvancedOptions.vue
+++ b/packages/nc-gui/components/smartsheet/column/AdvancedOptions.vue
@@ -50,7 +50,7 @@ vModel.value.au = !!vModel.value.au */
@@ -59,7 +59,6 @@ vModel.value.au = !!vModel.value.au */
@@ -68,17 +67,17 @@ vModel.value.au = !!vModel.value.au */
-
+
-
+
@@ -96,13 +95,13 @@ vModel.value.au = !!vModel.value.au */
-
+
diff --git a/packages/nc-gui/components/smartsheet/grid/Table.vue b/packages/nc-gui/components/smartsheet/grid/Table.vue
index b7075a1335..ef05be4d58 100644
--- a/packages/nc-gui/components/smartsheet/grid/Table.vue
+++ b/packages/nc-gui/components/smartsheet/grid/Table.vue
@@ -1124,7 +1124,7 @@ defineExpose({
})
// when expand is clicked the drawer should open
-// and cell should loose focs
+// and cell should loose focus
const expandAndLooseFocus = (row: Row, col: Record) => {
if (expandForm) {
expandForm(row, col)
diff --git a/packages/nc-gui/components/smartsheet/header/Cell.vue b/packages/nc-gui/components/smartsheet/header/Cell.vue
index 4ba762705d..dc287fb699 100644
--- a/packages/nc-gui/components/smartsheet/header/Cell.vue
+++ b/packages/nc-gui/components/smartsheet/header/Cell.vue
@@ -1,6 +1,6 @@
-
-
-
-
{{ $t('title.appStore') }}
-
-
-
-
diff --git a/packages/nc-gui/pages/index-old/index.vue b/packages/nc-gui/pages/index-old/index.vue
deleted file mode 100644
index 0e7d4a931d..0000000000
--- a/packages/nc-gui/pages/index-old/index.vue
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
diff --git a/packages/nc-gui/pages/index-old/index/[projectId].vue b/packages/nc-gui/pages/index-old/index/[projectId].vue
deleted file mode 100644
index 35862ff1e4..0000000000
--- a/packages/nc-gui/pages/index-old/index/[projectId].vue
+++ /dev/null
@@ -1,110 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
{{ $t('activity.editProject') }}
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $t('general.edit') }}
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/index-old/index/create-external.vue b/packages/nc-gui/pages/index-old/index/create-external.vue
deleted file mode 100644
index d81a051f0a..0000000000
--- a/packages/nc-gui/pages/index-old/index/create-external.vue
+++ /dev/null
@@ -1,643 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
{{ $t('activity.createProject') }}
-
-
-
-
-
-
-
-
-
- {{ client.text }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $t('activity.useConnectionUrl') }}
-
-
{{ $t('title.advancedParameters') }}
-
-
-
-
- {{ opt }}
-
-
-
-
-
-
-
-
- {{ $t('tooltip.clientCert') }}
-
-
-
- {{ $t('labels.clientCert') }}
-
-
-
-
-
-
- {{ $t('tooltip.clientKey') }}
-
-
- {{ $t('labels.clientKey') }}
-
-
-
-
-
-
- {{ $t('tooltip.clientCA') }}
-
-
-
- {{ $t('labels.serverCA') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ type }}
-
-
-
-
-
- {{ type }}
-
-
-
-
-
-
- {{ $t('activity.editConnJson') }}
-
-
-
-
-
-
-
-
-
- {{ $t('activity.testDbConn') }}
-
-
-
- {{ $t('general.submit') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/index-old/index/create.vue b/packages/nc-gui/pages/index-old/index/create.vue
deleted file mode 100644
index 0ccc063328..0000000000
--- a/packages/nc-gui/pages/index-old/index/create.vue
+++ /dev/null
@@ -1,171 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ $t('activity.createProject') }}
-
-
-
-
-
-
-
-
-
-
-
- {{ $t('general.create') }}
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/index-old/index/index-old.vue b/packages/nc-gui/pages/index-old/index/index-old.vue
deleted file mode 100644
index 556204e9d4..0000000000
--- a/packages/nc-gui/pages/index-old/index/index-old.vue
+++ /dev/null
@@ -1,354 +0,0 @@
-
-
-
-
-
- {{ $t('title.myProject') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ text }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/index-old/index/index.vue b/packages/nc-gui/pages/index-old/index/index.vue
deleted file mode 100644
index dc7795fe99..0000000000
--- a/packages/nc-gui/pages/index-old/index/index.vue
+++ /dev/null
@@ -1,306 +0,0 @@
-
-
-
-
-
-
-
-
All Projects
-
-
- Recent
-
-
-
- Shared with me
-
-
-
- Starred
-
-
-
-
-
-
-
-
-
- {{ projectListType }}
-
-
-
-
-
-
-
-
{{ $t('title.newProj') }}
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/index-old/index/user.vue b/packages/nc-gui/pages/index-old/index/user.vue
deleted file mode 100644
index 37a87b1335..0000000000
--- a/packages/nc-gui/pages/index-old/index/user.vue
+++ /dev/null
@@ -1,162 +0,0 @@
-
-
-
-
-
-
-
router.back()"
- >
-
-
-
-
{{ $t('activity.changePwd') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $t('activity.changePwd') }}
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/index.vue b/packages/nc-gui/pages/index.vue
index 2f7583f126..d4cdaf1485 100644
--- a/packages/nc-gui/pages/index.vue
+++ b/packages/nc-gui/pages/index.vue
@@ -35,6 +35,12 @@ const isSharedView = computed(() => {
// check route is not project page by route name
return !routeName.startsWith('index-typeOrId-projectId-') && !['index', 'index-typeOrId'].includes(routeName)
})
+const isSharedFormView = computed(() => {
+ const routeName = (route.value.name as string) || ''
+
+ // check route is shared form view route
+ return routeName.startsWith('index-typeOrId-form-viewId')
+})
watch(
() => route.value.params.typeOrId,
@@ -67,7 +73,10 @@ provide(ToggleDialogInj, toggleDialog)
-
+
+
+
+
diff --git a/packages/nc-gui/pages/index/[typeOrId].vue b/packages/nc-gui/pages/index/[typeOrId].vue
index 5800abe05f..60bd7eeeab 100644
--- a/packages/nc-gui/pages/index/[typeOrId].vue
+++ b/packages/nc-gui/pages/index/[typeOrId].vue
@@ -5,7 +5,7 @@ const route = router.currentRoute
-
+
diff --git a/packages/nc-gui/pages/index/[typeOrId]/form/[viewId].vue b/packages/nc-gui/pages/index/[typeOrId]/form/[viewId].vue
index 7645e223f6..35ea03dcba 100644
--- a/packages/nc-gui/pages/index/[typeOrId]/form/[viewId].vue
+++ b/packages/nc-gui/pages/index/[typeOrId]/form/[viewId].vue
@@ -59,7 +59,7 @@ watch(
-
+
-
+
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index.vue
deleted file mode 100644
index 10666f5a3b..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index.vue
+++ /dev/null
@@ -1,155 +0,0 @@
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index.vue
deleted file mode 100644
index 1d9eba69c8..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index.vue
+++ /dev/null
@@ -1,157 +0,0 @@
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/[type]/[viewId]/[[viewTitle]].vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/[type]/[viewId]/[[viewTitle]].vue
deleted file mode 100644
index 4e68db58e4..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/[type]/[viewId]/[[viewTitle]].vue
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/auth.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/auth.vue
deleted file mode 100644
index dd3d59cd47..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/auth.vue
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/erd/[baseId].vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/erd/[baseId].vue
deleted file mode 100644
index 240b432c67..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/erd/[baseId].vue
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/index.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/index.vue
deleted file mode 100644
index 10933e4238..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/index.vue
+++ /dev/null
@@ -1,133 +0,0 @@
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/sql/[baseId].vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/sql/[baseId].vue
deleted file mode 100644
index a154d08eb5..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/[projectId]/index/index/sql/[baseId].vue
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/base/[baseId].vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/base/[baseId].vue
deleted file mode 100644
index db36d11db1..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/base/[baseId].vue
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/cowriter/[projectId]/[...slugs].vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/cowriter/[projectId]/[...slugs].vue
deleted file mode 100644
index a154d08eb5..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/cowriter/[projectId]/[...slugs].vue
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId].vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId].vue
deleted file mode 100644
index 7645e223f6..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId].vue
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-
-
-
-
-
-
-
-
This shared view is protected
-
-
-
-
-
-
-
- {{ passwordError }}
-
-
-
- {{ $t('general.unlock') }}
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId]/index.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId]/index.vue
deleted file mode 100644
index 22ff34cebc..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId]/index.vue
+++ /dev/null
@@ -1,148 +0,0 @@
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId]/index/index.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId]/index/index.vue
deleted file mode 100644
index 9cfca9bab8..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId]/index/index.vue
+++ /dev/null
@@ -1,141 +0,0 @@
-
-
-
-
-
-
- {{ sharedFormView.heading }}
-
-
- {{ sharedFormView.subheading }}
-
-
-
-
-
-
-
-
-
-
- New form will be loaded after {{ secondsRemain }} seconds
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId]/index/survey.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId]/index/survey.vue
deleted file mode 100644
index f0e60b379a..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/form/[viewId]/index/survey.vue
+++ /dev/null
@@ -1,472 +0,0 @@
-
-
-
-
-
-
-
- {{ sharedFormView.heading }}
-
-
-
- {{ sharedFormView?.subheading }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ error.$message }}
-
-
-
- {{ field.description }}
-
-
-
- Shift + Enter
- to make a line break
-
-
-
-
-
-
-
-
-
- {{ $t('general.submit') }}
-
-
-
-
-
-
-
- Ok
-
-
-
-
-
-
-
-
-
-
-
- Press Enter
-
-
-
-
-
-
-
-
-
- {{ sharedFormView?.success_msg }}
-
-
-
-
-
Thank you!
-
-
You have successfully submitted the form data.
-
-
-
-
-
-
- New form will be loaded after {{ secondsRemain }} seconds
-
-
-
-
- Submit Another Form
-
-
-
-
-
-
-
-
-
-
-
- {{ index + 1 }} / {{ formColumns?.length }}
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/gallery/[viewId]/index.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/gallery/[viewId]/index.vue
deleted file mode 100644
index d76cb8ac11..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/gallery/[viewId]/index.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/index.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/index.vue
deleted file mode 100644
index a25bb89dc4..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/index.vue
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/kanban/[viewId]/index.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/kanban/[viewId]/index.vue
deleted file mode 100644
index 6271f585e3..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/kanban/[viewId]/index.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/map/[viewId]/index.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/map/[viewId]/index.vue
deleted file mode 100644
index 70f6791da4..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/map/[viewId]/index.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/shared/[erdUuid]/index.vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/shared/[erdUuid]/index.vue
deleted file mode 100644
index c3dbcf41e8..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/shared/[erdUuid]/index.vue
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
- {{ appInfo.version }}
-
-
-
-
{{ project.title }}
-
-
-
-
-
-
-
diff --git a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/view/[viewId].vue b/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/view/[viewId].vue
deleted file mode 100644
index c91c203d28..0000000000
--- a/packages/nc-gui/pages/ws/[typeOrId]/[[projectType]]/view/[viewId].vue
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/nc-lib-gui/package.json b/packages/nc-lib-gui/package.json
index 3c4e29343e..a490c3474e 100644
--- a/packages/nc-lib-gui/package.json
+++ b/packages/nc-lib-gui/package.json
@@ -1,6 +1,6 @@
{
"name": "nc-lib-gui",
- "version": "0.111.0",
+ "version": "0.111.1",
"description": "NocoDB GUI",
"author": {
"name": "NocoDB",
diff --git a/packages/noco-docs/docs/020.getting-started/010.installation.md b/packages/noco-docs/docs/020.getting-started/010.installation.md
index 0e01f27bef..9c598889ee 100644
--- a/packages/noco-docs/docs/020.getting-started/010.installation.md
+++ b/packages/noco-docs/docs/020.getting-started/010.installation.md
@@ -82,15 +82,6 @@ If you plan to input some special characters, you may need to change the charact
We provide different docker-compose.yml files under [this directory](https://github.com/nocodb/nocodb/tree/master/docker-compose). Here are some examples.
-
-
-```js
-function helloWorld() {
- console.log('Hello, world!');
-}
-```
-
-
```bash
@@ -319,7 +310,7 @@ If your service fails to start, you may check the logs in ECS console or in Clou
Click to Expand
-
+
#### Pull NocoDB Image on Cloud Shell
Since Cloud Run only supports images from Google Container Registry (GCR) or Artifact Registry, we need to pull NocoDB image, tag it and push it in GCP using Cloud Shell. Here are some sample commands which you can execute in Cloud Shell.
@@ -350,7 +341,7 @@ If your service fails to start, you may check the logs in ECS console or in Clou
Click to Expand
-
+
#### Create Apps
On Home page, Click on Create icon & Select Apps (Deploy your code).
@@ -392,7 +383,7 @@ If your service fails to start, you may check the logs in ECS console or in Clou
Click to Expand
-
+
#### Navigate to App Store
Log into Cloudron and select App Store
@@ -440,7 +431,7 @@ If your service fails to start, you may check the logs in ECS console or in Clou
Click to Expand
-
+
#### Navigate to Templates
Go to [Templates](https://railway.app/templates), Search NocoDB and click Deploy
diff --git a/packages/noco-docs/docs/020.getting-started/020.environment-variables.md b/packages/noco-docs/docs/020.getting-started/020.environment-variables.md
index c8bd42596f..e775554e43 100644
--- a/packages/noco-docs/docs/020.getting-started/020.environment-variables.md
+++ b/packages/noco-docs/docs/020.getting-started/020.environment-variables.md
@@ -12,7 +12,7 @@ For production usecases, it is **recommended** to configure
| Variable | Comments | If absent |
|---|---|---|
-| NC_DB | See our database URLs | A local SQLite will be created in root folder if `NC_DB` is not provided |
+| NC_DB | See our example database URLs [here](https://github.com/nocodb/nocodb#docker). | A local SQLite will be created in root folder if `NC_DB` is not provided |
| NC_DB_JSON | Can be used instead of `NC_DB` and value should be valid knex connection JSON | |
| NC_DB_JSON_FILE | Can be used instead of `NC_DB` and value should be a valid path to knex connection JSON | |
| DATABASE_URL | JDBC URL Format. Can be used instead of NC_DB. | |
diff --git a/packages/noco-docs/docs/030.setup-and-usages/061.links.md b/packages/noco-docs/docs/030.setup-and-usages/061.links.md
index 28930af8f7..4c850c1926 100644
--- a/packages/noco-docs/docs/030.setup-and-usages/061.links.md
+++ b/packages/noco-docs/docs/030.setup-and-usages/061.links.md
@@ -18,7 +18,7 @@ description: "Understanding Link Columns!"
Further details of relationship types can be found [here](https://afteracademy.com/blog/what-are-the-different-types-of-relationships-in-dbms)
-From Release v0.110.0, table records can be connected through relationships using the **Links** column type.
+From Release v0.111.0, table records can be connected through relationships using the **Links** column type.
It is important to note that, earlier supported column type **LinkToAnotherRecord** for creating relations is considered deprecated. While the old datatype is still supported for backward compatibility, it is no longer possible to create new fields of that type.
The main distinction between these two column types lies in how the contents are displayed within the cell when links are established between two tables. With the **LinkToAnotherRecord** column type, the cell displays the **Primary value** of the related records. On the other hand, the **Links** column type only shows the **count** of related records.
diff --git a/packages/noco-docs/docs/040.developer-resources/040.webhooks.md b/packages/noco-docs/docs/040.developer-resources/040.webhooks.md
index 0f0b7c5399..cfd38b608e 100644
--- a/packages/noco-docs/docs/040.developer-resources/040.webhooks.md
+++ b/packages/noco-docs/docs/040.developer-resources/040.webhooks.md
@@ -5,27 +5,298 @@ description: "Webhooks allows user to trigger on certain operations on following
## Overview
-Some types of notifications can be triggered by a webhook after a particular event.
+You can employ webhooks to notify external systems whenever there are additions, updates, or removals of rows within NocoDB. This feature allows you to receive instantaneous notifications for any changes made to your database.
+NocoDB also offers webhooks for bulk endpoints for creating, updating, or deleting multiple records simultaneously.
+
+Note that, Webhooks currently are specific for associated table.
+
-- Open `Details` tab in topbar, click on `Webhooks`
-- Click `Add New Webhook`
-![webhook](https://github.com/nocodb/nocodb/assets/86527202/dfd9e8ff-e175-4a9d-8a95-75e3c2f96873)
### Configure Webhook
-- General configurations
- - Webhook Name
- - Webhook Trigger
- - Webhook Type
-- Webhook Type specific configuration : additional configuration details depending on webhook type selected
-- Webhook Conditional Trigger
- - Only records meeting the criteria will trigger webhook
+To setup a new Webhook
+
+1. Open `Details` tab in topbar,
+2. Click on `Webhooks` tab
+3. Click `Add New Webhook`
+
+![webhook](https://github.com/nocodb/nocodb/assets/86527202/07f375af-f2c3-4d7c-9500-976f38b15c12)
+
+Webhook configuration
+
+![webhook config](https://github.com/nocodb/nocodb/assets/86527202/338c8f23-237c-4a00-870d-5221e00a1d34)
+
+
+1. Name
+
+2. Event
+ 1. After Insert
+ 2. After Update
+ 3. After Delete
+ 4. After Bulk Insert
+ 5. After Bulk Update
+ 6. After Bulk Delete
+
+3. Type
+
+| Trigger | Details |
+| --------------- | ---------------------------------------------- |
+| Email | Send email to certain email addresses |
+| Slack | Notify via Slack channel |
+| Microsoft Teams | Notify via Microsoft Teams channel |
+| Discord | Notify via Discord channel |
+| Mattermost | Notify via Mattermost channel |
+| Twilio | Send SMS to certain mobile numbers |
+| Whatsapp Twilio | Send Whatsapp messages to numbers using Twilio |
+| URL | Invoke an HTTP API |
+
+4. Action
+ 1. GET
+ 2. POST
+ 3. DELETE
+ 4. PUT
+ 5. HEAD
+ 6. PATCH
+
+5. Type specific configuration : additional configuration details depending on webhook type selected
+ Example: `Link` for type `URL`
+
+6. [Optional] Headers & Parameters :
+ Configure Request headers & parameters (if any)
+
+7. [Optional] Condition :
+ Only records meeting the criteria will trigger webhook
+
+8. [Optional] Test :
+ Test webhook (with sample payload) to verify if parameter are configured appropriately
+
+9. Save
+
-![webhook config](https://github.com/nocodb/nocodb/assets/86527202/3c95ed48-b763-44ad-b73e-2a28e049ec23)
+### Enable/Disable Webhook
+
+To disable a Webhook
+- Open `Webhook` tab to find list of webhooks created
+- Toggle `Activate` button to enable/disable
+
+![Screenshot 2023-09-01 at 3 59 28 PM](https://github.com/nocodb/nocodb/assets/86527202/c62cca12-6164-46a8-87e5-179d28c989b6)
+
+
+### Delete Webhook
+
+To delete a Webhook
+- Open `Webhook` tab to find list of webhooks created
+- Click on `...` actions button associated with the webhook to be deleted
+- Select `Delete`
+
+![Screenshot 2023-09-01 at 4 01 46 PM](https://github.com/nocodb/nocodb/assets/86527202/23a8aec1-ba29-4be4-8143-f3c94198a88c)
+
+
+### Duplicate Webhook
+
+To duplicate a Webhook
+- Open `Webhook` tab to find list of webhooks created
+- Click on `...` actions button associated with the webhook to be duplicate
+- Select `Duplicate`
+
+![Screenshot 2023-09-01 at 4 01 46 PM](https://github.com/nocodb/nocodb/assets/86527202/23a8aec1-ba29-4be4-8143-f3c94198a88c)
+
+A copy of the webhook will be created (disabled by default) with a suffix ` - Copy`
+
+
+### Webhook Response Sample
+
+#### Insert
+```
+{
+ "type": "records.after.insert",
+ "id": "9dac1c54-b3be-49a1-a676-af388145fa8c",
+ "data": {
+ "table_id": "md_xzru7dcqrecc60",
+ "table_name": "Film",
+ "view_id": "vw_736wrpoas7tr0c",
+ "view_name": "Film",
+ "rows": [
+ {
+ "FilmId": 1011,
+ "Title": "FOO",
+ "Language": {
+ "LanguageId": 1,
+ "Name": "English"
+ },
+ }
+ ]
+ }
+}
+```
+#### Update
+```
+{
+ "type": "records.after.update",
+ "id": "6a6ebfe4-b0b5-434e-b5d6-5212adbf82fa",
+ "data": {
+ "table_id": "md_xzru7dcqrecc60",
+ "table_name": "Film",
+ "view_id": "vw_736wrpoas7tr0c",
+ "view_name": "Film",
+ "previous_rows": [
+ {
+ "FilmId": 1,
+ "Title": "ACADEMY DINOSAUR",
+ "Description": "A Epic Drama of a Feminist in The Canadian Rockies",
+ "Actor List": [
+ {
+ "ActorId": 10,
+ "FirstName": "CHRISTIAN"
+ }
+ ],
+ }
+ ],
+ "rows": [
+ {
+ "FilmId": 1,
+ "Title": "ACADEMY DINOSAUR (Edited)",
+ "Actor List": [
+ {
+ "ActorId": 10,
+ "FirstName": "CHRISTIAN"
+ }
+ ],
+ }
+ ]
+ }
+}
+```
+#### Delete
+```
+{
+ "type": "records.after.delete",
+ "id": "e593079f-70e5-4965-8944-5ff7aeed005c",
+ "data": {
+ "table_id": "md_xzru7dcqrecc60",
+ "table_name": "Film",
+ "view_id": "vw_736wrpoas7tr0c",
+ "view_name": "Film",
+ "rows": [
+ {
+ "FilmId": 1010,
+ "Title": "ALL-EDITED",
+ "Language": {
+ "LanguageId": 1,
+ "Name": "English"
+ },
+ }
+ ]
+ }
+}
+```
+#### Bulk Insert
+```
+{
+ "type": "records.after.bulkInsert",
+ "id": "f8397b06-a399-4a3a-b6b0-6d1c0c2f7578",
+ "data": {
+ "table_id": "md_xzru7dcqrecc60",
+ "table_name": "Film",
+ "view_id": "vw_3fq2e9q8drkblw",
+ "view_name": "GridView",
+ "rows_inserted": 2
+ }
+}
+```
+
+#### Bulk Update
+```
+{
+ "type": "records.after.bulkUpdate",
+ "id": "e983cea5-8e38-438e-96a0-048751f6830b",
+ "data": {
+ "table_id": "md_xzru7dcqrecc60",
+ "table_name": "Film",
+ "view_id": "vw_3fq2e9q8drkblw",
+ "view_name": "Sheet-1",
+ "previous_rows": [
+ [
+ {
+ "FilmId": 1005,
+ "Title": "Q",
+ "Language": {
+ "LanguageId": 1,
+ "Name": "English"
+ },
+ },
+ {
+ "FilmId": 1004,
+ "Title": "P",
+ "Language": {
+ "LanguageId": 1,
+ "Name": "English"
+ }
+ }
+ ]
+ ],
+ "rows": [
+ [
+ {
+ "FilmId": 1005,
+ "Title": "Q-EDITED",
+ "Language": {
+ "LanguageId": 1,
+ "Name": "English"
+ }
+ },
+ {
+ "FilmId": 1004,
+ "Title": "P-EDITED",
+ "Language": {
+ "LanguageId": 1,
+ "Name": "English"
+ },
+ }
+ ]
+ ]
+ }
+}
+```
+#### Bulk Delete
+```
+{
+ "type": "records.after.bulkDelete",
+ "id": "e7f1f4e5-7052-4ca2-9355-241ceb836f43",
+ "data": {
+ "table_id": "md_xzru7dcqrecc60",
+ "table_name": "Film",
+ "view_id": "vw_3fq2e9q8drkblw",
+ "view_name": "Sheet-1",
+ "rows": [
+ [
+ {
+ "FilmId": 1022,
+ "Title": "x",
+ "Language": {
+ "LanguageId": 1,
+ "Name": "English"
+ },
+ },
+ {
+ "FilmId": 1023,
+ "Title": "x",
+ "Language": {
+ "LanguageId": 1,
+ "Name": "English"
+ },
+ }
+ ]
+ ]
+ }
+}
+```
+
+
## Call Log
Call Log allows user to check the call history of the hook. By default, it has been disabled. However, it can be configured by using environment variable `NC_AUTOMATION_LOG_LEVEL`.
diff --git a/packages/noco-docs/docusaurus.config.js b/packages/noco-docs/docusaurus.config.js
index d4a8a0e7ad..83849d1517 100644
--- a/packages/noco-docs/docusaurus.config.js
+++ b/packages/noco-docs/docusaurus.config.js
@@ -67,10 +67,10 @@ const config = {
// Replace with your project's social card
image: 'img/docusaurus-social-card.jpg',
navbar: {
- title: 'NocoDB',
+ title: '',
logo: {
alt: 'NocoDB',
- src: 'img/icon.png',
+ src: 'img/nocodb-full-color.png',
},
items: [
{
diff --git a/packages/noco-docs/src/css/header.scss b/packages/noco-docs/src/css/header.scss
index 44dc0adc01..f7e162e50a 100644
--- a/packages/noco-docs/src/css/header.scss
+++ b/packages/noco-docs/src/css/header.scss
@@ -13,6 +13,10 @@
}
}
+.navbar__logo {
+ height: 3rem;
+}
+
/* Dark Mode Styles */
html[data-theme='dark'] {
diff --git a/packages/noco-docs/static/img/nocodb-full-color.png b/packages/noco-docs/static/img/nocodb-full-color.png
new file mode 100644
index 0000000000..e0855fcc0a
Binary files /dev/null and b/packages/noco-docs/static/img/nocodb-full-color.png differ
diff --git a/packages/noco-docs/versioned_docs/version-0.109.7/020.getting-started/010.installation.md b/packages/noco-docs/versioned_docs/version-0.109.7/020.getting-started/010.installation.md
index d3942e2f91..45b5e09f54 100644
--- a/packages/noco-docs/versioned_docs/version-0.109.7/020.getting-started/010.installation.md
+++ b/packages/noco-docs/versioned_docs/version-0.109.7/020.getting-started/010.installation.md
@@ -17,15 +17,6 @@ Simple installation - takes about three minutes!
If you are a Docker user, you may try this way!
-
-
-```js
-function helloWorld() {
- console.log('Hello, world!');
-}
-```
-
-
```bash
@@ -91,15 +82,6 @@ If you plan to input some special characters, you may need to change the charact
We provide different docker-compose.yml files under [this directory](https://github.com/nocodb/nocodb/tree/master/docker-compose). Here are some examples.
-
-
-```js
-function helloWorld() {
- console.log('Hello, world!');
-}
-```
-
-
```bash
@@ -328,7 +310,7 @@ If your service fails to start, you may check the logs in ECS console or in Clou
Click to Expand
-
+
#### Pull NocoDB Image on Cloud Shell
Since Cloud Run only supports images from Google Container Registry (GCR) or Artifact Registry, we need to pull NocoDB image, tag it and push it in GCP using Cloud Shell. Here are some sample commands which you can execute in Cloud Shell.
@@ -359,7 +341,7 @@ If your service fails to start, you may check the logs in ECS console or in Clou
Click to Expand
-
+
#### Create Apps
On Home page, Click on Create icon & Select Apps (Deploy your code).
@@ -401,7 +383,7 @@ If your service fails to start, you may check the logs in ECS console or in Clou
Click to Expand
-
+
#### Navigate to App Store
Log into Cloudron and select App Store
@@ -449,7 +431,7 @@ If your service fails to start, you may check the logs in ECS console or in Clou
Click to Expand
-
+
#### Navigate to Templates
Go to [Templates](https://railway.app/templates), Search NocoDB and click Deploy
diff --git a/packages/noco-docs/versioned_docs/version-0.109.7/030.setup-and-usages/220.links.md b/packages/noco-docs/versioned_docs/version-0.109.7/030.setup-and-usages/220.links.md
index 9153357d79..fb33ff4bef 100644
--- a/packages/noco-docs/versioned_docs/version-0.109.7/030.setup-and-usages/220.links.md
+++ b/packages/noco-docs/versioned_docs/version-0.109.7/030.setup-and-usages/220.links.md
@@ -18,7 +18,7 @@ description: "Understanding Link Columns!"
Further details of relationship types can be found [here](https://afteracademy.com/blog/what-are-the-different-types-of-relationships-in-dbms)
-From Release v0.110.0, table records can be connected through relationships using the **Links** column type.
+From Release v0.111.0, table records can be connected through relationships using the **Links** column type.
It is important to note that, earlier supported column type **LinkToAnotherRecord** for creating relations is considered deprecated. While the old datatype is still supported for backward compatibility, it is no longer possible to create new fields of that type.
The main distinction between these two column types lies in how the contents are displayed within the cell when links are established between two tables. With the **LinkToAnotherRecord** column type, the cell displays the **Primary value** of the related records. On the other hand, the **Links** column type only shows the **count** of related records.
diff --git a/packages/nocodb-sdk/package.json b/packages/nocodb-sdk/package.json
index 146c8b4909..31834d1673 100644
--- a/packages/nocodb-sdk/package.json
+++ b/packages/nocodb-sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "nocodb-sdk",
- "version": "0.111.0",
+ "version": "0.111.1",
"description": "NocoDB SDK",
"main": "build/main/index.js",
"typings": "build/main/index.d.ts",
diff --git a/packages/nocodb-sdk/src/lib/Api.ts b/packages/nocodb-sdk/src/lib/Api.ts
index 91d22a9b16..c30020902b 100644
--- a/packages/nocodb-sdk/src/lib/Api.ts
+++ b/packages/nocodb-sdk/src/lib/Api.ts
@@ -5906,13 +5906,7 @@ export class Api<
* @summary Bulk create-update-delete columns
* @request POST:/api/v1/db/meta/tables/{tableId}/columns/bulk
* @response `200` `{
- failedOps?: ({
- op: "add" | "update" | "delete",
- \** Model for Column *\
- column: ColumnType,
- error?: any,
-
-})[],
+ failedOps?: (any)[],
}` OK
* @response `400` `{
@@ -5932,12 +5926,7 @@ export class Api<
) =>
this.request<
{
- failedOps?: {
- op: 'add' | 'update' | 'delete';
- /** Model for Column */
- column: ColumnType;
- error?: any;
- }[];
+ failedOps?: any[];
},
{
/** @example BadRequest [Error]: */
@@ -9976,126 +9965,6 @@ export class Api<
...params,
}),
};
- cowriterTable = {
- /**
- * No description
- *
- * @tags Cowriter Table
- * @name Create
- * @summary Cowriter Create
- * @request POST:/api/v1/cowriter/meta/tables/{tableId}
- * @response `200` `any` OK
- */
- create: (tableId: string, data: object, params: RequestParams = {}) =>
- this.request({
- path: `/api/v1/cowriter/meta/tables/${tableId}`,
- method: 'POST',
- body: data,
- type: ContentType.Json,
- format: 'json',
- ...params,
- }),
-
- /**
- * No description
- *
- * @tags Cowriter Table
- * @name List
- * @summary Cowriter List
- * @request GET:/api/v1/cowriter/meta/tables/{tableId}
- * @response `200` `any` OK
- */
- list: (tableId: string, params: RequestParams = {}) =>
- this.request({
- path: `/api/v1/cowriter/meta/tables/${tableId}`,
- method: 'GET',
- format: 'json',
- ...params,
- }),
-
- /**
- * No description
- *
- * @tags Cowriter Table
- * @name Get
- * @summary Cowriter Get
- * @request GET:/api/v1/cowriter/meta/tables/{tableId}/{cowriterId}
- * @response `200` `any` OK
- * @response `0` `any`
- */
- get: (tableId: string, cowriterId: string, params: RequestParams = {}) =>
- this.request({
- path: `/api/v1/cowriter/meta/tables/${tableId}/${cowriterId}`,
- method: 'GET',
- format: 'json',
- ...params,
- }),
-
- /**
- * No description
- *
- * @tags Cowriter Table
- * @name Patch
- * @summary Cowriter Patch
- * @request PATCH:/api/v1/cowriter/meta/tables/{tableId}/{cowriterId}
- * @response `200` `void` OK
- */
- patch: (
- tableId: string,
- cowriterId: string,
- data: any,
- params: RequestParams = {}
- ) =>
- this.request({
- path: `/api/v1/cowriter/meta/tables/${tableId}/${cowriterId}`,
- method: 'PATCH',
- body: data,
- type: ContentType.Json,
- ...params,
- }),
-
- /**
- * @description Generate Columns using AI
- *
- * @tags Cowriter Table
- * @name GenerateColumns
- * @summary Cowriter Generate Columns
- * @request POST:/api/v1/cowriter/meta/tables/{tableId}/generate-columns
- * @response `200` `void` OK
- */
- generateColumns: (
- tableId: string,
- data: {
- title?: string;
- },
- params: RequestParams = {}
- ) =>
- this.request({
- path: `/api/v1/cowriter/meta/tables/${tableId}/generate-columns`,
- method: 'POST',
- body: data,
- type: ContentType.Json,
- ...params,
- }),
-
- /**
- * No description
- *
- * @tags Cowriter Table
- * @name CreateBulk
- * @summary Cowriter Create Bulk
- * @request POST:/api/v1/cowriter/meta/tables/{tableId}/bulk
- * @response `200` `void` OK
- */
- createBulk: (tableId: string, data: any[], params: RequestParams = {}) =>
- this.request({
- path: `/api/v1/cowriter/meta/tables/${tableId}/bulk`,
- method: 'POST',
- body: data,
- type: ContentType.Json,
- ...params,
- }),
- };
plugin = {
/**
* @description List all plugins
diff --git a/packages/nocodb/package.json b/packages/nocodb/package.json
index 24125f9ec9..7f6419348a 100644
--- a/packages/nocodb/package.json
+++ b/packages/nocodb/package.json
@@ -1,6 +1,6 @@
{
"name": "nocodb",
- "version": "0.111.0",
+ "version": "0.111.1",
"description": "NocoDB Backend",
"main": "dist/bundle.js",
"author": {
@@ -129,7 +129,7 @@
"mysql2": "^3.2.0",
"nanoid": "^3.1.20",
"nc-help": "^0.2.88",
- "nc-lib-gui": "0.111.0",
+ "nc-lib-gui": "0.111.1",
"nc-plugin": "^0.1.3",
"ncp": "^2.0.0",
"nestjs-kafka": "^1.0.6",
@@ -226,4 +226,4 @@
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
-}
+}
\ No newline at end of file
diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts
index b8c5350c7d..47c4b77a0c 100644
--- a/packages/nocodb/src/db/BaseModelSqlv2.ts
+++ b/packages/nocodb/src/db/BaseModelSqlv2.ts
@@ -2176,7 +2176,7 @@ class BaseModelSqlv2 {
return res;
}
- async updateByPk(id, data, trx?, cookie?, disableOptimization = false) {
+ async updateByPk(id, data, trx?, cookie?, _disableOptimization = false) {
try {
const updateObj = await this.model.mapAliasToColumn(
data,
@@ -2679,7 +2679,15 @@ class BaseModelSqlv2 {
// pk not specified - bypass
continue;
}
- if (!raw) prevData.push(await this.readByPk(pkValues));
+ if (!raw)
+ prevData.push(
+ await this.readByPk(
+ pkValues,
+ false,
+ {},
+ { ignoreView: true, getHiddenColumn: true },
+ ),
+ );
const wherePk = await this._wherePk(pkValues);
res.push(wherePk);
toBeUpdated.push({ d, wherePk });
@@ -2696,7 +2704,12 @@ class BaseModelSqlv2 {
if (!raw) {
for (const pkValues of updatePkValues) {
- const oldRecord = await this.readByPk(pkValues);
+ const oldRecord = await this.readByPk(
+ pkValues,
+ false,
+ {},
+ { ignoreView: true, getHiddenColumn: true },
+ );
if (!oldRecord && throwExceptionIfNotExist)
NcError.unprocessableEntity(
`Record with pk ${JSON.stringify(pkValues)} not found`,
@@ -2800,7 +2813,12 @@ class BaseModelSqlv2 {
continue;
}
- const oldRecord = await this.readByPk(pkValues);
+ const oldRecord = await this.readByPk(
+ pkValues,
+ false,
+ {},
+ { ignoreView: true, getHiddenColumn: true },
+ );
if (!oldRecord && throwExceptionIfNotExist)
NcError.unprocessableEntity(
`Record with pk ${JSON.stringify(pkValues)} not found`,
@@ -3650,9 +3668,10 @@ class BaseModelSqlv2 {
const proto = await this.getProto();
const data = await groupedQb;
+
const result = data?.map((d) => {
d.__proto__ = proto;
- return d;
+ return this.convertDateFormat(d);
});
const groupedResult = result.reduce>(
@@ -3660,9 +3679,7 @@ class BaseModelSqlv2 {
if (!aggObj.has(row[column.title])) {
aggObj.set(row[column.title], []);
}
-
aggObj.get(row[column.title]).push(row);
-
return aggObj;
},
new Map(),
@@ -3915,10 +3932,7 @@ class BaseModelSqlv2 {
}
}
- if (
- this.isPg &&
- (col.dt === 'timestamp with time zone' || col.dt === 'timestamptz')
- ) {
+ if (this.isPg) {
// postgres - timezone already attached to input
// e.g. 2023-05-11 16:16:51+08:00
keepLocalTime = false;
diff --git a/packages/nocodb/src/modules/jobs/jobs/at-import/at-import.processor.ts b/packages/nocodb/src/modules/jobs/jobs/at-import/at-import.processor.ts
index e31384f18d..c6c2c2b4e2 100644
--- a/packages/nocodb/src/modules/jobs/jobs/at-import/at-import.processor.ts
+++ b/packages/nocodb/src/modules/jobs/jobs/at-import/at-import.processor.ts
@@ -231,7 +231,7 @@ export class AtImportProcessor {
const template = await FetchAT.readTemplate(sDB.shareId);
await FetchAT.initialize(template.template.exploreApplication.shareId);
} else {
- await FetchAT.initialize(sDB.shareId);
+ await FetchAT.initialize(sDB.shareId, sDB.appId);
}
const ft = await FetchAT.read();
const duration = Date.now() - start;
@@ -2450,6 +2450,7 @@ export interface AirtableSyncConfig {
projectId?: string;
baseId?: string;
apiKey: string;
+ appId?: string;
shareId: string;
user: UserType;
options: {
diff --git a/packages/nocodb/src/modules/jobs/jobs/at-import/helpers/fetchAT.ts b/packages/nocodb/src/modules/jobs/jobs/at-import/helpers/fetchAT.ts
index 1a9a8151bb..cbb7d0c650 100644
--- a/packages/nocodb/src/modules/jobs/jobs/at-import/helpers/fetchAT.ts
+++ b/packages/nocodb/src/modules/jobs/jobs/at-import/helpers/fetchAT.ts
@@ -4,9 +4,14 @@ const info: any = {
initialized: false,
};
-async function initialize(shareId) {
+async function initialize(shareId, appId?: string) {
info.cookie = '';
- const url = `https://airtable.com/${shareId}`;
+
+ if (!appId || appId === '') {
+ appId = null;
+ }
+
+ const url = `https://airtable.com/${appId ? `${appId}/` : ''}${shareId}`;
try {
const hreq = await axios
diff --git a/packages/nocodb/src/schema/swagger.json b/packages/nocodb/src/schema/swagger.json
index 4e4fafa13d..a7b7a5050d 100644
--- a/packages/nocodb/src/schema/swagger.json
+++ b/packages/nocodb/src/schema/swagger.json
@@ -13570,210 +13570,6 @@
]
}
},
- "/api/v1/cowriter/meta/tables/{tableId}": {
- "parameters": [
- {
- "schema": {
- "type": "string"
- },
- "name": "tableId",
- "in": "path",
- "required": true
- }
- ],
- "post": {
- "summary": "Cowriter Create",
- "operationId": "cowriter-table-create",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Cowriter"
- }
- }
- }
- }
- },
- "tags": [
- "Cowriter Table"
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object"
- }
- }
- }
- }
- },
- "get": {
- "summary": "Cowriter List",
- "operationId": "cowriter-table-list",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CowriterList"
- }
- }
- }
- }
- },
- "tags": [
- "Cowriter Table"
- ]
- }
- },
- "/api/v1/cowriter/meta/tables/{tableId}/{cowriterId}": {
- "parameters": [
- {
- "schema": {
- "type": "string"
- },
- "name": "tableId",
- "in": "path",
- "required": true
- },
- {
- "schema": {
- "type": "string"
- },
- "name": "cowriterId",
- "in": "path",
- "required": true
- }
- ],
- "get": {
- "summary": "Cowriter Get",
- "operationId": "cowriter-table-get",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CowriterList"
- }
- }
- }
- },
- "": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Cowriter"
- }
- }
- }
- }
- },
- "tags": [
- "Cowriter Table"
- ]
- },
- "patch": {
- "summary": "Cowriter Patch",
- "operationId": "cowriter-table-patch",
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "tags": [
- "Cowriter Table"
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/Cowriter"
- }
- }
- }
- }
- }
- },
- "/api/v1/cowriter/meta/tables/{tableId}/generate-columns": {
- "parameters": [
- {
- "schema": {
- "type": "string"
- },
- "name": "tableId",
- "in": "path",
- "required": true
- }
- ],
- "post": {
- "summary": "Cowriter Generate Columns",
- "operationId": "cowriter-table-generate-columns",
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "description": "Generate Columns using AI",
- "tags": [
- "Cowriter Table"
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string"
- }
- }
- }
- }
- }
- }
- }
- },
- "/api/v1/cowriter/meta/tables/{tableId}/bulk": {
- "parameters": [
- {
- "schema": {
- "type": "string"
- },
- "name": "tableId",
- "in": "path",
- "required": true
- }
- ],
- "post": {
- "summary": "Cowriter Create Bulk",
- "operationId": "cowriter-table-create-bulk",
- "responses": {
- "200": {
- "description": "OK"
- }
- },
- "description": "",
- "tags": [
- "Cowriter Table"
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Cowriter"
- }
- }
- }
- }
- }
- }
- },
"/api/v1/db/meta/plugins": {
"parameters": [
{
@@ -15444,22 +15240,24 @@
"failedOps": {
"type": "array",
"items": {
- "type": "object",
- "properties": {
- "op": {
- "type": "string",
- "enum": [
- "add",
- "update",
- "delete"
- ],
- "required": true
- },
- "column": {
- "$ref": "#/components/schemas/Column",
- "required": true
- },
- "error": {}
+ "schema": {
+ "type": "object",
+ "properties": {
+ "op": {
+ "type": "string",
+ "enum": [
+ "add",
+ "update",
+ "delete"
+ ],
+ "required": true
+ },
+ "column": {
+ "$ref": "#/components/schemas/Column",
+ "required": true
+ },
+ "error": {}
+ }
}
}
}
@@ -22835,9 +22633,6 @@
{
"$ref": "#/components/schemas/ProjectEvent"
},
- {
- "$ref": "#/components/schemas/WorkspaceInviteEvent"
- },
{
"$ref": "#/components/schemas/TableEvent"
},
diff --git a/packages/nocodb/src/services/project-users/project-users.service.ts b/packages/nocodb/src/services/project-users/project-users.service.ts
index bf3853dc2f..b7a39efa50 100644
--- a/packages/nocodb/src/services/project-users/project-users.service.ts
+++ b/packages/nocodb/src/services/project-users/project-users.service.ts
@@ -42,16 +42,18 @@ export class ProjectUsersService {
projectUser: ProjectUserReqType;
req: any;
}): Promise {
- validatePayload(
- 'swagger.json#/components/schemas/ProjectUserReq',
- param.projectUser,
- );
-
const emails = (param.projectUser.email || '')
.toLowerCase()
.split(/\s*,\s*/)
.map((v) => v.trim());
+ emails.forEach((email) => {
+ validatePayload('swagger.json#/components/schemas/ProjectUserReq', {
+ ...param.projectUser,
+ email,
+ });
+ });
+
// check for invalid emails
const invalidEmails = emails.filter((v) => !validator.isEmail(v));
if (!emails.length) {
diff --git a/packages/nocodb/src/services/telemetry.service.ts b/packages/nocodb/src/services/telemetry.service.ts
index 29887b400d..48096b1b7e 100644
--- a/packages/nocodb/src/services/telemetry.service.ts
+++ b/packages/nocodb/src/services/telemetry.service.ts
@@ -19,7 +19,9 @@ export class TelemetryService {
evt_type: string;
[key: string]: any;
}) {
- if (event === '$pageview') T.page({ ...payload, event_name: event });
- else T.event({ ...payload, event_name: event });
+ if (event === '$pageview') T.page({ ...payload, event });
+ else {
+ T.event({ ...payload, event });
+ }
}
}
diff --git a/packages/nocodb/src/utils/projectAcl.ts b/packages/nocodb/src/utils/projectAcl.ts
index c0f89dce8a..1642d8f9c7 100644
--- a/packages/nocodb/src/utils/projectAcl.ts
+++ b/packages/nocodb/src/utils/projectAcl.ts
@@ -135,6 +135,7 @@ const rolePermissions = {
filterDelete: true,
filterGet: true,
filterChildrenRead: true,
+ filterChildrenList: true,
mmList: true,
hmList: true,
diff --git a/tests/playwright/pages/Account/AppStore.ts b/tests/playwright/pages/Account/AppStore.ts
index 0ac3ff39d2..8cd56ea7a7 100644
--- a/tests/playwright/pages/Account/AppStore.ts
+++ b/tests/playwright/pages/Account/AppStore.ts
@@ -1,4 +1,3 @@
-import { expect } from '@playwright/test';
import BasePage from '../Base';
import { AccountPage } from './index';
@@ -23,7 +22,7 @@ export class AccountAppStorePage extends BasePage {
}
async install({ name }: { name: string }) {
- const card = await this.accountPage.get().locator(`.nc-app-store-card-${name}`);
+ const card = this.accountPage.get().locator(`.nc-app-store-card-${name}`);
await card.click();
// todo: Hack to solve the issue when if the test installing a plugin fails, the next test will fail because the plugin is already installed
diff --git a/tests/playwright/pages/Dashboard/BarcodeOverlay/index.ts b/tests/playwright/pages/Dashboard/BarcodeOverlay/index.ts
index ce70f07bcc..8cfb514240 100644
--- a/tests/playwright/pages/Dashboard/BarcodeOverlay/index.ts
+++ b/tests/playwright/pages/Dashboard/BarcodeOverlay/index.ts
@@ -16,7 +16,7 @@ export class BarcodeOverlay extends BasePage {
async verifyBarcodeSvgValue(expectedValue: string) {
const foundBarcodeSvg = await this.get().getByTestId('barcode').innerHTML();
- await expect(foundBarcodeSvg).toContain(expectedValue);
+ expect(foundBarcodeSvg).toContain(expectedValue);
}
async clickCloseButton() {
diff --git a/tests/playwright/pages/Dashboard/BulkUpdate/index.ts b/tests/playwright/pages/Dashboard/BulkUpdate/index.ts
index 382b85d1ec..d04447ddad 100644
--- a/tests/playwright/pages/Dashboard/BulkUpdate/index.ts
+++ b/tests/playwright/pages/Dashboard/BulkUpdate/index.ts
@@ -1,7 +1,6 @@
import { expect, Locator } from '@playwright/test';
import BasePage from '../../Base';
import { DashboardPage } from '..';
-import { DateTimeCellPageObject } from '../common/Cell/DateTimeCell';
import { getTextExcludeIconText } from '../../../tests/utils/general';
export class BulkUpdatePage extends BasePage {
@@ -29,17 +28,17 @@ export class BulkUpdatePage extends BasePage {
}
async getInactiveColumn(index: number) {
- const inactiveColumns = await this.columnsDrawer.locator('.ant-card');
+ const inactiveColumns = this.columnsDrawer.locator('.ant-card');
return inactiveColumns.nth(index);
}
async getActiveColumn(index: number) {
- const activeColumns = await this.form.locator('[data-testid="nc-bulk-update-fields"]');
+ const activeColumns = this.form.locator('[data-testid="nc-bulk-update-fields"]');
return activeColumns.nth(index);
}
async getInactiveColumns() {
- const inactiveColumns = await this.columnsDrawer.locator('.ant-card');
+ const inactiveColumns = this.columnsDrawer.locator('.ant-card');
const inactiveColumnsCount = await inactiveColumns.count();
const inactiveColumnsTitles = [];
// get title for each inactive column
@@ -52,7 +51,7 @@ export class BulkUpdatePage extends BasePage {
}
async getActiveColumns() {
- const activeColumns = await this.form.locator('[data-testid="nc-bulk-update-fields"]');
+ const activeColumns = this.form.locator('[data-testid="nc-bulk-update-fields"]');
const activeColumnsCount = await activeColumns.count();
const activeColumnsTitles = [];
// get title for each active column
@@ -67,7 +66,7 @@ export class BulkUpdatePage extends BasePage {
}
async removeField(index: number) {
- const removeFieldButton = await this.form.locator('[data-testid="nc-bulk-update-fields"]');
+ const removeFieldButton = this.form.locator('[data-testid="nc-bulk-update-fields"]');
const removeFieldButtonCount = await removeFieldButton.count();
await removeFieldButton.nth(index).locator('[data-testid="nc-bulk-update-fields-remove-icon"]').click();
const newRemoveFieldButtonCount = await removeFieldButton.count();
@@ -75,7 +74,7 @@ export class BulkUpdatePage extends BasePage {
}
async addField(index: number) {
- const addFieldButton = await this.columnsDrawer.locator('.ant-card');
+ const addFieldButton = this.columnsDrawer.locator('.ant-card');
const addFieldButtonCount = await addFieldButton.count();
await addFieldButton.nth(index).click();
const newAddFieldButtonCount = await addFieldButton.count();
@@ -119,16 +118,8 @@ export class BulkUpdatePage extends BasePage {
const time = value.split(':');
// eslint-disable-next-line no-case-declarations
const timePanel = picker.locator('.ant-picker-time-panel-column');
- await timePanel
- .nth(0)
- .locator('li')
- .nth(+time[0])
- .click();
- await timePanel
- .nth(1)
- .locator('li')
- .nth(+time[1])
- .click();
+ await timePanel.nth(0).locator('li').nth(+time[0]).click();
+ await timePanel.nth(1).locator('li').nth(+time[1]).click();
await picker.locator('.ant-picker-ok').click();
break;
case 'singleSelect':
@@ -151,7 +142,7 @@ export class BulkUpdatePage extends BasePage {
case 'attachment':
// eslint-disable-next-line no-case-declarations
const attachFileAction = field.locator('[data-testid="attachment-cell-file-picker-button"]').click();
- await this.attachFile({ filePickUIAction: attachFileAction, filePath: value });
+ await this.attachFile({ filePickUIAction: attachFileAction, filePath: [value] });
break;
case 'date':
{
@@ -182,7 +173,7 @@ export class BulkUpdatePage extends BasePage {
awaitResponse?: boolean;
} = {}) {
await this.bulkUpdateButton.click();
- const confirmModal = await this.rootPage.locator('.ant-modal-confirm');
+ const confirmModal = this.rootPage.locator('.ant-modal-confirm');
const saveRowAction = () => confirmModal.locator('.ant-btn-primary').click();
if (!awaitResponse) {
diff --git a/tests/playwright/pages/Dashboard/Details/ErdPage.ts b/tests/playwright/pages/Dashboard/Details/ErdPage.ts
new file mode 100644
index 0000000000..e327b9cfdc
--- /dev/null
+++ b/tests/playwright/pages/Dashboard/Details/ErdPage.ts
@@ -0,0 +1,107 @@
+import BasePage from '../../Base';
+import { expect, Locator } from '@playwright/test';
+import { DetailsPage } from './index';
+
+export class ErdPage extends BasePage {
+ readonly detailsPage: DetailsPage;
+
+ readonly contextMenuBase: Locator;
+ readonly contextMenu = {};
+
+ readonly btn_fullScreen: Locator;
+ readonly btn_zoomIn: Locator;
+ readonly btn_zoomOut: Locator;
+
+ constructor(details: DetailsPage) {
+ super(details.rootPage);
+ this.detailsPage = details;
+ this.btn_fullScreen = this.get().locator('.nc-erd-histogram > .nc-icon');
+ this.btn_zoomIn = this.get().locator('.nc-erd-zoom-btn').nth(0);
+ this.btn_zoomOut = this.get().locator('.nc-erd-zoom-btn').nth(1);
+
+ this.contextMenuBase = this.get().locator('.nc-erd-context-menu');
+ this.contextMenu['Show Columns'] = this.contextMenuBase.locator('.ant-checkbox-wrapper').nth(0);
+ this.contextMenu['Show Primary and Foreign Keys'] = this.contextMenuBase.locator('.ant-checkbox-wrapper').nth(1);
+ this.contextMenu['Show SQL Views'] = this.contextMenuBase.locator('.ant-checkbox-wrapper').nth(2);
+ }
+
+ get() {
+ // pop up when triggered from data sources page
+ return this.rootPage.locator('.vue-flow');
+ }
+
+ async verifyNode({
+ tableName,
+ columnName,
+ columnNameShouldNotExist,
+ }: {
+ tableName: string;
+ columnName?: string;
+ columnNameShouldNotExist?: string;
+ }) {
+ await this.get().locator(`.nc-erd-table-node-${tableName}`).waitFor({ state: 'visible' });
+ if (columnName) {
+ await this.get().locator(`.nc-erd-table-node-${tableName}-column-${columnName}`).waitFor({ state: 'visible' });
+ }
+ if (columnNameShouldNotExist) {
+ await this.get()
+ .locator(`.nc-erd-table-node-${tableName}-column-${columnNameShouldNotExist}`)
+ .waitFor({ state: 'hidden' });
+ }
+ }
+
+ async verifyNodeDoesNotExist({ tableName }: { tableName: string }) {
+ await this.get().locator(`.nc-erd-table-node-${tableName}`).waitFor({ state: 'hidden' });
+ }
+
+ async verifyColumns({ tableName, columns }: { tableName: string; columns: string[] }) {
+ for (const column of columns) {
+ await this.verifyNode({ tableName, columnName: column });
+ }
+ }
+
+ async verifyNodesCount(count: number) {
+ await expect(this.get().locator('.nc-erd-table-node')).toHaveCount(count);
+ }
+
+ async verifyEdgesCount({
+ count,
+ circleCount,
+ rectangleCount,
+ }: {
+ count: number;
+ circleCount: number;
+ rectangleCount: number;
+ }) {
+ await expect(this.get().locator('.vue-flow__edge')).toHaveCount(count);
+ await expect(this.get().locator('.nc-erd-edge-circle')).toHaveCount(circleCount);
+ await expect(this.get().locator('.nc-erd-edge-rect')).toHaveCount(rectangleCount);
+ }
+
+ async verifyJunctionTableLabel({ tableTitle, tableName }: { tableName: string; tableTitle: string }) {
+ await this.get().locator(`.nc-erd-table-label-${tableTitle}-${tableName}`).waitFor({
+ state: 'visible',
+ });
+ }
+
+ async clickShowColumnNames() {
+ await this.contextMenu['Show Columns'].click();
+ await (await this.get().elementHandle())?.waitForElementState('stable');
+ }
+
+ async clickShowPkAndFk() {
+ await this.contextMenu['Show Primary and Foreign Keys'].click();
+ await (await this.get().elementHandle())?.waitForElementState('stable');
+ }
+
+ async clickShowSqlViews() {
+ await this.contextMenu['Show SQL Views'].click();
+ await (await this.get().elementHandle())?.waitForElementState('stable');
+ }
+
+ async close() {
+ await this.get().click();
+ await this.rootPage.keyboard.press('Escape');
+ await this.get().waitFor({ state: 'hidden' });
+ }
+}
diff --git a/tests/playwright/pages/Dashboard/Details/index.ts b/tests/playwright/pages/Dashboard/Details/index.ts
index 15a7fe4ccc..205442f483 100644
--- a/tests/playwright/pages/Dashboard/Details/index.ts
+++ b/tests/playwright/pages/Dashboard/Details/index.ts
@@ -3,11 +3,13 @@ import BasePage from '../../Base';
import { TopbarPage } from '../common/Topbar';
import { Locator } from '@playwright/test';
import { WebhookPage } from './WebhookPage';
+import { ErdPage } from './ErdPage';
export class DetailsPage extends BasePage {
readonly dashboard: DashboardPage;
readonly topbar: TopbarPage;
readonly webhook: WebhookPage;
+ readonly relations: ErdPage;
readonly tab_webhooks: Locator;
readonly tab_apiSnippet: Locator;
@@ -21,6 +23,7 @@ export class DetailsPage extends BasePage {
this.dashboard = dashboard;
this.topbar = dashboard.grid.topbar;
this.webhook = new WebhookPage(this);
+ this.relations = new ErdPage(this);
this.tab_webhooks = this.get().locator(`[data-testid="nc-webhooks-tab"]`);
this.tab_apiSnippet = this.get().locator(`[data-testid="nc-apis-tab"]`);
diff --git a/tests/playwright/pages/Dashboard/ExpandedForm/index.ts b/tests/playwright/pages/Dashboard/ExpandedForm/index.ts
index 12eb6874bb..50d93d846b 100644
--- a/tests/playwright/pages/Dashboard/ExpandedForm/index.ts
+++ b/tests/playwright/pages/Dashboard/ExpandedForm/index.ts
@@ -38,7 +38,7 @@ export class ExpandedFormPage extends BasePage {
// add delay; wait for the menu to appear
await this.rootPage.waitForTimeout(500);
- const popUpMenu = await this.rootPage.locator('.ant-dropdown');
+ const popUpMenu = this.rootPage.locator('.ant-dropdown');
await popUpMenu.locator(`.ant-dropdown-menu-item:has-text("${menuItem}")`).click();
}
@@ -55,12 +55,12 @@ export class ExpandedFormPage extends BasePage {
}
async isDisabledDuplicateRow() {
- const isDisabled = await this.duplicateRowButton;
+ const isDisabled = this.duplicateRowButton;
return await isDisabled.count();
}
async isDisabledDeleteRow() {
- const isDisabled = await this.deleteRowButton;
+ const isDisabled = this.deleteRowButton;
return await isDisabled.count();
}
@@ -71,7 +71,7 @@ export class ExpandedFormPage extends BasePage {
}
async gotoUsingUrlAndRowId({ rowId }: { rowId: string }) {
- const url = await this.dashboard.rootPage.url();
+ const url = this.dashboard.rootPage.url();
const expandedFormUrl = '/' + url.split('/').slice(3).join('/').split('?')[0] + `?rowId=${rowId}`;
await this.rootPage.goto(expandedFormUrl);
await this.dashboard.waitForLoaderToDisappear();
@@ -157,7 +157,7 @@ export class ExpandedFormPage extends BasePage {
}
async openChildCard(param: { column: string; title: string }) {
- const childList = await this.get().locator(`[data-testid="nc-expand-col-${param.column}"]`);
+ const childList = this.get().locator(`[data-testid="nc-expand-col-${param.column}"]`);
await childList.locator(`.ant-card:has-text("${param.title}")`).click();
}
@@ -172,9 +172,9 @@ export class ExpandedFormPage extends BasePage {
expect(await this.btn_moreActions.count()).toBe(1);
await this.btn_moreActions.click();
- const menu = await this.rootPage.locator('.ant-dropdown:visible');
+ const menu = this.rootPage.locator('.ant-dropdown:visible');
await menu.waitFor({ state: 'visible' });
- const menuItems = await menu.locator('.ant-dropdown-menu-item');
+ const menuItems = menu.locator('.ant-dropdown-menu-item');
for (let i = 0; i < (await menuItems.count()); i++) {
if (role === 'owner' || role === 'editor' || role === 'creator') {
const menuText = ['Reload', 'Duplicate row', 'Delete row', 'Close'];
diff --git a/tests/playwright/pages/Dashboard/Form/index.ts b/tests/playwright/pages/Dashboard/Form/index.ts
index aa7fdbad94..f775a4006f 100644
--- a/tests/playwright/pages/Dashboard/Form/index.ts
+++ b/tests/playwright/pages/Dashboard/Form/index.ts
@@ -79,62 +79,60 @@ export class FormPage extends BasePage {
}
async verifyFormFieldLabel({ index, label }: { index: number; label: string }) {
- await expect(await this.getFormFields().nth(index).locator('[data-testid="nc-form-input-label"]')).toContainText(
- label
- );
+ await expect(this.getFormFields().nth(index).locator('[data-testid="nc-form-input-label"]')).toContainText(label);
}
async verifyFormFieldHelpText({ index, helpText }: { index: number; helpText: string }) {
await expect(
- await this.getFormFields().nth(index).locator('[data-testid="nc-form-input-help-text-label"]')
+ this.getFormFields().nth(index).locator('[data-testid="nc-form-input-help-text-label"]')
).toContainText(helpText);
}
async verifyFieldsIsEditable({ index }: { index: number }) {
- await expect(await this.getFormFields().nth(index)).toHaveClass(/nc-editable/);
+ await expect(this.getFormFields().nth(index)).toHaveClass(/nc-editable/);
}
async verifyAfterSubmitMsg({ msg }: { msg: string }) {
- await expect((await this.afterSubmitMsg.inputValue()).includes(msg)).toBeTruthy();
+ expect((await this.afterSubmitMsg.inputValue()).includes(msg)).toBeTruthy();
}
async verifyFormViewFieldsOrder({ fields }: { fields: string[] }) {
- const fieldLabels = await this.get().locator('[data-testid="nc-form-input-label"]');
- await expect(await fieldLabels).toHaveCount(fields.length);
+ const fieldLabels = this.get().locator('[data-testid="nc-form-input-label"]');
+ await expect(fieldLabels).toHaveCount(fields.length);
for (let i = 0; i < fields.length; i++) {
- await expect(await fieldLabels.nth(i)).toContainText(fields[i]);
+ await expect(fieldLabels.nth(i)).toContainText(fields[i]);
}
}
async reorderFields({ sourceField, destinationField }: { sourceField: string; destinationField: string }) {
- await expect(await this.get().locator(`.nc-form-drag-${sourceField}`)).toBeVisible();
- await expect(await this.get().locator(`.nc-form-drag-${destinationField}`)).toBeVisible();
- const src = await this.get().locator(`.nc-form-drag-${sourceField.replace(' ', '')}`);
- const dst = await this.get().locator(`.nc-form-drag-${destinationField.replace(' ', '')}`);
+ await expect(this.get().locator(`.nc-form-drag-${sourceField}`)).toBeVisible();
+ await expect(this.get().locator(`.nc-form-drag-${destinationField}`)).toBeVisible();
+ const src = this.get().locator(`.nc-form-drag-${sourceField.replace(' ', '')}`);
+ const dst = this.get().locator(`.nc-form-drag-${destinationField.replace(' ', '')}`);
await src.dragTo(dst);
}
async removeField({ field, mode }: { mode: string; field: string }) {
if (mode === 'dragDrop') {
- const src = await this.get().locator(`.nc-form-drag-${field.replace(' ', '')}`);
- const dst = await this.get().locator(`[data-testid="nc-drag-n-drop-to-hide"]`);
+ const src = this.get().locator(`.nc-form-drag-${field.replace(' ', '')}`);
+ const dst = this.get().locator(`[data-testid="nc-drag-n-drop-to-hide"]`);
await src.dragTo(dst);
} else if (mode === 'hideField') {
- const src = await this.get().locator(`.nc-form-drag-${field.replace(' ', '')}`);
+ const src = this.get().locator(`.nc-form-drag-${field.replace(' ', '')}`);
await src.locator(`[data-testid="nc-field-remove-icon"]`).click();
}
}
async addField({ field, mode }: { mode: string; field: string }) {
if (mode === 'dragDrop') {
- const src = await this.get().locator(`[data-testid="nc-form-hidden-column-${field}"] > div.ant-card-body`);
- const dst = await this.get().locator(`[data-testid="nc-form-input-Country"]`);
+ const src = this.get().locator(`[data-testid="nc-form-hidden-column-${field}"] > div.ant-card-body`);
+ const dst = this.get().locator(`[data-testid="nc-form-input-Country"]`);
await src.waitFor({ state: 'visible' });
await dst.waitFor({ state: 'visible' });
await src.dragTo(dst, { trial: true });
await src.dragTo(dst);
} else if (mode === 'clickField') {
- const src = await this.get().locator(`[data-testid="nc-form-hidden-column-${field}"]`);
+ const src = this.get().locator(`[data-testid="nc-form-hidden-column-${field}"]`);
await src.click();
}
}
@@ -206,12 +204,12 @@ export class FormPage extends BasePage {
if (required) expectText = label + ' *';
else expectText = label;
- const fieldLabel = await this.get()
+ const fieldLabel = this.get()
.locator(`.nc-form-drag-${field.replace(' ', '')}`)
.locator('div[data-testid="nc-form-input-label"]');
await expect(fieldLabel).toHaveText(expectText);
- const fieldHelpText = await this.get()
+ const fieldHelpText = this.get()
.locator(`.nc-form-drag-${field.replace(' ', '')}`)
.locator('div[data-testid="nc-form-input-help-text-label"]');
await expect(fieldHelpText).toHaveText(helpText);
@@ -232,10 +230,10 @@ export class FormPage extends BasePage {
await this.rootPage.waitForTimeout(100 * retryCounter);
retryCounter++;
}
- await expect(await this.getFormAfterSubmit()).toContainText(param.message);
+ await expect(this.getFormAfterSubmit()).toContainText(param.message);
}
if (true === param.submitAnotherForm) {
- await expect(await this.getFormAfterSubmit().locator('button:has-text("Submit Another Form")')).toBeVisible();
+ await expect(this.getFormAfterSubmit().locator('button:has-text("Submit Another Form")')).toBeVisible();
}
if (true === param.showBlankForm) {
await this.get().waitFor();
diff --git a/tests/playwright/pages/Dashboard/Grid/Column/Attachment.ts b/tests/playwright/pages/Dashboard/Grid/Column/Attachment.ts
index 14a1479ddb..efeda6a67f 100644
--- a/tests/playwright/pages/Dashboard/Grid/Column/Attachment.ts
+++ b/tests/playwright/pages/Dashboard/Grid/Column/Attachment.ts
@@ -34,22 +34,22 @@ export class AttachmentColumnPageObject extends BasePage {
// Checkbox order: Application, Audio, Image, Video, Misc
if (fileCount) {
- const inputMaxCount = await this.column.get().locator(`.nc-attachment-max-count`);
+ const inputMaxCount = this.column.get().locator(`.nc-attachment-max-count`);
await inputMaxCount.locator(`input`).fill(fileCount.toString());
}
if (fileSize) {
- const inputMaxSize = await this.column.get().locator(`.nc-attachment-max-size`);
+ const inputMaxSize = this.column.get().locator(`.nc-attachment-max-size`);
await inputMaxSize.locator(`input`).fill(fileSize.toString());
}
if (fileTypesExcludeList) {
// click on nc-allow-all-mime-type-checkbox
- const allowAllMimeCheckbox = await this.column.get().locator(`.nc-allow-all-mime-type-checkbox`);
+ const allowAllMimeCheckbox = this.column.get().locator(`.nc-allow-all-mime-type-checkbox`);
await allowAllMimeCheckbox.click();
- const treeList = await this.column.get().locator(`.ant-tree-list`);
- const checkboxList = await treeList.locator(`.ant-tree-treenode`);
+ const treeList = this.column.get().locator(`.ant-tree-list`);
+ const checkboxList = treeList.locator(`.ant-tree-treenode`);
for (let i = 0; i < fileTypesExcludeList.length; i++) {
const fileType = fileTypesExcludeList[i];
diff --git a/tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts b/tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts
index ebc55120bb..c83defa3fe 100644
--- a/tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts
+++ b/tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts
@@ -20,8 +20,8 @@ export class ChildList extends BasePage {
// button: Link to 'City'
// icon: reload
await expect(this.get().locator(`.ant-modal-title`)).toHaveText(`Child list`);
- await expect(await this.get().locator(`text=/Link to '.*${linkField}'/i`).isVisible()).toBeTruthy();
- await expect(await this.get().locator(`[data-testid="nc-child-list-reload"]`).isVisible()).toBeTruthy();
+ expect(await this.get().locator(`text=/Link to '.*${linkField}'/i`).isVisible()).toBeTruthy();
+ expect(await this.get().locator(`[data-testid="nc-child-list-reload"]`).isVisible()).toBeTruthy();
// child list body validation (card count, card title)
const cardCount = cardTitle.length;
@@ -29,20 +29,16 @@ export class ChildList extends BasePage {
{
const childList = this.get().locator(`.ant-card`);
const childCards = await childList.count();
- await expect(childCards).toEqual(cardCount);
+ expect(childCards).toEqual(cardCount);
for (let i = 0; i < cardCount; i++) {
await childList.nth(i).locator('.name').waitFor({ state: 'visible' });
await childList.nth(i).locator('.name').scrollIntoViewIfNeeded();
await this.rootPage.waitForTimeout(100);
- await expect(await childList.nth(i).locator('.name').textContent()).toContain(cardTitle[i]);
+ expect(await childList.nth(i).locator('.name').textContent()).toContain(cardTitle[i]);
// icon: unlink
// icon: delete
- await expect(
- await childList.nth(i).locator(`[data-testid="nc-child-list-icon-unlink"]`).isVisible()
- ).toBeTruthy();
- await expect(
- await childList.nth(i).locator(`[data-testid="nc-child-list-icon-delete"]`).isVisible()
- ).toBeTruthy();
+ expect(await childList.nth(i).locator(`[data-testid="nc-child-list-icon-unlink"]`).isVisible()).toBeTruthy();
+ expect(await childList.nth(i).locator(`[data-testid="nc-child-list-icon-delete"]`).isVisible()).toBeTruthy();
}
}
}
diff --git a/tests/playwright/pages/Dashboard/Grid/Column/LTAR/LinkRecord.ts b/tests/playwright/pages/Dashboard/Grid/Column/LTAR/LinkRecord.ts
index 156c7e71ca..f2064474b2 100644
--- a/tests/playwright/pages/Dashboard/Grid/Column/LTAR/LinkRecord.ts
+++ b/tests/playwright/pages/Dashboard/Grid/Column/LTAR/LinkRecord.ts
@@ -12,26 +12,26 @@ export class LinkRecord extends BasePage {
async verify(cardTitle?: string[]) {
await this.dashboard.get().locator('.nc-modal-link-record').waitFor();
- const linkRecord = await this.get();
+ const linkRecord = this.get();
// DOM element validation
// title: Link Record
// button: Add new record
// icon: reload
await expect(this.get().locator(`.ant-modal-title`)).toHaveText(`Link record`);
- await expect(await linkRecord.locator(`button:has-text("Add new record")`).isVisible()).toBeTruthy();
- await expect(await linkRecord.locator(`.nc-reload`).isVisible()).toBeTruthy();
+ expect(await linkRecord.locator(`button:has-text("Add new record")`).isVisible()).toBeTruthy();
+ expect(await linkRecord.locator(`.nc-reload`).isVisible()).toBeTruthy();
// placeholder: Filter query
- await expect(await linkRecord.locator(`[placeholder="Filter query"]`).isVisible()).toBeTruthy();
+ expect(await linkRecord.locator(`[placeholder="Filter query"]`).isVisible()).toBeTruthy();
{
const childList = linkRecord.locator(`.ant-card`);
const childCards = await childList.count();
- await expect(childCards).toEqual(cardTitle.length);
+ expect(childCards).toEqual(cardTitle.length);
for (let i = 0; i < cardTitle.length; i++) {
await childList.nth(i).locator('.name').scrollIntoViewIfNeeded();
await childList.nth(i).locator('.name').waitFor({ state: 'visible' });
- await expect(await childList.nth(i).locator('.name').textContent()).toContain(cardTitle[i]);
+ expect(await childList.nth(i).locator('.name').textContent()).toContain(cardTitle[i]);
}
}
}
diff --git a/tests/playwright/pages/Dashboard/Grid/Column/index.ts b/tests/playwright/pages/Dashboard/Grid/Column/index.ts
index 303e6d67c3..458ea39c14 100644
--- a/tests/playwright/pages/Dashboard/Grid/Column/index.ts
+++ b/tests/playwright/pages/Dashboard/Grid/Column/index.ts
@@ -336,7 +336,7 @@ export class ColumnPageObject extends BasePage {
}
await this.waitForResponse({
- uiAction: async() => await this.rootPage.locator('li[role="menuitem"]:has-text("Hide Field"):visible').click(),
+ uiAction: async () => await this.rootPage.locator('li[role="menuitem"]:has-text("Hide Field"):visible').click(),
requestUrlPathToMatch: 'api/v1/db/meta/views',
httpMethodsToMatch: ['PATCH'],
});
@@ -346,7 +346,7 @@ export class ColumnPageObject extends BasePage {
async save({ isUpdated }: { isUpdated?: boolean } = {}) {
await this.waitForResponse({
- uiAction: async() => await this.get().locator('button:has-text("Save")').click(),
+ uiAction: async () => await this.get().locator('button:has-text("Save")').click(),
requestUrlPathToMatch: 'api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'],
responseJsonMatcher: json => json['pageInfo'],
@@ -401,7 +401,7 @@ export class ColumnPageObject extends BasePage {
async sortColumn({ title, direction = 'asc' }: { title: string; direction: 'asc' | 'desc' }) {
await this.grid.get().locator(`th[data-title="${title}"] .nc-ui-dt-dropdown`).click();
- let menuOption;
+ let menuOption: { (): Promise; (): Promise };
if (direction === 'desc') {
menuOption = () => this.rootPage.locator('li[role="menuitem"]:has-text("Sort Descending"):visible').click();
} else {
@@ -444,7 +444,7 @@ export class ColumnPageObject extends BasePage {
async getWidth(param: { title: string }) {
const { title } = param;
- const cell = await this.rootPage.locator(`th[data-title="${title}"]`);
+ const cell = this.rootPage.locator(`th[data-title="${title}"]`);
return await cell.evaluate(el => el.getBoundingClientRect().width);
}
}
diff --git a/tests/playwright/pages/Dashboard/Grid/columnHeader.ts b/tests/playwright/pages/Dashboard/Grid/columnHeader.ts
new file mode 100644
index 0000000000..b12f23cfe8
--- /dev/null
+++ b/tests/playwright/pages/Dashboard/Grid/columnHeader.ts
@@ -0,0 +1,46 @@
+import { GridPage } from '../Grid';
+import BasePage from '../../Base';
+import { expect, Locator } from '@playwright/test';
+
+export class ColumnHeaderPageObject extends BasePage {
+ readonly grid: GridPage;
+
+ readonly btn_addColumn: Locator;
+ readonly btn_selectAll: Locator;
+
+ constructor(grid: GridPage) {
+ super(grid.rootPage);
+ this.grid = grid;
+
+ this.btn_addColumn = this.get().locator(`.nc-grid-add-edit-column`);
+ this.btn_selectAll = this.get().locator(`[data-testid="nc-check-all"]`);
+ }
+
+ get() {
+ return this.rootPage.locator('.nc-grid-header');
+ }
+
+ async getColumnHeader({ title }: { title: string }) {
+ return this.get().locator(`th[data-title="${title}"]`);
+ }
+
+ async getColumnHeaderContextMenu({ title }: { title: string }) {
+ return (await this.getColumnHeader({ title })).locator(`.nc-ui-dt-dropdown`);
+ }
+
+ async verifyLockMode() {
+ // add column button
+ await expect(this.btn_addColumn).toBeVisible({ visible: false });
+
+ // column header context menu
+ expect(await this.get().locator('.nc-ui-dt-dropdown').count()).toBe(0);
+ }
+
+ async verifyCollaborativeMode() {
+ // add column button
+ await expect(this.btn_addColumn).toBeVisible({ visible: true });
+
+ // column header context menu
+ expect(await this.get().locator('.nc-ui-dt-dropdown').count()).toBeGreaterThan(1);
+ }
+}
diff --git a/tests/playwright/pages/Dashboard/Grid/index.ts b/tests/playwright/pages/Dashboard/Grid/index.ts
index d008b3e8f1..771ca1d301 100644
--- a/tests/playwright/pages/Dashboard/Grid/index.ts
+++ b/tests/playwright/pages/Dashboard/Grid/index.ts
@@ -12,6 +12,7 @@ import { BarcodeOverlay } from '../BarcodeOverlay';
import { RowPageObject } from './Row';
import { WorkspaceMenuObject } from '../common/WorkspaceMenu';
import { GroupPageObject } from './Group';
+import { ColumnHeaderPageObject } from './columnHeader';
export class GridPage extends BasePage {
readonly dashboard: DashboardPage;
@@ -19,6 +20,7 @@ export class GridPage extends BasePage {
readonly dashboardPage: DashboardPage;
readonly qrCodeOverlay: QrCodeOverlay;
readonly barcodeOverlay: BarcodeOverlay;
+ readonly columnHeader: ColumnHeaderPageObject;
readonly column: ColumnPageObject;
readonly cell: CellPageObject;
readonly topbar: TopbarPage;
@@ -29,6 +31,8 @@ export class GridPage extends BasePage {
readonly rowPage: RowPageObject;
readonly groupPage: GroupPageObject;
+ readonly btn_addNewRow: Locator;
+
constructor(dashboardPage: DashboardPage) {
super(dashboardPage.rootPage);
this.dashboard = dashboardPage;
@@ -36,6 +40,7 @@ export class GridPage extends BasePage {
this.qrCodeOverlay = new QrCodeOverlay(this);
this.barcodeOverlay = new BarcodeOverlay(this);
this.column = new ColumnPageObject(this);
+ this.columnHeader = new ColumnHeaderPageObject(this);
this.cell = new CellPageObject(this);
this.topbar = new TopbarPage(this);
this.toolbar = new ToolbarPage(this);
@@ -44,6 +49,26 @@ export class GridPage extends BasePage {
this.workspaceMenu = new WorkspaceMenuObject(this);
this.rowPage = new RowPageObject(this);
this.groupPage = new GroupPageObject(this);
+
+ this.btn_addNewRow = this.get().locator('.nc-grid-add-new-cell');
+ }
+
+ async verifyLockMode() {
+ // add new row button
+ expect(await this.btn_addNewRow.count()).toBe(0);
+
+ await this.toolbar.verifyLockMode();
+ await this.footbar.verifyLockMode();
+ await this.columnHeader.verifyLockMode();
+ }
+
+ async verifyCollaborativeMode() {
+ // add new row button
+ expect(await this.btn_addNewRow.count()).toBe(1);
+
+ await this.toolbar.verifyCollaborativeMode();
+ await this.footbar.verifyCollaborativeMode();
+ await this.columnHeader.verifyCollaborativeMode();
}
get() {
@@ -98,7 +123,7 @@ export class GridPage extends BasePage {
// add delay for UI to render (can wait for count to stabilize by reading it multiple times)
await this.rootPage.waitForTimeout(100);
- await expect(await this.get().locator('.nc-grid-row').count()).toBe(rowCount + 1);
+ expect(await this.get().locator('.nc-grid-row').count()).toBe(rowCount + 1);
await this._fillRow({ index, columnHeader, value: rowValue });
@@ -187,13 +212,13 @@ export class GridPage extends BasePage {
async addRowRightClickMenu(index: number, columnHeader = 'Title') {
const rowCount = await this.get().locator('.nc-grid-row').count();
- const cell = await this.get().locator(`td[data-testid="cell-${columnHeader}-${index}"]`).last();
+ const cell = this.get().locator(`td[data-testid="cell-${columnHeader}-${index}"]`).last();
await cell.click();
await cell.click({ button: 'right' });
// Click text=Insert New Row
await this.rootPage.locator('text=Insert New Row').click();
- await expect(await this.get().locator('.nc-grid-row')).toHaveCount(rowCount + 1);
+ await expect(this.get().locator('.nc-grid-row')).toHaveCount(rowCount + 1);
}
async openExpandedRow({ index }: { index: number }) {
@@ -203,7 +228,7 @@ export class GridPage extends BasePage {
}
async selectRow(index: number) {
- const cell: Locator = await this.get().locator(`td[data-testid="cell-Id-${index}"]`);
+ const cell: Locator = this.get().locator(`td[data-testid="cell-Id-${index}"]`);
await cell.hover();
await cell.locator('input[type="checkbox"]').check({ force: true });
}
@@ -333,24 +358,24 @@ export class GridPage extends BasePage {
index: 0,
columnHeader: columnHeader,
});
- await expect(await cell.locator('input')).not.toBeVisible();
+ await expect(cell.locator('input')).not.toBeVisible();
// right click menu
await this.get().locator(`td[data-testid="cell-${columnHeader}-0"]`).click({
button: 'right',
});
- await expect(await this.rootPage.locator('text=Insert New Row')).not.toBeVisible();
+ await expect(this.rootPage.locator('text=Insert New Row')).not.toBeVisible();
// in cell-add
await this.cell.get({ index: 0, columnHeader: 'Cities' }).hover();
await expect(
- await this.cell.get({ index: 0, columnHeader: 'Cities' }).locator('.nc-action-icon.nc-plus')
+ this.cell.get({ index: 0, columnHeader: 'Cities' }).locator('.nc-action-icon.nc-plus')
).not.toBeVisible();
// expand row
await this.cell.get({ index: 0, columnHeader: 'Cities' }).hover();
await expect(
- await this.cell.get({ index: 0, columnHeader: 'Cities' }).locator('.nc-action-icon >> nth=0')
+ this.cell.get({ index: 0, columnHeader: 'Cities' }).locator('.nc-action-icon >> nth=0')
).not.toBeVisible();
}
@@ -361,7 +386,7 @@ export class GridPage extends BasePage {
index: 0,
columnHeader: columnHeader,
});
- await expect(await cell.locator('input')).toBeVisible();
+ await expect(cell.locator('input')).toBeVisible();
// press escape to exit edit mode
await cell.press('Escape');
@@ -370,13 +395,11 @@ export class GridPage extends BasePage {
await this.get().locator(`td[data-testid="cell-${columnHeader}-0"]`).click({
button: 'right',
});
- await expect(await this.rootPage.locator('text=Insert New Row')).toBeVisible();
+ await expect(this.rootPage.locator('text=Insert New Row')).toBeVisible();
// in cell-add
await this.cell.get({ index: 0, columnHeader: 'Cities' }).hover();
- await expect(
- await this.cell.get({ index: 0, columnHeader: 'Cities' }).locator('.nc-action-icon.nc-plus')
- ).toBeVisible();
+ await expect(this.cell.get({ index: 0, columnHeader: 'Cities' }).locator('.nc-action-icon.nc-plus')).toBeVisible();
}
async verifyRoleAccess(param: { role: string }) {
@@ -387,9 +410,9 @@ export class GridPage extends BasePage {
}
async selectRange({ start, end }: { start: CellProps; end: CellProps }) {
- const startCell = await this.cell.get({ index: start.index, columnHeader: start.columnHeader });
- const endCell = await this.cell.get({ index: end.index, columnHeader: end.columnHeader });
- const page = await this.dashboard.get().page();
+ const startCell = this.cell.get({ index: start.index, columnHeader: start.columnHeader });
+ const endCell = this.cell.get({ index: end.index, columnHeader: end.columnHeader });
+ const page = this.dashboard.get().page();
await startCell.hover();
await page.mouse.down();
await endCell.hover();
diff --git a/tests/playwright/pages/Dashboard/Import/ImportTemplate.ts b/tests/playwright/pages/Dashboard/Import/ImportTemplate.ts
index b30f692b67..0a71a30048 100644
--- a/tests/playwright/pages/Dashboard/Import/ImportTemplate.ts
+++ b/tests/playwright/pages/Dashboard/Import/ImportTemplate.ts
@@ -19,7 +19,7 @@ export class ImportTemplatePage extends BasePage {
async getImportTableList() {
await this.get().locator(`.ant-collapse-header`).nth(0).waitFor();
- const tr = await this.get().locator(`.ant-collapse-header`);
+ const tr = this.get().locator(`.ant-collapse-header`);
const rowCount = await tr.count();
const tableList: string[] = [];
for (let i = 0; i < rowCount; i++) {
@@ -32,7 +32,7 @@ export class ImportTemplatePage extends BasePage {
async getImportColumnList() {
// return an array
const columnList: { type: string; name: string }[] = [];
- const tr = await this.get().locator(`tr.ant-table-row-level-0:visible`);
+ const tr = this.get().locator(`tr.ant-table-row-level-0:visible`);
const rowCount = await tr.count();
for (let i = 0; i < rowCount; i++) {
// replace \n and \t from innerText
@@ -51,9 +51,9 @@ export class ImportTemplatePage extends BasePage {
const tblList = await this.getImportTableList();
for (let i = 0; i < result.length; i++) {
- await expect(tblList[i]).toBe(result[i].name);
+ expect(tblList[i]).toBe(result[i].name);
const columnList = await this.getImportColumnList();
- await expect(columnList).toEqual(result[i].columns);
+ expect(columnList).toEqual(result[i].columns);
if (i < result.length - 1) {
await this.expandTableList({ index: i + 1 });
}
diff --git a/tests/playwright/pages/Dashboard/Kanban/index.ts b/tests/playwright/pages/Dashboard/Kanban/index.ts
index 6714a7c6ec..06e9170eb0 100644
--- a/tests/playwright/pages/Dashboard/Kanban/index.ts
+++ b/tests/playwright/pages/Dashboard/Kanban/index.ts
@@ -34,10 +34,10 @@ export class KanbanPage extends BasePage {
async dragDropCard(param: { from: { stack: number; card: number }; to: { stack: number; card: number } }) {
const { from, to } = param;
- const srcStack = await this.get().locator(`.nc-kanban-stack`).nth(from.stack);
- const dstStack = await this.get().locator(`.nc-kanban-stack`).nth(to.stack);
- const fromCard = await srcStack.locator(`.nc-kanban-item`).nth(from.card);
- const toCard = await dstStack.locator(`.nc-kanban-item`).nth(to.card);
+ const srcStack = this.get().locator(`.nc-kanban-stack`).nth(from.stack);
+ const dstStack = this.get().locator(`.nc-kanban-stack`).nth(to.stack);
+ const fromCard = srcStack.locator(`.nc-kanban-item`).nth(from.card);
+ const toCard = dstStack.locator(`.nc-kanban-item`).nth(to.card);
console.log(await fromCard.allTextContents());
console.log(await toCard.allTextContents());
@@ -68,10 +68,10 @@ export class KanbanPage extends BasePage {
const { order } = param;
const stacks = await this.get().locator(`.nc-kanban-stack`).count();
for (let i = 0; i < stacks; i++) {
- const stack = await this.get().locator(`.nc-kanban-stack`).nth(i);
+ const stack = this.get().locator(`.nc-kanban-stack`).nth(i);
await stack.scrollIntoViewIfNeeded();
// Since otherwise stack title will be repeated as title is in two divs, with one having hidden class
- const stackTitle = await stack.locator(`.nc-kanban-stack-head >> [data-testid="truncate-label"]`);
+ const stackTitle = stack.locator(`.nc-kanban-stack-head >> [data-testid="truncate-label"]`);
await expect(stackTitle).toHaveText(order[i], { ignoreCase: true });
}
}
@@ -80,10 +80,10 @@ export class KanbanPage extends BasePage {
const { count } = param;
const stacks = await this.get().locator(`.nc-kanban-stack`).count();
for (let i = 0; i < stacks; i++) {
- const stack = await this.get().locator(`.nc-kanban-stack`).nth(i);
+ const stack = this.get().locator(`.nc-kanban-stack`).nth(i);
await stack.scrollIntoViewIfNeeded();
const stackFooter = await stack.locator(`.nc-kanban-data-count`).innerText();
- await expect(stackFooter).toContain(`${count[i]} record${count[i] !== 1 ? 's' : ''}`);
+ expect(stackFooter).toContain(`${count[i]} record${count[i] !== 1 ? 's' : ''}`);
}
}
@@ -91,7 +91,7 @@ export class KanbanPage extends BasePage {
const { count } = param;
const stacks = await this.get().locator(`.nc-kanban-stack`).count();
for (let i = 0; i < stacks; i++) {
- const stack = await this.get().locator(`.nc-kanban-stack`).nth(i);
+ const stack = this.get().locator(`.nc-kanban-stack`).nth(i);
await stack.scrollIntoViewIfNeeded();
const stackCards = stack.locator(`.nc-kanban-item`);
await expect(stackCards).toHaveCount(count[i]);
@@ -100,11 +100,11 @@ export class KanbanPage extends BasePage {
async verifyCardOrder(param: { order: string[]; stackIndex: number }) {
const { order, stackIndex } = param;
- const stack = await this.get().locator(`.nc-kanban-stack`).nth(stackIndex);
+ const stack = this.get().locator(`.nc-kanban-stack`).nth(stackIndex);
for (let i = 0; i < order.length; i++) {
- const card = await stack.locator(`.nc-kanban-item`).nth(i);
+ const card = stack.locator(`.nc-kanban-item`).nth(i);
await card.scrollIntoViewIfNeeded();
- const cardTitle = await card.locator(`.nc-cell`);
+ const cardTitle = card.locator(`.nc-cell`);
await expect(cardTitle).toHaveText(order[i]);
}
}
@@ -121,7 +121,7 @@ export class KanbanPage extends BasePage {
async collapseStack(param: { index: number }) {
await this.get().locator(`.nc-kanban-stack-head`).nth(param.index).click();
- const modal = await this.rootPage.locator(`.nc-dropdown-kanban-stack-context-menu`);
+ const modal = this.rootPage.locator(`.nc-dropdown-kanban-stack-context-menu`);
await modal.locator('.ant-dropdown-menu-item:has-text("Collapse Stack")').click();
}
@@ -135,15 +135,15 @@ export class KanbanPage extends BasePage {
async addCard(param: { stackIndex: number }) {
await this.get().locator(`.nc-kanban-stack-head`).nth(param.stackIndex).click();
- const modal = await this.rootPage.locator(`.nc-dropdown-kanban-stack-context-menu`);
+ const modal = this.rootPage.locator(`.nc-dropdown-kanban-stack-context-menu`);
await modal.locator('.ant-dropdown-menu-item:has-text("Add new record")').click();
}
async deleteStack(param: { index: number }) {
await this.get().locator(`.nc-kanban-stack-head`).nth(param.index).click();
- const modal = await this.rootPage.locator(`.nc-dropdown-kanban-stack-context-menu`);
+ const modal = this.rootPage.locator(`.nc-dropdown-kanban-stack-context-menu`);
await modal.locator('.ant-dropdown-menu-item:has-text("Delete Stack")').click();
- const confirmationModal = await this.rootPage.locator(`div.ant-modal-content`);
+ const confirmationModal = this.rootPage.locator(`div.ant-modal-content`);
await confirmationModal.locator(`button:has-text("Delete Stack")`).click();
}
}
diff --git a/tests/playwright/pages/Dashboard/Map/index.ts b/tests/playwright/pages/Dashboard/Map/index.ts
index e3dbfbd4e3..5ffb2e51fa 100644
--- a/tests/playwright/pages/Dashboard/Map/index.ts
+++ b/tests/playwright/pages/Dashboard/Map/index.ts
@@ -22,7 +22,7 @@ export class MapPage extends BasePage {
async marker(lat: string, long: string) {
const latLongStr = `${lat}, ${long}`;
- const marker = await this.get().locator(`.leaflet-marker-pane img[alt="${latLongStr}"]`);
+ const marker = this.get().locator(`.leaflet-marker-pane img[alt="${latLongStr}"]`);
return marker;
}
@@ -35,12 +35,12 @@ export class MapPage extends BasePage {
}
async verifyMarkerCount(count: number) {
- const markers = await this.get().locator('.leaflet-marker-pane img');
+ const markers = this.get().locator('.leaflet-marker-pane img');
await expect(markers).toHaveCount(count);
}
async zoomOut(times = 10) {
- const zoomOutButton = await this.get().locator('.leaflet-control-zoom-out');
+ const zoomOutButton = this.get().locator('.leaflet-control-zoom-out');
for (let i = 0; i < times; i++) {
await zoomOutButton.click();
await this.rootPage.waitForTimeout(400);
diff --git a/tests/playwright/pages/Dashboard/ProjectView/AccessSettingsPage.ts b/tests/playwright/pages/Dashboard/ProjectView/AccessSettingsPage.ts
index 91c2d0d7ce..7c66bffb6f 100644
--- a/tests/playwright/pages/Dashboard/ProjectView/AccessSettingsPage.ts
+++ b/tests/playwright/pages/Dashboard/ProjectView/AccessSettingsPage.ts
@@ -17,10 +17,10 @@ export class AccessSettingsPage extends BasePage {
await this.get().locator('.nc-collaborators-list-row').nth(0).waitFor({ state: 'visible' });
const userCount = await this.get().locator('.nc-collaborators-list-row').count();
for (let i = 0; i < userCount; i++) {
- const user = await this.get().locator('.nc-collaborators-list-row').nth(i);
+ const user = this.get().locator('.nc-collaborators-list-row').nth(i);
const userEmail = (await user.locator('.email').innerText()).split('\n')[1];
if (userEmail === email) {
- const roleDropdown = await user.locator('.nc-collaborator-role-select');
+ const roleDropdown = user.locator('.nc-collaborator-role-select');
const selectedRole = await user.locator('.nc-collaborator-role-select').innerText();
diff --git a/tests/playwright/pages/Dashboard/ProjectView/Audit.ts b/tests/playwright/pages/Dashboard/ProjectView/Audit.ts
new file mode 100644
index 0000000000..c9482c0e5e
--- /dev/null
+++ b/tests/playwright/pages/Dashboard/ProjectView/Audit.ts
@@ -0,0 +1,76 @@
+import { expect } from '@playwright/test';
+import BasePage from '../../Base';
+import { DataSourcePage } from './DataSourcePage';
+
+export class AuditPage extends BasePage {
+ constructor(dataSource: DataSourcePage) {
+ super(dataSource.rootPage);
+ }
+
+ get() {
+ return this.rootPage.locator('div.ant-modal-content');
+ }
+ async verifyRow({
+ index,
+ opType,
+ opSubtype,
+ description,
+ user,
+ created,
+ }: {
+ index: number;
+ opType?: string;
+ opSubtype?: string;
+ description?: string;
+ user?: string;
+ created?: string;
+ }) {
+ const table = this.get().locator('[data-testid="audit-tab-table"]');
+ const row = table.locator(`tr.ant-table-row`).nth(index);
+
+ if (opType) {
+ await row
+ .locator(`td.ant-table-cell`)
+ .nth(0)
+ .textContent()
+ .then(async text => expect(text).toContain(opType));
+ }
+
+ if (opSubtype) {
+ await row
+ .locator(`td.ant-table-cell`)
+ .nth(1)
+ .textContent()
+ .then(async text => expect(text).toContain(opSubtype));
+ }
+
+ if (description) {
+ await row
+ .locator(`td.ant-table-cell`)
+ .nth(2)
+ .textContent()
+ .then(async text => expect(text).toContain(description));
+ }
+
+ if (user) {
+ await row
+ .locator(`td.ant-table-cell`)
+ .nth(3)
+ .textContent()
+ .then(async text => expect(text).toContain(user));
+ }
+
+ if (created) {
+ await row
+ .locator(`td.ant-table-cell`)
+ .nth(4)
+ .textContent()
+ .then(async text => expect(text).toContain(created));
+ }
+ }
+
+ async close() {
+ await this.get().click();
+ await this.rootPage.keyboard.press('Escape');
+ }
+}
diff --git a/tests/playwright/pages/Dashboard/ProjectView/DataSourcePage.ts b/tests/playwright/pages/Dashboard/ProjectView/DataSourcePage.ts
index 31c07b91ba..8732bd7f2a 100644
--- a/tests/playwright/pages/Dashboard/ProjectView/DataSourcePage.ts
+++ b/tests/playwright/pages/Dashboard/ProjectView/DataSourcePage.ts
@@ -1,15 +1,21 @@
import BasePage from '../../Base';
import { ProjectViewPage } from './index';
import { Locator } from '@playwright/test';
+import { MetaDataPage } from './Metadata';
+import { AuditPage } from './Audit';
export class DataSourcePage extends BasePage {
readonly projectView: ProjectViewPage;
readonly databaseType: Locator;
+ readonly metaData: MetaDataPage;
+ readonly audit: AuditPage;
constructor(projectView: ProjectViewPage) {
super(projectView.rootPage);
this.projectView = projectView;
this.databaseType = this.get().locator('.nc-extdb-db-type');
+ this.metaData = new MetaDataPage(this);
+ this.audit = new AuditPage(this);
}
get() {
@@ -18,7 +24,7 @@ export class DataSourcePage extends BasePage {
async getDatabaseTypeList() {
await this.databaseType.click();
- const nodes = await this.rootPage.locator('.nc-dropdown-ext-db-type').locator('.ant-select-item');
+ const nodes = this.rootPage.locator('.nc-dropdown-ext-db-type').locator('.ant-select-item');
const list = [];
for (let i = 0; i < (await nodes.count()); i++) {
const node = nodes.nth(i);
@@ -28,12 +34,27 @@ export class DataSourcePage extends BasePage {
return list;
}
+ async openMetaSync({ rowIndex }: { rowIndex: number }) {
+ // 0th offset for header
+ const row = this.get()
+ .locator('.ds-table-row')
+ .nth(rowIndex + 1);
+ await row.locator('button.nc-action-btn:has-text("Sync Metadata")').click();
+ }
+
async openERD({ rowIndex }: { rowIndex: number }) {
- // hardwired
- await this.rootPage.locator('button.nc-action-btn').nth(1).click();
+ // 0th offset for header
+ const row = this.get()
+ .locator('.ds-table-row')
+ .nth(rowIndex + 1);
+ await row.locator('button.nc-action-btn:has-text("Relations")').click();
+ }
- // const row = this.get().locator('.ds-table-row').nth(rowIndex);
- // await row.locator('.ds-table-actions').locator('button.nc-action-btn').waitFor();
- // await row.locator('.ds-table-actions').locator('button.nc-action-btn').nth(1).click();
+ async openAudit({ rowIndex }: { rowIndex: number }) {
+ // 0th offset for header
+ const row = this.get()
+ .locator('.ds-table-row')
+ .nth(rowIndex + 1);
+ await row.locator('button.nc-action-btn:has-text("Audit")').click();
}
}
diff --git a/tests/playwright/pages/Dashboard/Settings/Metadata.ts b/tests/playwright/pages/Dashboard/ProjectView/Metadata.ts
similarity index 69%
rename from tests/playwright/pages/Dashboard/Settings/Metadata.ts
rename to tests/playwright/pages/Dashboard/ProjectView/Metadata.ts
index b93c629a1d..fb821c4897 100644
--- a/tests/playwright/pages/Dashboard/Settings/Metadata.ts
+++ b/tests/playwright/pages/Dashboard/ProjectView/Metadata.ts
@@ -1,18 +1,15 @@
import { expect } from '@playwright/test';
import BasePage from '../../Base';
-import { DataSourcesPage } from './DataSources';
import { getTextExcludeIconText } from '../../../tests/utils/general';
+import { DataSourcePage } from './DataSourcePage';
export class MetaDataPage extends BasePage {
- private readonly dataSources: DataSourcesPage;
-
- constructor(dataSources: DataSourcesPage) {
- super(dataSources.rootPage);
- this.dataSources = dataSources;
+ constructor(dataSource: DataSourcePage) {
+ super(dataSource.rootPage);
}
get() {
- return this.dataSources.get();
+ return this.rootPage.locator('div.ant-modal-content');
}
async clickReload() {
@@ -24,6 +21,13 @@ export class MetaDataPage extends BasePage {
await this.get().locator(`.animate-spin`).waitFor({ state: 'detached' });
}
+ async close() {
+ await this.get().click();
+ await this.rootPage.keyboard.press('Escape');
+ await this.rootPage.keyboard.press('Escape');
+ await this.get().waitFor({ state: 'detached' });
+ }
+
async sync() {
await this.get().locator(`button:has-text("Sync Now")`).click();
await this.verifyToast({ message: 'Table metadata recreated successfully' });
@@ -32,9 +36,9 @@ export class MetaDataPage extends BasePage {
}
async verifyRow({ index, model, state }: { index: number; model: string; state: string }) {
- const fieldLocator = await this.get().locator(`tr.ant-table-row`).nth(index).locator(`td.ant-table-cell`).nth(0);
+ const fieldLocator = this.get().locator(`tr.ant-table-row`).nth(index).locator(`td.ant-table-cell`).nth(0);
const fieldText = await getTextExcludeIconText(fieldLocator);
- await expect(fieldText).toBe(model);
+ expect(fieldText).toBe(model);
await expect(this.get().locator(`tr.ant-table-row`).nth(index).locator(`td.ant-table-cell`).nth(1)).toHaveText(
state,
diff --git a/tests/playwright/pages/Dashboard/QrCodeOverlay/index.ts b/tests/playwright/pages/Dashboard/QrCodeOverlay/index.ts
index 7c34a45aec..5909a89347 100644
--- a/tests/playwright/pages/Dashboard/QrCodeOverlay/index.ts
+++ b/tests/playwright/pages/Dashboard/QrCodeOverlay/index.ts
@@ -18,7 +18,7 @@ export class QrCodeOverlay extends BasePage {
const foundQrValueLabelText = await this.get()
.locator('[data-testid="nc-qr-code-large-value-label"]')
.textContent();
- await expect(foundQrValueLabelText).toContain(expectedValue);
+ expect(foundQrValueLabelText).toContain(expectedValue);
}
async clickCloseButton() {
diff --git a/tests/playwright/pages/Dashboard/Settings/Audit.ts b/tests/playwright/pages/Dashboard/Settings/Audit.ts
index d2a790fc76..f4281f75fe 100644
--- a/tests/playwright/pages/Dashboard/Settings/Audit.ts
+++ b/tests/playwright/pages/Dashboard/Settings/Audit.ts
@@ -29,7 +29,7 @@ export class AuditSettingsPage extends BasePage {
user?: string;
created?: string;
}) {
- const table = await this.get();
+ const table = this.get();
const row = table.locator(`tr.ant-table-row`).nth(index);
if (opType) {
@@ -37,7 +37,7 @@ export class AuditSettingsPage extends BasePage {
.locator(`td.ant-table-cell`)
.nth(0)
.textContent()
- .then(async text => await expect(text).toContain(opType));
+ .then(async text => expect(text).toContain(opType));
}
if (opSubtype) {
@@ -45,7 +45,7 @@ export class AuditSettingsPage extends BasePage {
.locator(`td.ant-table-cell`)
.nth(1)
.textContent()
- .then(async text => await expect(text).toContain(opSubtype));
+ .then(async text => expect(text).toContain(opSubtype));
}
if (description) {
@@ -53,7 +53,7 @@ export class AuditSettingsPage extends BasePage {
.locator(`td.ant-table-cell`)
.nth(2)
.textContent()
- .then(async text => await expect(text).toContain(description));
+ .then(async text => expect(text).toContain(description));
}
if (user) {
@@ -61,7 +61,7 @@ export class AuditSettingsPage extends BasePage {
.locator(`td.ant-table-cell`)
.nth(3)
.textContent()
- .then(async text => await expect(text).toContain(user));
+ .then(async text => expect(text).toContain(user));
}
if (created) {
@@ -69,7 +69,7 @@ export class AuditSettingsPage extends BasePage {
.locator(`td.ant-table-cell`)
.nth(4)
.textContent()
- .then(async text => await expect(text).toContain(created));
+ .then(async text => expect(text).toContain(created));
}
}
}
diff --git a/tests/playwright/pages/Dashboard/Settings/DataSources.ts b/tests/playwright/pages/Dashboard/Settings/DataSources.ts
index 1da4c02416..d1b5ce646e 100644
--- a/tests/playwright/pages/Dashboard/Settings/DataSources.ts
+++ b/tests/playwright/pages/Dashboard/Settings/DataSources.ts
@@ -3,7 +3,7 @@ import { defaultBaseName } from '../../../constants';
import BasePage from '../../Base';
import { AclPage } from './Acl';
import { SettingsErdPage } from './Erd';
-import { MetaDataPage } from './Metadata';
+import { MetaDataPage } from '../ProjectView/Metadata';
export class DataSourcesPage extends BasePage {
private readonly settings: SettingsPage;
diff --git a/tests/playwright/pages/Dashboard/Settings/Teams.ts b/tests/playwright/pages/Dashboard/Settings/Teams.ts
index d7dc46004b..1b8ff1eff1 100644
--- a/tests/playwright/pages/Dashboard/Settings/Teams.ts
+++ b/tests/playwright/pages/Dashboard/Settings/Teams.ts
@@ -38,7 +38,7 @@ export class TeamsPage extends BasePage {
await this.inviteTeamModal.getByTestId('docs-share-dlg-share-project-collaborate-emails').fill(email);
await this.inviteTeamModal.getByTestId('nc-share-invite-user-role-option-viewer').click();
- const dropdown = await this.rootPage.locator('.nc-dropdown-user-role');
+ const dropdown = this.rootPage.locator('.nc-dropdown-user-role');
await dropdown.locator(`.nc-role-option:has-text("${role}")`).click();
await this.inviteTeamModal.getByTestId('docs-share-btn').click();
await this.inviteTeamModal.getByTestId('docs-share-invitation-copy').waitFor({ state: 'visible', timeout: 2000 });
@@ -72,14 +72,14 @@ export class TeamsPage extends BasePage {
if (toggle) {
// if share base was disabled && request was to enable
await toggleBtn.click();
- const modal = await this.rootPage.locator(`.nc-dropdown-shared-base-toggle`);
+ const modal = this.rootPage.locator(`.nc-dropdown-shared-base-toggle`);
await modal.locator(`.ant-dropdown-menu-title-content`).click();
}
} else {
if (!toggle) {
// if share base was enabled && request was to disable
await toggleBtn.click();
- const modal = await this.rootPage.locator(`.nc-dropdown-shared-base-toggle`);
+ const modal = this.rootPage.locator(`.nc-dropdown-shared-base-toggle`);
await modal.locator(`.ant-dropdown-menu-title-content`).click();
}
}
@@ -106,7 +106,7 @@ export class TeamsPage extends BasePage {
// .locator(`.nc-shared-base-role`)
// .waitFor();
await this.getSharedBaseSubModal().locator(`.nc-shared-base-role:visible`).click();
- const userRoleModal = await this.rootPage.locator(`.nc-dropdown-share-base-role:visible`);
+ const userRoleModal = this.rootPage.locator(`.nc-dropdown-share-base-role:visible`);
await userRoleModal.locator(`.ant-select-item-option-content:has-text("${role}"):visible`).click();
}
}
diff --git a/tests/playwright/pages/Dashboard/ShareProjectButton/index.ts b/tests/playwright/pages/Dashboard/ShareProjectButton/index.ts
index a1e4504800..c616d89584 100644
--- a/tests/playwright/pages/Dashboard/ShareProjectButton/index.ts
+++ b/tests/playwright/pages/Dashboard/ShareProjectButton/index.ts
@@ -1,4 +1,4 @@
-import { expect, Locator } from '@playwright/test';
+import { expect } from '@playwright/test';
import { DashboardPage } from '..';
import BasePage from '../../Base';
diff --git a/tests/playwright/pages/Dashboard/Sidebar/DocsSidebar.ts b/tests/playwright/pages/Dashboard/Sidebar/DocsSidebar.ts
index e4567c466c..905b353d46 100644
--- a/tests/playwright/pages/Dashboard/Sidebar/DocsSidebar.ts
+++ b/tests/playwright/pages/Dashboard/Sidebar/DocsSidebar.ts
@@ -1,4 +1,4 @@
-import { expect, Locator } from '@playwright/test';
+import { expect } from '@playwright/test';
import { SidebarPage } from '.';
import BasePage from '../../Base';
diff --git a/tests/playwright/pages/Dashboard/SurveyForm/index.ts b/tests/playwright/pages/Dashboard/SurveyForm/index.ts
index 87d3b8d789..26e9d8aaca 100644
--- a/tests/playwright/pages/Dashboard/SurveyForm/index.ts
+++ b/tests/playwright/pages/Dashboard/SurveyForm/index.ts
@@ -51,7 +51,7 @@ export class SurveyFormPage extends BasePage {
fieldLabel = fieldLabel.replace(/\u00A0/g, ' ');
fieldText = fieldText.replace(/\u00A0/g, ' ');
- await expect(fieldText).toBe(fieldLabel);
+ expect(fieldText).toBe(fieldLabel);
// parse footer text ("1 / 3") to identify if last slide
let isLastSlide = false;
@@ -76,7 +76,7 @@ export class SurveyFormPage extends BasePage {
await this.get().locator(`[data-testid="nc-survey-form__input-${param.fieldLabel}"] >> input`).press('Enter');
} else if (param.type === 'DateTime') {
await this.get().locator(`[data-testid="nc-survey-form__input-${param.fieldLabel}"] >> input`).click();
- const modal = await this.rootPage.locator('.nc-picker-datetime');
+ const modal = this.rootPage.locator('.nc-picker-datetime');
await expect(modal).toBeVisible();
await modal.locator('.ant-picker-now-btn').click();
await modal.locator('.ant-picker-ok').click();
diff --git a/tests/playwright/pages/Dashboard/TreeView.ts b/tests/playwright/pages/Dashboard/TreeView.ts
index 3e4010affe..1690973745 100644
--- a/tests/playwright/pages/Dashboard/TreeView.ts
+++ b/tests/playwright/pages/Dashboard/TreeView.ts
@@ -63,7 +63,7 @@ export class TreeViewPage extends BasePage {
}
async openBase({ title }: { title: string }) {
- const nodes = await this.get().locator(`[data-testid="nc-sidebar-project-${title.toLowerCase()}"]`);
+ const nodes = this.get().locator(`[data-testid="nc-sidebar-project-${title.toLowerCase()}"]`);
await nodes.click();
return;
}
@@ -164,11 +164,12 @@ export class TreeViewPage extends BasePage {
await this.dashboard.get().locator('div.nc-project-menu-item:has-text("Delete"):visible').click();
await this.waitForResponse({
- uiAction: () => {
+ uiAction: async () => {
// Create a promise that resolves after 1 second
- const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
+ const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
// Returning a promise that resolves with the result after the 1-second delay
- return delay(100).then(() => this.dashboard.get().locator('button:has-text("Delete Table")').click());
+ await delay(100);
+ return await this.dashboard.get().locator('button:has-text("Delete Table")').click();
},
httpMethodsToMatch: ['DELETE'],
requestUrlPathToMatch: `/api/v1/db/meta/tables/`,
@@ -210,7 +211,7 @@ export class TreeViewPage extends BasePage {
await settingsMenu.locator(`[data-menu-id="teamAndSettings"]`).click();
}
- async quickImport({ title, projectTitle }: { title: string; projectTitle }) {
+ async quickImport({ title, projectTitle }: { title: string; projectTitle: string }) {
await this.getProjectContextMenu({ projectTitle }).hover();
await this.getProjectContextMenu({ projectTitle }).click();
const importMenu = this.dashboard.get().locator('.ant-dropdown-menu.nc-scrollbar-md');
@@ -223,7 +224,7 @@ export class TreeViewPage extends BasePage {
await this.get().locator(`.nc-project-tree-tbl-${title} .nc-table-icon`).click();
await this.rootPage.locator('.emoji-mart-search').type(icon);
- const emojiList = await this.rootPage.locator('[id="emoji-mart-list"]');
+ const emojiList = this.rootPage.locator('[id="emoji-mart-list"]');
await emojiList.locator('button').first().click();
await expect(
this.get().locator(`.nc-project-tree-tbl-${title}`).locator(`.nc-table-icon:has-text("${iconDisplay}")`)
@@ -235,14 +236,14 @@ export class TreeViewPage extends BasePage {
await this.dashboard.get().locator('div.nc-project-menu-item:has-text("Duplicate")').click();
// Find the checkbox element with the label "Include data"
- const includeDataCheckbox = await this.dashboard.get().getByText('Include data', { exact: true });
+ const includeDataCheckbox = this.dashboard.get().getByText('Include data', { exact: true });
// Check the checkbox if it is not already checked
if ((await includeDataCheckbox.isChecked()) && !includeData) {
await includeDataCheckbox.click(); // click the checkbox to check it
}
// Find the checkbox element with the label "Include data"
- const includeViewsCheckbox = await this.dashboard.get().getByText('Include views', { exact: true });
+ const includeViewsCheckbox = this.dashboard.get().getByText('Include views', { exact: true });
// Check the checkbox if it is not already checked
if ((await includeViewsCheckbox.isChecked()) && !includeViews) {
await includeViewsCheckbox.click(); // click the checkbox to check it
@@ -319,12 +320,36 @@ export class TreeViewPage extends BasePage {
return this.get().locator(`.project-title-node`).nth(param.index);
}
+ async renameProject(param: { newTitle: string; title: string }) {
+ await this.getProjectContextMenu({ projectTitle: param.title }).hover();
+ await this.getProjectContextMenu({ projectTitle: param.title }).click();
+ const contextMenu = this.dashboard.get().locator('.ant-dropdown-menu.nc-scrollbar-md:visible').last();
+ await contextMenu.waitFor();
+ await contextMenu.locator(`.ant-dropdown-menu-item:has-text("Edit")`).click();
+
+ const projectNodeInput = (await this.getProject({ index: 0, title: param.title })).locator('input');
+ await projectNodeInput.clear();
+ await projectNodeInput.fill(param.newTitle);
+ await projectNodeInput.press('Enter');
+ }
+
async deleteProject(param: { title: string }) {
await this.getProjectContextMenu({ projectTitle: param.title }).hover();
await this.getProjectContextMenu({ projectTitle: param.title }).click();
- const contextMenu = this.dashboard.get().locator('.ant-dropdown-menu.nc-scrollbar-md');
+ const contextMenu = this.dashboard.get().locator('.ant-dropdown-menu.nc-scrollbar-md:visible').last();
+ await contextMenu.waitFor();
await contextMenu.locator(`.ant-dropdown-menu-item:has-text("Delete")`).click();
await this.rootPage.locator('div.ant-modal-content').locator(`button.ant-btn:has-text("Delete Project")`).click();
}
+
+ async duplicateProject(param: { title: string }) {
+ await this.getProjectContextMenu({ projectTitle: param.title }).hover();
+ await this.getProjectContextMenu({ projectTitle: param.title }).click();
+ const contextMenu = this.dashboard.get().locator('.ant-dropdown-menu.nc-scrollbar-md:visible');
+ await contextMenu.waitFor();
+ await contextMenu.locator(`.ant-dropdown-menu-item:has-text("Duplicate Project")`).click();
+
+ await this.rootPage.locator('div.ant-modal-content').locator(`button.ant-btn:has-text("Confirm")`).click();
+ }
}
diff --git a/tests/playwright/pages/Dashboard/ViewSidebar/index.ts b/tests/playwright/pages/Dashboard/ViewSidebar/index.ts
index ca2012bc68..e4c38978eb 100644
--- a/tests/playwright/pages/Dashboard/ViewSidebar/index.ts
+++ b/tests/playwright/pages/Dashboard/ViewSidebar/index.ts
@@ -177,7 +177,7 @@ export class ViewSidebarPage extends BasePage {
await this.get().locator(`[data-testid="view-sidebar-view-${title}"] .nc-view-icon`).click();
await this.rootPage.locator('.emoji-mart-search').type(icon);
- const emojiList = await this.rootPage.locator('[id="emoji-mart-list"]');
+ const emojiList = this.rootPage.locator('[id="emoji-mart-list"]');
await emojiList.locator('button').first().click();
await expect(
this.get()
diff --git a/tests/playwright/pages/Dashboard/WebhookForm/index.ts b/tests/playwright/pages/Dashboard/WebhookForm/index.ts
index acceb38aff..33696d178a 100644
--- a/tests/playwright/pages/Dashboard/WebhookForm/index.ts
+++ b/tests/playwright/pages/Dashboard/WebhookForm/index.ts
@@ -68,17 +68,17 @@ export class WebhookFormPage extends BasePage {
save: boolean;
}) {
await this.get().locator(`.nc-check-box-hook-condition`).click();
- const modal = await this.get().locator(`.menu-filter-dropdown`).last();
+ const modal = this.get().locator(`.menu-filter-dropdown`).last();
await modal.locator(`button:has-text("Add Filter")`).click();
await modal.locator('.nc-filter-field-select').waitFor({ state: 'visible', timeout: 4000 });
await modal.locator('.nc-filter-field-select').click();
- const modalField = await this.dashboard.rootPage.locator('.nc-dropdown-toolbar-field-list:visible');
+ const modalField = this.dashboard.rootPage.locator('.nc-dropdown-toolbar-field-list:visible');
await modalField.locator(`.ant-select-item:has-text("${column}")`).click();
await modal.locator('.nc-filter-operation-select').click();
- const modalOp = await this.dashboard.rootPage.locator('.nc-dropdown-filter-comp-op:visible');
+ const modalOp = this.dashboard.rootPage.locator('.nc-dropdown-filter-comp-op:visible');
await modalOp.locator(`.ant-select-item:has-text("${operator}")`).click();
if (operator != 'is null' && operator != 'is not null') {
@@ -203,7 +203,7 @@ export class WebhookFormPage extends BasePage {
const locator = this.get().locator('.nc-select-hook-notification-type >> .ant-select-selection-item');
const text = await getTextExcludeIconText(locator);
- await expect(text).toBe(notificationType);
+ expect(text).toBe(notificationType);
await expect(this.get().locator('.nc-select-hook-url-method >> .ant-select-selection-item')).toHaveText(urlMethod);
await expect.poll(async () => await this.get().locator('input.nc-text-field-hook-url-path').inputValue()).toBe(url);
diff --git a/tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts b/tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts
index 6f2a73d29b..bbee30240c 100644
--- a/tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts
+++ b/tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts
@@ -46,14 +46,14 @@ export class AttachmentCellPageObject extends BasePage {
}
async verifyFile({ index, columnHeader }: { index: number; columnHeader: string }) {
- await expect(await this.get({ index, columnHeader }).locator('.nc-attachment')).toBeVisible();
+ await expect(this.get({ index, columnHeader }).locator('.nc-attachment')).toBeVisible();
}
async verifyFileCount({ index, columnHeader, count }: { index: number; columnHeader: string; count: number }) {
// retry below logic for 5 times, with 1 second delay
let retryCount = 0;
while (retryCount < 5) {
- const attachments = await this.get({ index, columnHeader }).locator('.nc-attachment');
+ const attachments = this.get({ index, columnHeader }).locator('.nc-attachment');
// console.log(await attachments.count());
if ((await attachments.count()) === count) {
break;
diff --git a/tests/playwright/pages/Dashboard/common/Cell/DateCell.ts b/tests/playwright/pages/Dashboard/common/Cell/DateCell.ts
index 76259cb872..90b9171c37 100644
--- a/tests/playwright/pages/Dashboard/common/Cell/DateCell.ts
+++ b/tests/playwright/pages/Dashboard/common/Cell/DateCell.ts
@@ -22,7 +22,7 @@ export class DateCellPageObject extends BasePage {
}
async verify({ index, columnHeader, date }: { index: number; columnHeader: string; date: string }) {
- const cell = await this.get({ index, columnHeader });
+ const cell = this.get({ index, columnHeader });
await cell.scrollIntoViewIfNeeded();
await expect(cell.locator(`[title="${date}"]`)).toBeVisible();
}
diff --git a/tests/playwright/pages/Dashboard/common/Cell/DateTimeCell.ts b/tests/playwright/pages/Dashboard/common/Cell/DateTimeCell.ts
index 2d12b39d12..227c939384 100644
--- a/tests/playwright/pages/Dashboard/common/Cell/DateTimeCell.ts
+++ b/tests/playwright/pages/Dashboard/common/Cell/DateTimeCell.ts
@@ -85,7 +85,7 @@ export class DateTimeCellPageObject extends BasePage {
async setDateTime({ index, columnHeader, dateTime }: { index: number; columnHeader: string; dateTime: string }) {
const [date, time] = dateTime.split(' ');
- const [hour, minute, second] = time.split(':');
+ const [hour, minute, _second] = time.split(':');
await this.open({ index, columnHeader });
await this.selectDate({ date });
await this.selectTime({ hour: +hour, minute: +minute });
diff --git a/tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts b/tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts
index aed481fc4b..363a71a2e5 100644
--- a/tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts
+++ b/tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts
@@ -24,7 +24,7 @@ export class RatingCellPageObject extends BasePage {
}
async verify({ index, columnHeader, rating }: { index: number; columnHeader: string; rating: number }) {
- const cell = await this.get({ index, columnHeader });
+ const cell = this.get({ index, columnHeader });
await cell.scrollIntoViewIfNeeded();
await expect(cell.locator(`li.ant-rate-star.ant-rate-star-full`)).toHaveCount(rating);
}
diff --git a/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts b/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts
index 01a0cdc999..8ce4a2f27e 100644
--- a/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts
+++ b/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts
@@ -91,7 +91,7 @@ export class SelectOptionCellPageObject extends BasePage {
return await expect(this.cell.get({ index, columnHeader })).toContainText(option, { useInnerText: true });
}
- const locator = await this.cell.get({ index, columnHeader }).locator('.ant-tag');
+ const locator = this.cell.get({ index, columnHeader }).locator('.ant-tag');
await locator.waitFor({ state: 'visible' });
const text = await locator.allInnerTexts();
return expect(text).toContain(option);
diff --git a/tests/playwright/pages/Dashboard/common/Cell/TimeCell.ts b/tests/playwright/pages/Dashboard/common/Cell/TimeCell.ts
index 897fd91a67..283217e83a 100644
--- a/tests/playwright/pages/Dashboard/common/Cell/TimeCell.ts
+++ b/tests/playwright/pages/Dashboard/common/Cell/TimeCell.ts
@@ -15,7 +15,7 @@ export class TimeCellPageObject extends BasePage {
}
async verify({ index, columnHeader, value }: { index: number; columnHeader: string; value: string }) {
- const cell = await this.get({ index, columnHeader });
+ const cell = this.get({ index, columnHeader });
await cell.scrollIntoViewIfNeeded();
await cell.locator(`input[title="${value}"]`).waitFor({ state: 'visible' });
await expect(cell.locator(`[title="${value}"]`)).toBeVisible();
@@ -31,7 +31,7 @@ export class TimeCellPageObject extends BasePage {
hour: number;
minute: number;
}) {
- const timePanel = await this.rootPage.locator('.ant-picker-time-panel-column');
+ const timePanel = this.rootPage.locator('.ant-picker-time-panel-column');
await timePanel.nth(0).locator('.ant-picker-time-panel-cell').nth(hour).click();
await timePanel.nth(1).locator('.ant-picker-time-panel-cell').nth(minute).click();
if (hour < 12) {
diff --git a/tests/playwright/pages/Dashboard/common/Cell/YearCell.ts b/tests/playwright/pages/Dashboard/common/Cell/YearCell.ts
index d622bf1e63..292d7bf3bb 100644
--- a/tests/playwright/pages/Dashboard/common/Cell/YearCell.ts
+++ b/tests/playwright/pages/Dashboard/common/Cell/YearCell.ts
@@ -15,7 +15,7 @@ export class YearCellPageObject extends BasePage {
}
async verify({ index, columnHeader, value }: { index: number; columnHeader: string; value: number }) {
- const cell = await this.get({ index, columnHeader });
+ const cell = this.get({ index, columnHeader });
await cell.scrollIntoViewIfNeeded();
await cell.locator(`input[title="${value}"]`).waitFor({ state: 'visible' });
await expect(cell.locator(`[title="${value}"]`)).toBeVisible();
diff --git a/tests/playwright/pages/Dashboard/common/Cell/index.ts b/tests/playwright/pages/Dashboard/common/Cell/index.ts
index 73366bcf08..084638c2ab 100644
--- a/tests/playwright/pages/Dashboard/common/Cell/index.ts
+++ b/tests/playwright/pages/Dashboard/common/Cell/index.ts
@@ -135,10 +135,7 @@ export class CellPageObject extends BasePage {
columnHeader,
}).scrollIntoViewIfNeeded();
while (count < 5) {
- const innerTexts = await this.get({
- index,
- columnHeader,
- }).allInnerTexts();
+ const innerTexts = await getTextExcludeIconText(this.get({ index, columnHeader }));
const cellText = typeof innerTexts === 'string' ? [innerTexts] : innerTexts;
if (cellText) {
@@ -148,7 +145,12 @@ export class CellPageObject extends BasePage {
}
await this.rootPage.waitForTimeout(1000);
count++;
- if (count === 5) throw new Error(`Cell text ${text} not found`);
+ if (count === 5) {
+ console.log('cellText', cellText);
+ console.log('text', text);
+
+ throw new Error(`Cell text "${text}" not found`);
+ }
}
};
@@ -254,14 +256,11 @@ export class CellPageObject extends BasePage {
columnHeader: string;
expectedSvgValue: string;
}) {
- const _verify = async expectedBarcodeSvg => {
+ const _verify = async (expectedBarcodeSvg: unknown) => {
await expect
.poll(async () => {
- const barcodeCell = await this.get({
- index,
- columnHeader,
- });
- const barcodeSvg = await barcodeCell.getByTestId('barcode');
+ const barcodeCell = this.get({ index, columnHeader });
+ const barcodeSvg = barcodeCell.getByTestId('barcode');
return await barcodeSvg.innerHTML();
})
.toEqual(expectedBarcodeSvg);
@@ -290,12 +289,12 @@ export class CellPageObject extends BasePage {
verifyChildList?: boolean;
options?: { singular?: string; plural?: string };
}) {
- const cell = await this.get({ index, columnHeader });
- const linkText = await cell.locator('.nc-datatype-link');
+ const cell = this.get({ index, columnHeader });
+ const linkText = cell.locator('.nc-datatype-link');
await cell.scrollIntoViewIfNeeded();
- // lazy load- give enough time for cell to load
+ // lazy load - give enough time for cell to load
await this.rootPage.waitForTimeout(1000);
if (type === 'bt') {
@@ -342,7 +341,7 @@ export class CellPageObject extends BasePage {
await this.rootPage.waitForSelector('.nc-modal-child-list:visible');
// verify child list count & contents
- const childList = await this.rootPage.locator('.ant-card:visible');
+ const childList = this.rootPage.locator('.ant-card:visible');
expect(await childList.count()).toBe(count);
// close child list
@@ -377,23 +376,23 @@ export class CellPageObject extends BasePage {
const role = param.role.toLowerCase();
const count = role === 'creator' || role === 'editor' || role === 'owner' ? 1 : 0;
// normal text cell
- const cell = await this.get({ index: 0, columnHeader: 'Country' });
+ const cell = this.get({ index: 0, columnHeader: 'Country' });
// editable cell
await cell.dblclick();
- await expect(await cell.locator(`input`)).toHaveCount(count);
+ await expect(cell.locator(`input`)).toHaveCount(count);
// press escape to close the input
await cell.press('Escape');
await cell.press('Escape');
await cell.click({ button: 'right', clickCount: 1 });
- await expect(await this.rootPage.locator(`.nc-dropdown-grid-context-menu:visible`)).toHaveCount(count);
+ await expect(this.rootPage.locator(`.nc-dropdown-grid-context-menu:visible`)).toHaveCount(count);
// virtual cell
- const vCell = await this.get({ index: 0, columnHeader: 'Cities' });
+ const vCell = this.get({ index: 0, columnHeader: 'Cities' });
await vCell.hover();
// in-cell add
- await expect(await vCell.locator('.nc-action-icon.nc-plus:visible')).toHaveCount(count);
+ await expect(vCell.locator('.nc-action-icon.nc-plus:visible')).toHaveCount(count);
// virtual cell link text
const linkText = await getTextExcludeIconText(vCell);
diff --git a/tests/playwright/pages/Dashboard/common/Footbar/index.ts b/tests/playwright/pages/Dashboard/common/Footbar/index.ts
index 88ce6c2f39..c2de855e3e 100644
--- a/tests/playwright/pages/Dashboard/common/Footbar/index.ts
+++ b/tests/playwright/pages/Dashboard/common/Footbar/index.ts
@@ -42,4 +42,14 @@ export class FootbarPage extends BasePage {
await this.rootPage.locator('.ant-dropdown-content:visible').waitFor();
await this.rootPage.locator('.ant-dropdown-content:visible').locator('.nc-new-record-with-form').click();
}
+
+ async verifyLockMode() {
+ // add record button
+ await expect(this.btn_addNewRow).toBeVisible({ visible: false });
+ }
+
+ async verifyCollaborativeMode() {
+ // add record button
+ await expect(this.btn_addNewRow).toBeVisible({ visible: true });
+ }
}
diff --git a/tests/playwright/pages/Dashboard/common/LeftSidebar/index.ts b/tests/playwright/pages/Dashboard/common/LeftSidebar/index.ts
index 64b4a77c62..07ea7356a5 100644
--- a/tests/playwright/pages/Dashboard/common/LeftSidebar/index.ts
+++ b/tests/playwright/pages/Dashboard/common/LeftSidebar/index.ts
@@ -30,6 +30,16 @@ export class LeftSidebarPage extends BasePage {
return this.dashboard.get().locator('.nc-sidebar');
}
+ async createProject({ title }: { title: string }) {
+ await this.btn_newProject.click();
+ await this.rootPage.locator('.ant-modal-content:has-text(" Create Database")').waitFor();
+ await this.rootPage.locator('.ant-modal-content:has-text(" Create Database")').locator('input').fill(title);
+ await this.rootPage
+ .locator('.ant-modal-content:has-text(" Create Database")')
+ .locator('button.ant-btn-primary')
+ .click();
+ }
+
async clickTeamAndSettings() {
await this.btn_teamAndSettings.click();
}
diff --git a/tests/playwright/pages/Dashboard/common/ProjectMenu/index.ts b/tests/playwright/pages/Dashboard/common/ProjectMenu/index.ts
index c63c9b82f8..0886d12a19 100644
--- a/tests/playwright/pages/Dashboard/common/ProjectMenu/index.ts
+++ b/tests/playwright/pages/Dashboard/common/ProjectMenu/index.ts
@@ -18,7 +18,7 @@ export class ProjectMenuObject extends BasePage {
}
async click({ menu, subMenu }: { menu: string; subMenu: string }) {
- const pMenu = await this.rootPage.locator(`.nc-dropdown-project-menu:visible`);
+ const pMenu = this.rootPage.locator(`.nc-dropdown-project-menu:visible`);
await pMenu.locator(`div.nc-project-menu-item:has-text("${menu}"):visible`).click();
await this.rootPage.waitForTimeout(2000);
diff --git a/tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts b/tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts
index 127528881b..874809af27 100644
--- a/tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts
+++ b/tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts
@@ -17,9 +17,9 @@ export class ToolbarFilterPage extends BasePage {
}
async verify({ index, column, operator, value }: { index: number; column: string; operator: string; value: string }) {
- const fieldLocator = await this.get().locator('.nc-filter-field-select').nth(index);
+ const fieldLocator = this.get().locator('.nc-filter-field-select').nth(index);
const fieldText = await getTextExcludeIconText(fieldLocator);
- await expect(fieldText).toBe(column);
+ expect(fieldText).toBe(column);
await expect(this.get().locator('.nc-filter-operation-select').nth(index)).toHaveText(operator);
await expect
@@ -63,16 +63,16 @@ export class ToolbarFilterPage extends BasePage {
filterLogicalOperator?: string;
}) {
await this.get().locator(`button:has-text("Add Filter Group")`).last().click();
- const filterDropdown = await this.get().locator('.menu-filter-dropdown').nth(filterGroupIndex);
+ const filterDropdown = this.get().locator('.menu-filter-dropdown').nth(filterGroupIndex);
await filterDropdown.waitFor({ state: 'visible' });
await filterDropdown.locator(`button:has-text("Add Filter")`).first().click();
- const selectField = await filterDropdown.locator('.nc-filter-field-select').last();
- const selectOperation = await filterDropdown.locator('.nc-filter-operation-select').last();
- const selectValue = await filterDropdown.locator('.nc-filter-value-select > input').last();
+ const selectField = filterDropdown.locator('.nc-filter-field-select').last();
+ const selectOperation = filterDropdown.locator('.nc-filter-operation-select').last();
+ const selectValue = filterDropdown.locator('.nc-filter-value-select > input').last();
await selectField.waitFor({ state: 'visible' });
await selectField.click();
- const fieldDropdown = await this.rootPage
+ const fieldDropdown = this.rootPage
.locator('div.ant-select-dropdown.nc-dropdown-toolbar-field-list')
.last()
.locator(`div[label="${title}"]:visible`);
@@ -81,7 +81,7 @@ export class ToolbarFilterPage extends BasePage {
await selectOperation.waitFor({ state: 'visible' });
await selectOperation.click();
- const operationDropdown = await this.rootPage
+ const operationDropdown = this.rootPage
.locator('div.ant-select-dropdown.nc-dropdown-filter-comp-op')
.last()
.locator(`.ant-select-item:has-text("${operation}")`);
@@ -93,13 +93,11 @@ export class ToolbarFilterPage extends BasePage {
if (filterGroupIndex) {
if (filterLogicalOperator === 'OR') {
- const logicalButton = await this.rootPage.locator('div.flex.nc-filter-logical-op').nth(filterGroupIndex - 1);
+ const logicalButton = this.rootPage.locator('div.flex.nc-filter-logical-op').nth(filterGroupIndex - 1);
await logicalButton.waitFor({ state: 'visible' });
await logicalButton.click();
- const logicalDropdown = await this.rootPage.locator(
- 'div.ant-select-dropdown.nc-dropdown-filter-logical-op-group'
- );
+ const logicalDropdown = this.rootPage.locator('div.ant-select-dropdown.nc-dropdown-filter-logical-op-group');
await logicalDropdown.waitFor({ state: 'visible' });
await logicalDropdown.locator(`.ant-select-item:has-text("${filterLogicalOperator}")`).click();
}
@@ -133,7 +131,7 @@ export class ToolbarFilterPage extends BasePage {
skipWaitingResponse = true;
const selectedField = await getTextExcludeIconText(
- await this.rootPage.locator('.nc-filter-field-select .ant-select-selection-item')
+ this.rootPage.locator('.nc-filter-field-select .ant-select-selection-item')
);
if (selectedField !== title) {
await this.rootPage.locator('.nc-filter-field-select').last().click();
@@ -158,7 +156,7 @@ export class ToolbarFilterPage extends BasePage {
}
}
- const selectedOpType = await getTextExcludeIconText(await this.rootPage.locator('.nc-filter-operation-select'));
+ const selectedOpType = await getTextExcludeIconText(this.rootPage.locator('.nc-filter-operation-select'));
if (selectedOpType !== operation) {
await this.rootPage.locator('.nc-filter-operation-select').click();
// first() : filter list has >, >=
@@ -174,7 +172,7 @@ export class ToolbarFilterPage extends BasePage {
} else {
await this.waitForResponse({
uiAction: async () =>
- await this.rootPage
+ await this.rootPage
.locator('.nc-dropdown-filter-comp-op')
.locator(`.ant-select-item:has-text("${operation}")`)
.first()
@@ -187,9 +185,7 @@ export class ToolbarFilterPage extends BasePage {
// subtype for date
if (dataType === UITypes.Date && subOperation) {
- const selectedSubType = await getTextExcludeIconText(
- await this.rootPage.locator('.nc-filter-sub_operation-select')
- );
+ const selectedSubType = await getTextExcludeIconText(this.rootPage.locator('.nc-filter-sub_operation-select'));
if (selectedSubType !== subOperation) {
await this.rootPage.locator('.nc-filter-sub_operation-select').click();
// first() : filter list has >, >=
@@ -222,14 +218,14 @@ export class ToolbarFilterPage extends BasePage {
switch (dataType) {
case UITypes.Year:
await this.get().locator('.nc-filter-value-select').click();
- await this.rootPage.locator(`.ant-picker-dropdown:visible`);
+ await this.rootPage.locator(`.ant-picker-dropdown:visible`).waitFor();
await this.rootPage.locator(`.ant-picker-cell-inner:has-text("${value}")`).click();
break;
case UITypes.Time:
// eslint-disable-next-line no-case-declarations
const time = value.split(':');
await this.get().locator('.nc-filter-value-select').click();
- await this.rootPage.locator(`.ant-picker-dropdown:visible`);
+ await this.rootPage.locator(`.ant-picker-dropdown:visible`).waitFor();
await this.rootPage
.locator(`.ant-picker-time-panel-column:nth-child(1)`)
.locator(`.ant-picker-time-panel-cell:has-text("${time[0]}")`)
@@ -243,14 +239,15 @@ export class ToolbarFilterPage extends BasePage {
case UITypes.Date:
if (subOperation === 'exact date') {
await this.get().locator('.nc-filter-value-select').click();
- await this.rootPage.locator(`.ant-picker-dropdown:visible`);
+ await this.rootPage.locator(`.ant-picker-dropdown:visible`).waitFor();
if (skipWaitingResponse) {
await this.rootPage.locator(`.ant-picker-cell-inner:has-text("${value}")`).click();
await this.rootPage.waitForTimeout(350);
} else {
await this.waitForResponse({
- uiAction: async () => await this.rootPage.locator(`.ant-picker-cell-inner:has-text("${value}")`).click(),
+ uiAction: async () =>
+ await this.rootPage.locator(`.ant-picker-cell-inner:has-text("${value}")`).click(),
httpMethodsToMatch: ['GET'],
requestUrlPathToMatch: locallySaved ? `/api/v1/db/public/` : `/api/v1/db/data/noco/`,
});
@@ -274,6 +271,7 @@ export class ToolbarFilterPage extends BasePage {
case UITypes.Duration:
if (skipWaitingResponse) {
await this.get().locator('.nc-filter-value-select').locator('input').fill(value);
+ await this.get().locator('.nc-filter-value-select').locator('input').press('Enter');
await this.rootPage.waitForTimeout(350);
} else {
await this.waitForResponse({
@@ -386,7 +384,7 @@ export class ToolbarFilterPage extends BasePage {
}
await this.rootPage.locator('.nc-filter-operation-select').click();
- const opList = await this.rootPage
+ const opList = this.rootPage
.locator('.nc-dropdown-filter-comp-op')
.locator(`.ant-select-item > .ant-select-item-option-content`);
diff --git a/tests/playwright/pages/Dashboard/common/Toolbar/Groupby.ts b/tests/playwright/pages/Dashboard/common/Toolbar/Groupby.ts
index 21b27ec86d..a65b634849 100644
--- a/tests/playwright/pages/Dashboard/common/Toolbar/Groupby.ts
+++ b/tests/playwright/pages/Dashboard/common/Toolbar/Groupby.ts
@@ -16,7 +16,7 @@ export class ToolbarGroupByPage extends BasePage {
}
async verify({ index, column, direction }: { index: number; column: string; direction: string }) {
- const fieldLocator = await this.get().locator('.nc-sort-field-select').nth(index);
+ const fieldLocator = this.get().locator('.nc-sort-field-select').nth(index);
const fieldText = await getTextExcludeIconText(fieldLocator);
expect(fieldText).toBe(column);
diff --git a/tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts b/tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts
index a4791e78d9..90cfc0d33e 100644
--- a/tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts
+++ b/tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts
@@ -83,13 +83,13 @@ export class ToolbarSortPage extends BasePage {
// Check if create sort modal is open or sort list is open
let isSortListOpen = false;
for (let i = 0; i < 3; i++) {
- const sortList = await this.rootPage.locator('.nc-filter-list');
+ const sortList = this.rootPage.locator('.nc-filter-list');
if (await sortList.isVisible()) {
isSortListOpen = true;
break;
}
- const searchInput = await this.rootPage.locator('.nc-sort-create-modal');
+ const searchInput = this.rootPage.locator('.nc-sort-create-modal');
if (await searchInput.isVisible()) {
isSortListOpen = false;
break;
diff --git a/tests/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts b/tests/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts
index f91c4c4235..09c4abe126 100644
--- a/tests/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts
+++ b/tests/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts
@@ -1,6 +1,5 @@
import { expect, Locator } from '@playwright/test';
import BasePage from '../../../Base';
-import { GridPage } from '../../Grid';
import { ToolbarPage } from './index';
// @ts-ignore
import fs from 'fs';
@@ -44,7 +43,7 @@ export class ToolbarViewMenuPage extends BasePage {
// verify downloaded content against expected content
const expectedData = fs.readFileSync(expectedDataFile, 'utf8').replace(/\r/g, '').split('\n');
const file = fs.readFileSync('./output/test.txt', 'utf8').replace(/\r/g, '').split('\n');
- await expect(file).toEqual(expectedData);
+ expect(file).toEqual(expectedData);
}
async verifyDownloadAsXLSX({
@@ -72,7 +71,7 @@ export class ToolbarViewMenuPage extends BasePage {
const expectedData = fs.readFileSync(expectedDataFile, 'utf8');
const file = fs.readFileSync('./output/test.txt', 'utf8');
// XLSX writes file with UTF-8 BOM, adds '\ufeff' to cater it
- await expect(file).toEqual('\ufeff' + expectedData);
+ expect(file).toEqual('\ufeff' + expectedData);
}
// menu items
@@ -92,16 +91,12 @@ export class ToolbarViewMenuPage extends BasePage {
// for CSV download, pass locator instead of clicking it here
if (subMenu === 'Download as CSV') {
await this.verifyDownloadAsCSV({
- downloadLocator: await this.rootPage
- .locator(`.ant-dropdown-menu-title-content:has-text("${subMenu}")`)
- .last(),
+ downloadLocator: this.rootPage.locator(`.ant-dropdown-menu-title-content:has-text("${subMenu}")`).last(),
expectedDataFile: verificationInfo?.verificationFile ?? './fixtures/expectedBaseDownloadData.txt',
});
} else if (subMenu === 'Download as XLSX') {
await this.verifyDownloadAsXLSX({
- downloadLocator: await this.rootPage
- .locator(`.ant-dropdown-menu-title-content:has-text("${subMenu}")`)
- .last(),
+ downloadLocator: this.rootPage.locator(`.ant-dropdown-menu-title-content:has-text("${subMenu}")`).last(),
expectedDataFile: verificationInfo?.verificationFile ?? './fixtures/expectedBaseDownloadData.txt',
});
} else {
@@ -135,38 +130,4 @@ export class ToolbarViewMenuPage extends BasePage {
}
await this.toolbar.parent.waitLoading();
}
-
- async verifyLockMode() {
- await expect(await this.toolbar.get().locator(`.nc-fields-menu-btn.nc-toolbar-btn`)).toBeDisabled();
- await expect(await this.toolbar.get().locator(`.nc-filter-menu-btn.nc-toolbar-btn`)).toBeDisabled();
- await expect(await this.toolbar.get().locator(`.nc-sort-menu-btn.nc-toolbar-btn`)).toBeDisabled();
- // await expect(
- // await this.toolbar.get().locator(`.nc-add-new-row-btn.nc-toolbar-btn > .material-symbols.disabled`)
- // ).toBeVisible();
- await expect(
- await this.rootPage.locator('.nc-pagination-wrapper').locator('button.ant-btn:has-text(" New Record ")')
- ).not.toBeVisible();
-
- await (this.toolbar.parent as GridPage).verifyEditDisabled({
- columnHeader: 'Country',
- });
- }
-
- async verifyCollaborativeMode() {
- await expect(await this.toolbar.get().locator(`.nc-fields-menu-btn.nc-toolbar-btn`)).toBeEnabled();
- await expect(await this.toolbar.get().locator(`.nc-filter-menu-btn.nc-toolbar-btn`)).toBeEnabled();
- await expect(await this.toolbar.get().locator(`.nc-sort-menu-btn.nc-toolbar-btn`)).toBeEnabled();
-
- // Add button not in toolbar now
- // await expect(
- // await this.toolbar.get().locator(`.nc-add-new-row-btn.nc-toolbar-btn > .material-symbols`)
- // ).toBeVisible();
- await expect(
- await this.rootPage.locator('.nc-pagination-wrapper').locator('button.ant-btn:has-text(" New Record ")')
- ).toBeVisible();
-
- await (this.toolbar.parent as GridPage).verifyEditEnabled({
- columnHeader: 'Country',
- });
- }
}
diff --git a/tests/playwright/pages/Dashboard/common/Toolbar/index.ts b/tests/playwright/pages/Dashboard/common/Toolbar/index.ts
index 89f4f02765..591ab13838 100644
--- a/tests/playwright/pages/Dashboard/common/Toolbar/index.ts
+++ b/tests/playwright/pages/Dashboard/common/Toolbar/index.ts
@@ -35,6 +35,7 @@ export class ToolbarPage extends BasePage {
readonly btn_sort: Locator;
readonly btn_filter: Locator;
readonly btn_rowHeight: Locator;
+ readonly btn_groupBy: Locator;
constructor(parent: GridPage | GalleryPage | FormPage | KanbanPage | MapPage) {
super(parent.rootPage);
@@ -54,6 +55,7 @@ export class ToolbarPage extends BasePage {
this.btn_sort = this.get().locator(`button.nc-sort-menu-btn`);
this.btn_filter = this.get().locator(`button.nc-filter-menu-btn`);
this.btn_rowHeight = this.get().locator(`button.nc-height-menu-btn`);
+ this.btn_groupBy = this.get().locator(`button.nc-group-by-menu-btn`);
}
get() {
@@ -96,9 +98,9 @@ export class ToolbarPage extends BasePage {
await expect(this.get().locator(`button.nc-fields-menu-btn`)).toBeVisible();
// menu text
- const fieldLocator = await this.get().locator(`button.nc-fields-menu-btn`);
+ const fieldLocator = this.get().locator(`button.nc-fields-menu-btn`);
const fieldText = await getTextExcludeIconText(fieldLocator);
- await expect(fieldText).toBe('Fields');
+ expect(fieldText).toBe('Fields');
// icons count within fields menu button
expect(await this.get().locator(`button.nc-fields-menu-btn`).locator(`.material-symbols`).count()).toBe(2);
@@ -108,9 +110,9 @@ export class ToolbarPage extends BasePage {
await expect(this.get().locator(`button.nc-fields-menu-btn`)).toBeVisible();
// menu text
- const fieldLocator = await this.get().locator(`button.nc-fields-menu-btn`);
+ const fieldLocator = this.get().locator(`button.nc-fields-menu-btn`);
const fieldText = await getTextExcludeIconText(fieldLocator);
- await expect(fieldText).not.toBe('Fields');
+ expect(fieldText).not.toBe('Fields');
// icons count within fields menu button
expect(await this.get().locator(`button.nc-fields-menu-btn`).locator(`.material-symbols`).count()).toBe(2);
@@ -177,7 +179,7 @@ export class ToolbarPage extends BasePage {
// verify downloaded content against expected content
const expectedData = fs.readFileSync(`./fixtures/${verificationFile}`, 'utf8').replace(/\r/g, '').split('\n');
const file = fs.readFileSync('./output/at.txt', 'utf8').replace(/\r/g, '').split('\n');
- await expect(file).toEqual(expectedData);
+ expect(file).toEqual(expectedData);
}
async clickRowHeight() {
@@ -188,7 +190,7 @@ export class ToolbarPage extends BasePage {
async verifyStackByButton({ title }: { title: string }) {
await this.get().locator(`.nc-toolbar-btn.nc-kanban-stacked-by-menu-btn`).waitFor({ state: 'visible' });
await expect(
- await this.get().locator(`.nc-toolbar-btn.nc-kanban-stacked-by-menu-btn:has-text("${title}")`)
+ this.get().locator(`.nc-toolbar-btn.nc-kanban-stacked-by-menu-btn:has-text("${title}")`)
).toBeVisible();
}
@@ -207,7 +209,7 @@ export class ToolbarPage extends BasePage {
commenter: ['Download as CSV', 'Download as XLSX'],
viewer: ['Download as CSV', 'Download as XLSX'],
};
- const vMenu = await this.rootPage.locator('.nc-dropdown-actions-menu:visible');
+ const vMenu = this.rootPage.locator('.nc-dropdown-actions-menu:visible');
for (const item of menuItems[param.role.toLowerCase()]) {
await expect(vMenu).toContainText(item);
}
@@ -228,4 +230,20 @@ export class ToolbarPage extends BasePage {
expect(await this.btn_sort.count()).toBe(1);
expect(await this.btn_rowHeight.count()).toBe(1);
}
+
+ async verifyLockMode() {
+ await expect(this.btn_fields).toBeDisabled();
+ await expect(this.btn_filter).toBeDisabled();
+ await expect(this.btn_sort).toBeDisabled();
+ await expect(this.btn_groupBy).toBeDisabled();
+ await expect(this.btn_rowHeight).toBeDisabled();
+ }
+
+ async verifyCollaborativeMode() {
+ await expect(this.btn_fields).toBeEnabled();
+ await expect(this.btn_filter).toBeEnabled();
+ await expect(this.btn_sort).toBeEnabled();
+ await expect(this.btn_groupBy).toBeEnabled();
+ await expect(this.btn_rowHeight).toBeEnabled();
+ }
}
diff --git a/tests/playwright/pages/Dashboard/common/Topbar/Share.ts b/tests/playwright/pages/Dashboard/common/Topbar/Share.ts
index a9afc5b5ff..edf354c006 100644
--- a/tests/playwright/pages/Dashboard/common/Topbar/Share.ts
+++ b/tests/playwright/pages/Dashboard/common/Topbar/Share.ts
@@ -54,10 +54,26 @@ export class TopbarSharePage extends BasePage {
await this.get().locator(`[data-testid="nc-share-base-sub-modal"]`).locator('.ant-switch').nth(0).click();
}
+ async isSharedBasePublicAccessEnabled() {
+ return await this.get()
+ .locator(`[data-testid="nc-share-base-sub-modal"]`)
+ .locator('.ant-switch')
+ .nth(0)
+ .isChecked();
+ }
+
async clickShareBaseEditorAccess() {
await this.get().locator(`[data-testid="nc-share-base-sub-modal"]`).locator('.ant-switch').nth(1).click();
}
+ async isSharedBaseEditorAccessEnabled() {
+ return await this.get()
+ .locator(`[data-testid="nc-share-base-sub-modal"]`)
+ .locator('.ant-switch')
+ .nth(1)
+ .isChecked();
+ }
+
async clickShareViewSurveyMode() {
await this.get().locator(`[data-testid="nc-modal-share-view__surveyMode"]`).click();
}
diff --git a/tests/playwright/pages/Dashboard/common/Topbar/index.ts b/tests/playwright/pages/Dashboard/common/Topbar/index.ts
index 60bfc9bfc1..6f2fa9c21c 100644
--- a/tests/playwright/pages/Dashboard/common/Topbar/index.ts
+++ b/tests/playwright/pages/Dashboard/common/Topbar/index.ts
@@ -56,9 +56,10 @@ export class TopbarPage extends BasePage {
async getSharedBaseUrl({ role }: { role: string }) {
await this.clickShare();
- await this.share.clickShareBase();
- await this.share.clickShareBasePublicAccess();
- if (role === 'editor') {
+ if (!(await this.share.isSharedBasePublicAccessEnabled())) await this.share.clickShareBasePublicAccess();
+ if (role === 'editor' && !(await this.share.isSharedBaseEditorAccessEnabled())) {
+ await this.share.clickShareBaseEditorAccess();
+ } else if (role === 'viewer' && (await this.share.isSharedBaseEditorAccessEnabled())) {
await this.share.clickShareBaseEditorAccess();
}
await this.share.clickCopyLink();
diff --git a/tests/playwright/pages/Dashboard/commonBase/Erd.ts b/tests/playwright/pages/Dashboard/commonBase/Erd.ts
index 503a544475..239c7a594a 100644
--- a/tests/playwright/pages/Dashboard/commonBase/Erd.ts
+++ b/tests/playwright/pages/Dashboard/commonBase/Erd.ts
@@ -8,36 +8,36 @@ export abstract class ErdBasePage extends BasePage {
async clickShowColumnNames() {
await this.get().locator(`.nc-erd-showColumns-checkbox`).click();
- (await this.vueFlow().elementHandle())?.waitForElementState('stable');
+ await (await this.vueFlow().elementHandle())?.waitForElementState('stable');
}
async dbClickShowColumnNames() {
await this.get().locator(`.nc-erd-showColumns-label`).dblclick();
- (await this.vueFlow().elementHandle())?.waitForElementState('stable');
+ await (await this.vueFlow().elementHandle())?.waitForElementState('stable');
}
async clickShowPkAndFk() {
await this.get().locator(`.nc-erd-showPkAndFk-checkbox`).click();
- (await this.vueFlow().elementHandle())?.waitForElementState('stable');
+ await (await this.vueFlow().elementHandle())?.waitForElementState('stable');
}
async clickShowSqlViews() {
await this.get().locator(`.nc-erd-showViews-checkbox`).click();
- (await this.vueFlow().elementHandle())?.waitForElementState('stable');
+ await (await this.vueFlow().elementHandle())?.waitForElementState('stable');
}
async clickShowMMTables() {
await this.get().locator(`.nc-erd-showMMTables-checkbox`).click();
- (await this.vueFlow().elementHandle())?.waitForElementState('stable');
+ await (await this.vueFlow().elementHandle())?.waitForElementState('stable');
}
async clickShowJunctionTableNames() {
await this.get().locator(`.nc-erd-showJunctionTableNames-checkbox`).click();
- (await this.vueFlow().elementHandle())?.waitForElementState('stable');
+ await (await this.vueFlow().elementHandle())?.waitForElementState('stable');
}
async verifyEasterEggNotShown() {
- await expect(await this.get().locator('.nc-erd-showMMTables-checkbox')).not.toBeVisible();
+ await expect(this.get().locator('.nc-erd-showMMTables-checkbox')).not.toBeVisible();
}
async verifyNode({
diff --git a/tests/playwright/pages/Dashboard/index.ts b/tests/playwright/pages/Dashboard/index.ts
index 00bd7a817f..073f6805c0 100644
--- a/tests/playwright/pages/Dashboard/index.ts
+++ b/tests/playwright/pages/Dashboard/index.ts
@@ -98,14 +98,14 @@ export class DashboardPage extends BasePage {
async verifyTeamAndSettingsLinkIsVisible() {
await this.projectMenuLink.click();
- const teamAndSettingsLink = await this.getProjectMenuLink({ title: ' Team & Settings' });
+ const teamAndSettingsLink = this.getProjectMenuLink({ title: ' Team & Settings' });
await expect(teamAndSettingsLink).toBeVisible();
await this.projectMenuLink.click();
}
async verifyTeamAndSettingsLinkIsNotVisible() {
await this.projectMenuLink.click();
- const teamAndSettingsLink = await this.getProjectMenuLink({ title: ' Team & Settings' });
+ const teamAndSettingsLink = this.getProjectMenuLink({ title: ' Team & Settings' });
await expect(teamAndSettingsLink).not.toBeVisible();
await this.projectMenuLink.click();
}
@@ -243,7 +243,7 @@ export class DashboardPage extends BasePage {
}*/
async closeAllTabs() {
- const tab = await this.tabBar.locator(`.ant-tabs-tab`);
+ const tab = this.tabBar.locator(`.ant-tabs-tab`);
const tabCount = await tab.count();
for (let i = 0; i < tabCount; i++) {
diff --git a/tests/playwright/pages/LoginPage/index.ts b/tests/playwright/pages/LoginPage/index.ts
index 15f4f96608..454b8887a3 100644
--- a/tests/playwright/pages/LoginPage/index.ts
+++ b/tests/playwright/pages/LoginPage/index.ts
@@ -1,4 +1,4 @@
-import { expect, Page } from '@playwright/test';
+import { Page } from '@playwright/test';
import BasePage from '../Base';
export class LoginPage extends BasePage {
diff --git a/tests/playwright/pages/ProjectsPage/index.ts b/tests/playwright/pages/ProjectsPage/index.ts
index cba61315b5..645237a7bb 100644
--- a/tests/playwright/pages/ProjectsPage/index.ts
+++ b/tests/playwright/pages/ProjectsPage/index.ts
@@ -70,14 +70,14 @@ export class ProjectsPage extends BasePage {
await this.rootPage.getByTestId('dupe-project-' + name).click();
// Find the checkbox element with the label "Include data"
- const includeDataCheckbox = await this.rootPage.getByText('Include data', { exact: true });
+ const includeDataCheckbox = this.rootPage.getByText('Include data', { exact: true });
// Check the checkbox if it is not already checked
if ((await includeDataCheckbox.isChecked()) && !includeData) {
await includeDataCheckbox.click(); // click the checkbox to check it
}
// Find the checkbox element with the label "Include data"
- const includeViewsCheckbox = await this.rootPage.getByText('Include views', { exact: true });
+ const includeViewsCheckbox = this.rootPage.getByText('Include views', { exact: true });
// Check the checkbox if it is not already checked
if ((await includeViewsCheckbox.isChecked()) && !includeViews) {
await includeViewsCheckbox.click(); // click the checkbox to check it
@@ -112,7 +112,7 @@ export class ProjectsPage extends BasePage {
await this.get().waitFor({
state: 'visible',
});
- (await this.get().elementHandle())?.waitForElementState('stable');
+ await (await this.get().elementHandle())?.waitForElementState('stable');
// Wait till the ant table is rendered
await this.get().locator('thead.ant-table-thead >> th').nth(0).waitFor({ state: 'visible' });
@@ -135,7 +135,7 @@ export class ProjectsPage extends BasePage {
if (!withoutPrefix) title = this.prefixTitle(title);
let project: any;
-
+
const responsePromise = this.rootPage.waitForResponse(async res => {
let json: any = {};
try {
@@ -154,7 +154,7 @@ export class ProjectsPage extends BasePage {
}
return isRequiredResponse;
- })
+ });
await this.get()
.locator(`.ant-table-cell`, {
@@ -162,7 +162,7 @@ export class ProjectsPage extends BasePage {
})
.click();
- await responsePromise
+ await responsePromise;
const dashboard = new DashboardPage(this.rootPage, project);
@@ -199,7 +199,7 @@ export class ProjectsPage extends BasePage {
if (!withoutPrefix) newTitle = this.prefixTitle(newTitle);
const project = this.rootPage;
- const projRow = await project.locator(`tr`, {
+ const projRow = project.locator(`tr`, {
has: project.locator(`td.ant-table-cell:has-text("${title}")`),
});
await projRow.locator('.nc-action-btn').nth(0).click();
@@ -222,7 +222,7 @@ export class ProjectsPage extends BasePage {
}
async selectLanguage({ index }: { index: number }) {
- const modal = await this.rootPage.locator('.nc-dropdown-menu-translate');
+ const modal = this.rootPage.locator('.nc-dropdown-menu-translate');
await modal.locator(`.ant-dropdown-menu-item`).nth(index).click();
}
@@ -249,23 +249,23 @@ export class ProjectsPage extends BasePage {
async validateRoleAccess(param: { role: string }) {
// new user; by default org level permission is to viewer (can't create project)
- await expect(await this.buttonNewProject).toBeVisible({ visible: false });
+ await expect(this.buttonNewProject).toBeVisible({ visible: false });
// role specific permissions
switch (param.role) {
case 'creator':
- await expect(await this.buttonColorSelector).toBeVisible();
- await expect(await this.buttonEditProject).toBeVisible();
- await expect(await this.buttonDeleteProject).toBeVisible();
- await expect(await this.buttonMoreActions).toBeVisible();
+ await expect(this.buttonColorSelector).toBeVisible();
+ await expect(this.buttonEditProject).toBeVisible();
+ await expect(this.buttonDeleteProject).toBeVisible();
+ await expect(this.buttonMoreActions).toBeVisible();
break;
case 'editor':
case 'commenter':
case 'viewer':
- await expect(await this.buttonColorSelector).toBeVisible({ visible: false });
- await expect(await this.buttonEditProject).toBeVisible({ visible: false });
- await expect(await this.buttonDeleteProject).toBeVisible({ visible: false });
- await expect(await this.buttonMoreActions).toBeVisible({ visible: false });
+ await expect(this.buttonColorSelector).toBeVisible({ visible: false });
+ await expect(this.buttonEditProject).toBeVisible({ visible: false });
+ await expect(this.buttonDeleteProject).toBeVisible({ visible: false });
+ await expect(this.buttonMoreActions).toBeVisible({ visible: false });
break;
}
}
diff --git a/tests/playwright/pages/SharedForm/index.ts b/tests/playwright/pages/SharedForm/index.ts
index bdea30bfa1..02a84684eb 100644
--- a/tests/playwright/pages/SharedForm/index.ts
+++ b/tests/playwright/pages/SharedForm/index.ts
@@ -24,7 +24,7 @@ export class SharedFormPage extends BasePage {
async verifySuccessMessage() {
await expect(
- await this.get().locator('.ant-alert-success', {
+ this.get().locator('.ant-alert-success', {
hasText: 'Successfully submitted form data',
})
).toBeVisible();
@@ -36,7 +36,7 @@ export class SharedFormPage extends BasePage {
async verifyChildList(cardTitle?: string[]) {
await this.get().locator('.nc-modal-link-record').waitFor();
- const linkRecord = await this.get();
+ const linkRecord = this.get();
// DOM element validation
// title: Link Record
@@ -45,18 +45,18 @@ export class SharedFormPage extends BasePage {
await expect(this.get().locator(`.ant-modal-title`)).toHaveText(`Link record`);
// add new record option is not available for shared form
- await expect(await linkRecord.locator(`button:has-text("Add new record")`).isVisible()).toBeFalsy();
+ expect(await linkRecord.locator(`button:has-text("Add new record")`).isVisible()).toBeFalsy();
- await expect(await linkRecord.locator(`.nc-reload`).isVisible()).toBeTruthy();
+ expect(await linkRecord.locator(`.nc-reload`).isVisible()).toBeTruthy();
// placeholder: Filter query
- await expect(await linkRecord.locator(`[placeholder="Filter query"]`).isVisible()).toBeTruthy();
+ expect(await linkRecord.locator(`[placeholder="Filter query"]`).isVisible()).toBeTruthy();
{
const childList = linkRecord.locator(`.ant-card`);
const childCards = await childList.count();
- await expect(childCards).toEqual(cardTitle.length);
+ expect(childCards).toEqual(cardTitle.length);
for (let i = 0; i < cardTitle.length; i++) {
- await expect(await childList.nth(i).textContent()).toContain(cardTitle[i]);
+ expect(await childList.nth(i).textContent()).toContain(cardTitle[i]);
}
}
}
diff --git a/tests/playwright/pages/WorkspacePage/HeaderPage.ts b/tests/playwright/pages/WorkspacePage/HeaderPage.ts
index 5e59641765..e0fdd26ea5 100644
--- a/tests/playwright/pages/WorkspacePage/HeaderPage.ts
+++ b/tests/playwright/pages/WorkspacePage/HeaderPage.ts
@@ -55,7 +55,7 @@ export class HeaderPage extends BasePage {
// Menu : Workspaces, Explore, Help, Community
async openMenu(param: { title: string }) {
- await this.get().locator(`[data-testid="nc-dash-nav-${param.title.toLowerCase()}"]`);
+ this.get().locator(`[data-testid="nc-dash-nav-${param.title.toLowerCase()}"]`);
}
async navigateUsingCmdK({
diff --git a/tests/playwright/pages/WorkspacePage/LeftSideBarPage.ts b/tests/playwright/pages/WorkspacePage/LeftSideBarPage.ts
index 1cdb5255d9..47d502a3f8 100644
--- a/tests/playwright/pages/WorkspacePage/LeftSideBarPage.ts
+++ b/tests/playwright/pages/WorkspacePage/LeftSideBarPage.ts
@@ -61,7 +61,7 @@ export class LeftSideBarPage extends BasePage {
}
async getWorkspaceCount() {
- return (await this.workspaceItems).count();
+ return this.workspaceItems.count();
}
async verifyStaticElements() {
@@ -88,7 +88,7 @@ export class LeftSideBarPage extends BasePage {
async workspaceGetLocator(title: string) {
// get workspace id
// return this.get().locator('[data-id="' + wsId + '"]');
- const list = await this.get().locator(`.nc-workspace-list-item`);
+ const list = this.get().locator(`.nc-workspace-list-item`);
for (let i = 0; i < (await list.count()); i++) {
const ws = list.nth(i);
const wsTitle = (await ws.innerText()).split('\n')[1];
@@ -100,7 +100,7 @@ export class LeftSideBarPage extends BasePage {
}
async workspaceList() {
- const wsList = await this.workspaceItems;
+ const wsList = this.workspaceItems;
// for each, extract title and add to array
const titles = [];
for (let i = 0; i < (await wsList.count()); i++) {
@@ -155,11 +155,12 @@ export class LeftSideBarPage extends BasePage {
// GET will be triggered after DELETE
await this.waitForResponse({
- uiAction: () => {
+ uiAction: async () => {
// Create a promise that resolves after 1 second
- const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
+ const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
// Returning a promise that resolves with the result after the 1-second delay
- return delay(500).then(() => this.rootPage.locator('button:has-text("Delete Workspace")').click());
+ await delay(500);
+ return await this.rootPage.locator('button:has-text("Delete Workspace")').click();
},
// uiAction: () => this.rootPage.locator('button:has-text("Delete Workspace")').click(),
httpMethodsToMatch: ['GET'],
diff --git a/tests/playwright/quickTests/commonTest.ts b/tests/playwright/quickTests/commonTest.ts
index c1d49b736a..aa42792797 100644
--- a/tests/playwright/quickTests/commonTest.ts
+++ b/tests/playwright/quickTests/commonTest.ts
@@ -1,8 +1,6 @@
import { DashboardPage } from '../pages/Dashboard';
-import { ProjectsPage } from '../pages/ProjectsPage';
import { NcContext } from '../setup';
import { isMysql, isPg } from '../setup/db';
-import { WorkspacePage } from '../pages/WorkspacePage';
// normal fields
const recordCells = {
diff --git a/tests/playwright/setup/db.ts b/tests/playwright/setup/db.ts
index 885b9b6b81..633d73b102 100644
--- a/tests/playwright/setup/db.ts
+++ b/tests/playwright/setup/db.ts
@@ -10,16 +10,13 @@ const isSqlite = (context: NcContext) => context.dbType === 'sqlite';
const isPg = (context: NcContext) => context.dbType === 'pg';
-// hardwired for hub; this has to be configured to false in nocodb
-// consider reading this from environment variable
-const isHub = () => true;
const isEE = () => process.env.EE === 'true';
const pg_credentials = (context: NcContext) => ({
user: 'postgres',
host: 'localhost',
// todo: Hack to resolve issue with pg resetting
- database: `sakila_${context.workerId}`,
+ database: `sakila${context.workerId}`,
password: 'password',
port: 5432,
});
@@ -68,4 +65,4 @@ async function sqliteExec(query) {
}
}
-export { sqliteExec, mysqlExec, isMysql, isSqlite, isPg, pgExec, isHub, isEE };
+export { sqliteExec, mysqlExec, isMysql, isSqlite, isPg, pgExec, isEE };
diff --git a/tests/playwright/tests/db/columns/columnAttachments.spec.ts b/tests/playwright/tests/db/columns/columnAttachments.spec.ts
index 61da97df7c..e8452f9aa5 100644
--- a/tests/playwright/tests/db/columns/columnAttachments.spec.ts
+++ b/tests/playwright/tests/db/columns/columnAttachments.spec.ts
@@ -88,11 +88,11 @@ test.describe('Attachment column', () => {
const rows = csvArray.slice(1);
const cells = rows[10].split(',');
- await expect(columns).toBe('Country,Cities,testAttach');
- await expect(cells[0]).toBe('Bahrain');
+ expect(columns).toBe('Country,Cities,testAttach');
+ expect(cells[0]).toBe('Bahrain');
// PR8504
// await expect(cells[1]).toBe('al-Manama');
- await expect(cells[1]).toBe('1');
- await expect(cells[2].includes('5.json(http://localhost:8080/download/')).toBe(true);
+ expect(cells[1]).toBe('1');
+ expect(cells[2].includes('5.json(http://localhost:8080/download/')).toBe(true);
});
});
diff --git a/tests/playwright/tests/db/columns/columnCheckbox.spec.ts b/tests/playwright/tests/db/columns/columnCheckbox.spec.ts
index c07df5f117..fc6668ceb4 100644
--- a/tests/playwright/tests/db/columns/columnCheckbox.spec.ts
+++ b/tests/playwright/tests/db/columns/columnCheckbox.spec.ts
@@ -4,7 +4,6 @@ import setup, { unsetup } from '../../../setup';
import { ToolbarPage } from '../../../pages/Dashboard/common/Toolbar';
import { UITypes } from 'nocodb-sdk';
import { Api } from 'nocodb-sdk';
-import { isPg } from '../../../setup/db';
let api: Api;
test.describe('Checkbox - cell, filter, sort', () => {
diff --git a/tests/playwright/tests/db/columns/columnLinkToAnotherRecord.spec.ts b/tests/playwright/tests/db/columns/columnLinkToAnotherRecord.spec.ts
index 5a7a1bd297..5225ff88ba 100644
--- a/tests/playwright/tests/db/columns/columnLinkToAnotherRecord.spec.ts
+++ b/tests/playwright/tests/db/columns/columnLinkToAnotherRecord.spec.ts
@@ -19,9 +19,6 @@ test.describe('LTAR create & update', () => {
});
test('LTAR', async () => {
- // close 'Team & Auth' tab
- await dashboard.closeTab({ title: 'Team & Auth' });
-
await dashboard.treeView.createTable({ title: 'Sheet1', projectTitle: context.project.title });
// subsequent table creation fails; hence delay
await dashboard.rootPage.waitForTimeout(1000);
@@ -189,6 +186,20 @@ test.describe('LTAR create & update', () => {
await dashboard.treeView.deleteTable({ title: 'Sheet1' });
await dashboard.treeView.deleteTable({ title: 'Sheet2' });
});
+});
+
+test.describe('Links after edit record', () => {
+ let dashboard: DashboardPage;
+ let context: any;
+
+ test.beforeEach(async ({ page }) => {
+ context = await setup({ page, isEmptyProject: false });
+ dashboard = new DashboardPage(page, context.project);
+ });
+
+ test.afterEach(async () => {
+ await unsetup(context);
+ });
async function verifyRow(param: {
index: number;
@@ -214,9 +225,10 @@ test.describe('LTAR create & update', () => {
await dashboard.grid.cell.verifyVirtualCell({
index: param.index,
columnHeader: 'Cities',
- count: param.value['Cities'].length,
- value: param.value['Cities'],
+ count: param.value.Cities.length,
+ options: { singular: 'City', plural: 'Cities' },
});
+
if (param.value.SLT) {
await dashboard.grid.cell.verify({
index: param.index,
@@ -235,10 +247,7 @@ test.describe('LTAR create & update', () => {
* https://github.com/nocodb/nocodb/issues/4220
*
*/
- test.skip('Existing LTAR table verification', async () => {
- // close 'Team & Auth' tab
- await dashboard.closeTab({ title: 'Team & Auth' });
-
+ test('Existing LTAR table verification', async () => {
// open table
await dashboard.treeView.openTable({ title: 'Country' });
await verifyRow({
diff --git a/tests/playwright/tests/db/columns/columnLtarDragdrop.spec.ts b/tests/playwright/tests/db/columns/columnLtarDragdrop.spec.ts
index 1cdf88c044..5d06918e1c 100644
--- a/tests/playwright/tests/db/columns/columnLtarDragdrop.spec.ts
+++ b/tests/playwright/tests/db/columns/columnLtarDragdrop.spec.ts
@@ -70,18 +70,16 @@ test.describe('Links', () => {
test('drag drop for Link, lookup creation', async () => {
await dashboard.treeView.openTable({ title: 'Table0' });
- const src = await dashboard.rootPage.locator(`[data-testid="tree-view-table-draggable-handle-Table1"]`);
- const dst = await dashboard.rootPage.locator(`[data-testid="grid-row-0"]`);
+ const src = dashboard.rootPage.locator(`[data-testid="tree-view-table-draggable-handle-Table1"]`);
+ const dst = dashboard.rootPage.locator(`[data-testid="grid-row-0"]`);
// drag drop for LTAR column creation
//
await src.dragTo(dst);
- const columnAddModal = await dashboard.rootPage.locator(`.nc-dropdown-grid-add-column`);
+ const columnAddModal = dashboard.rootPage.locator(`.nc-dropdown-grid-add-column`);
{
- const columnType = await getTextExcludeIconText(await columnAddModal.locator(`.nc-column-type-input`));
- const linkTable = await getTextExcludeIconText(
- await columnAddModal.locator(`.ant-form-item-control-input`).nth(3)
- );
+ const columnType = await getTextExcludeIconText(columnAddModal.locator(`.nc-column-type-input`));
+ const linkTable = await getTextExcludeIconText(columnAddModal.locator(`.ant-form-item-control-input`).nth(3));
expect(columnType).toContain('Links');
expect(linkTable).toContain('Table1');
@@ -97,13 +95,9 @@ test.describe('Links', () => {
await src.dragTo(dst);
{
// const columnAddModal = await dashboard.rootPage.locator(`.nc-dropdown-grid-add-column`);
- const columnType = await getTextExcludeIconText(await columnAddModal.locator(`.nc-column-type-input`));
- const linkField = await getTextExcludeIconText(
- await columnAddModal.locator(`.ant-form-item-control-input`).nth(2)
- );
- const childColumn = await getTextExcludeIconText(
- await columnAddModal.locator(`.ant-form-item-control-input`).nth(3)
- );
+ const columnType = await getTextExcludeIconText(columnAddModal.locator(`.nc-column-type-input`));
+ const linkField = await getTextExcludeIconText(columnAddModal.locator(`.ant-form-item-control-input`).nth(2));
+ const childColumn = await getTextExcludeIconText(columnAddModal.locator(`.ant-form-item-control-input`).nth(3));
// validate
expect(columnType).toContain('Lookup');
diff --git a/tests/playwright/tests/db/features/baseShare.spec.ts b/tests/playwright/tests/db/features/baseShare.spec.ts
index 101f0f90b4..ce175c70e2 100644
--- a/tests/playwright/tests/db/features/baseShare.spec.ts
+++ b/tests/playwright/tests/db/features/baseShare.spec.ts
@@ -3,25 +3,24 @@ import { DashboardPage } from '../../../pages/Dashboard';
import setup, { unsetup } from '../../../setup';
import { ToolbarPage } from '../../../pages/Dashboard/common/Toolbar';
import { LoginPage } from '../../../pages/LoginPage';
-import { ProjectsPage } from '../../../pages/ProjectsPage';
import { getDefaultPwd } from '../../../tests/utils/general';
// To be enabled after shared base is implemented
-test.describe.skip('Shared base', () => {
+test.describe('Shared base', () => {
let dashboard: DashboardPage;
let toolbar: ToolbarPage;
let context: any;
let loginPage: LoginPage;
- let projectPage: ProjectsPage;
async function roleTest(role: string) {
// todo: Wait till the page is loaded
await dashboard.rootPage.waitForTimeout(2000);
- await dashboard.validateProjectMenu({
- role: role.toLowerCase(),
- mode: 'shareBase',
- });
+ // fix me! this is currently disabled
+ // await dashboard.validateProjectMenu({
+ // role: role.toLowerCase(),
+ // mode: 'shareBase',
+ // });
await dashboard.treeView.openTable({ title: 'Country', mode: 'shareBase' });
@@ -51,7 +50,6 @@ test.describe.skip('Shared base', () => {
test.beforeEach(async ({ page }) => {
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
- projectPage = new ProjectsPage(page);
toolbar = dashboard.grid.toolbar;
loginPage = new LoginPage(page);
});
@@ -67,7 +65,7 @@ test.describe.skip('Shared base', () => {
let url = '';
// share button visible only if a table is opened
await dashboard.treeView.openTable({ title: 'Country' });
- url = await dashboard.grid.toolbar.getSharedBaseUrl({ role: 'editor' });
+ url = await dashboard.grid.topbar.getSharedBaseUrl({ role: 'editor' });
await dashboard.rootPage.waitForTimeout(2000);
// access shared base link
@@ -84,16 +82,9 @@ test.describe.skip('Shared base', () => {
withoutPrefix: true,
});
- await projectPage.openProject({ title: context.project.title, withoutPrefix: true });
- await dashboard.closeTab({ title: 'Team & Auth' });
-
- await dashboard.gotoSettings();
- await dashboard.settings.teams.clickInviteTeamBtn();
- await dashboard.settings.teams.toggleSharedBase({ toggle: true });
- await dashboard.settings.teams.sharedBaseRole({ role: 'viewer' });
- url = await dashboard.settings.teams.getSharedBaseUrl();
- await dashboard.settings.teams.closeInvite();
- await dashboard.settings.close();
+ // await dashboard.treeView.openProject({ title: context.project.title });
+ await dashboard.treeView.openTable({ title: 'Country' });
+ url = await dashboard.grid.topbar.getSharedBaseUrl({ role: 'viewer' });
await dashboard.rootPage.waitForTimeout(2000);
// access shared base link
diff --git a/tests/playwright/tests/db/features/erd.spec.ts b/tests/playwright/tests/db/features/erd.spec.ts
index 9516301dfd..9760d42d12 100644
--- a/tests/playwright/tests/db/features/erd.spec.ts
+++ b/tests/playwright/tests/db/features/erd.spec.ts
@@ -7,14 +7,11 @@ import {
sqliteSakilaSqlViews,
} from '../../../tests/utils/sakila';
import { DashboardPage } from '../../../pages/Dashboard';
-import { SettingsSubTab, SettingTab } from '../../../pages/Dashboard/Settings';
import setup, { unsetup } from '../../../setup';
import { isMysql, isPg, isSqlite } from '../../../setup/db';
-import { SettingsErdPage } from '../../../pages/Dashboard/Settings/Erd';
-import { defaultBaseName } from '../../../constants';
// Global ERD to be enabled after project-menu landing page is implemented
-test.describe.skip('Erd', () => {
+test.describe('Erd', () => {
let dashboard: DashboardPage;
let context: any;
let sakilaTables, sakilaSqlViews;
@@ -42,16 +39,9 @@ test.describe.skip('Erd', () => {
});
const toggleMM = async () => {
- await dashboard.treeView.projectSettings({});
+ await dashboard.treeView.projectSettings({ title: context.project.title });
await dashboard.settings.miscellaneous.clickShowM2MTables();
await dashboard.settings.close();
-
- // await dashboard.settings.selectTab({ tab: SettingTab.ProjectSettings, subTab: SettingsSubTab.Miscellaneous });
- // await dashboard.settings.miscellaneous.clickShowM2MTables();
- // await dashboard.settings.selectTab({ tab: SettingTab.DataSources });
- // await dashboard.settings.dataSources.openErd({
- // dataSourceName: defaultBaseName,
- // });
};
const openProjectErd = async () => {
@@ -69,9 +59,7 @@ test.describe.skip('Erd', () => {
await toggleMM();
await openProjectErd();
- const erd: SettingsErdPage = dashboard.settings.dataSources.erd;
-
- await erd.dbClickShowColumnNames();
+ const erd = dashboard.details.relations;
if (isPg(context)) {
await erd.verifyNodesCount(sakilaTables.length);
@@ -105,22 +93,23 @@ test.describe.skip('Erd', () => {
});
// Disable show column names and pk/fk
- // todo: rerender edges, otherwise some edges wont be rendered
- await erd.clickShowColumnNames();
- await erd.clickShowJunctionTableNames();
- await erd.clickShowJunctionTableNames();
- await erd.verifyColumns({
- tableName: `actor`,
- columns: actorLTARColumns,
- });
- await erd.verifyColumns({
- tableName: `payment`,
- columns: paymentLTARColumns,
- });
+ // Fix me! Disabled currently
+ // await erd.clickShowColumnNames();
+ // await erd.clickShowJunctionTableNames();
+ // await erd.clickShowJunctionTableNames();
+ //
+ // await erd.verifyColumns({
+ // tableName: `actor`,
+ // columns: actorLTARColumns,
+ // });
+ // await erd.verifyColumns({
+ // tableName: `payment`,
+ // columns: paymentLTARColumns,
+ // });
// Enable show column names and disable pk/fk
- await erd.clickShowColumnNames();
+ // await erd.clickShowColumnNames();
await erd.clickShowPkAndFk();
await erd.verifyColumns({
@@ -165,38 +154,39 @@ test.describe.skip('Erd', () => {
await erd.verifyNodeDoesNotExist({ tableName: `film_actor` });
+ // Fix me! Easter egg menu is disabled currently
// // Verify MM tables
- await erd.clickShowMMTables();
- await erd.clickShowJunctionTableNames();
- await erd.clickShowJunctionTableNames();
-
- await erd.verifyNodesCount(isPg(context) ? 21 : 16);
- await erd.verifyEdgesCount({
- count: isPg(context) ? 42 : 24,
- circleCount: isPg(context) ? 40 : 22,
- rectangleCount: isPg(context) ? 44 : 26,
- });
-
- await erd.verifyNode({ tableName: `film_actor` });
-
- // Verify show junction table names
- await erd.clickShowJunctionTableNames();
- await erd.verifyJunctionTableLabel({
- tableName: `film_actor`,
- tableTitle: 'filmactor',
- });
+ // await erd.clickShowMMTables();
+ // await erd.clickShowJunctionTableNames();
+ // await erd.clickShowJunctionTableNames();
+ //
+ // await erd.verifyNodesCount(isPg(context) ? 21 : 16);
+ // await erd.verifyEdgesCount({
+ // count: isPg(context) ? 42 : 24,
+ // circleCount: isPg(context) ? 40 : 22,
+ // rectangleCount: isPg(context) ? 44 : 26,
+ // });
+ //
+ // await erd.verifyNode({ tableName: `film_actor` });
+ //
+ // // Verify show junction table names
+ // await erd.clickShowJunctionTableNames();
+ // await erd.verifyJunctionTableLabel({
+ // tableName: `film_actor`,
+ // tableTitle: 'filmactor',
+ // });
});
test('Verify ERD Table view, and verify column operations are reflected to the ERD view', async () => {
await openErdOfATable('Country');
- const erd = dashboard.grid.toolbar.actions.erd;
+ const erd = dashboard.details.relations;
+ await erd.clickShowColumnNames();
// Verify tables with default config
await erd.verifyColumns({
tableName: `country`,
columns: ['country_id', 'country', 'last_update', 'cities'],
});
-
await erd.verifyColumns({
tableName: `city`,
columns: ['city_id', 'city', 'country_id', 'last_update', 'country', 'addresses'],
@@ -208,7 +198,6 @@ test.describe.skip('Erd', () => {
tableName: `country`,
columns: ['country', 'last_update', 'cities'],
});
-
await erd.verifyColumns({
tableName: `city`,
columns: ['city', 'last_update', 'country', 'addresses'],
@@ -217,7 +206,6 @@ test.describe.skip('Erd', () => {
// Verify with all columns disabled
await erd.clickShowColumnNames();
await erd.verifyColumns({ tableName: `country`, columns: ['cities'] });
-
await erd.verifyColumns({
tableName: `city`,
columns: ['country', 'addresses'],
@@ -226,25 +214,26 @@ test.describe.skip('Erd', () => {
// Enable All columns
await erd.clickShowColumnNames();
- await erd.close();
+ // switch to data tab, add column, switch back to ERD tab, verify column added
+ //
+ await dashboard.grid.topbar.btn_data.click();
// Add column
await dashboard.grid.column.create({ title: 'test_column' });
- // Verify in Settings ERD and table ERD
- await openProjectErd();
- await dashboard.settings.dataSources.erd.verifyNode({
- tableName: `country`,
- columnName: 'test_column',
- });
- await dashboard.settings.close();
- await dashboard.viewSidebar.openDeveloperTab({ option: 'ERD' });
+ // Verify
+ await dashboard.grid.topbar.btn_details.click();
+ await openErdOfATable('Country');
+ await erd.clickShowColumnNames();
- await dashboard.grid.toolbar.actions.erd.verifyNode({
+ await erd.verifyNode({
tableName: `country`,
columnName: 'test_column',
});
- await dashboard.grid.toolbar.actions.erd.close();
+
+ /////////////////////////////////////////////////////////////////
+
+ await dashboard.grid.topbar.btn_data.click();
// Update column
await dashboard.grid.column.openEdit({ title: 'test_column' });
@@ -252,66 +241,58 @@ test.describe.skip('Erd', () => {
await dashboard.grid.column.save({
isUpdated: true,
});
- // Verify in Settings ERD and table ERD
- await openProjectErd();
- await dashboard.settings.dataSources.erd.verifyNode({
- tableName: `country`,
- columnName: 'new_test_column',
- });
- await dashboard.settings.close();
- await dashboard.viewSidebar.openDeveloperTab({ option: 'ERD' });
+ // Verify
+ await dashboard.grid.topbar.btn_details.click();
+ await openErdOfATable('Country');
+ await erd.clickShowColumnNames();
- await dashboard.grid.toolbar.actions.erd.verifyNode({
+ await erd.verifyNode({
tableName: `country`,
columnName: 'new_test_column',
});
- await dashboard.grid.toolbar.actions.erd.close();
+ /////////////////////////////////////////////////////////////////
+
+ await dashboard.grid.topbar.btn_data.click();
// Delete column
await dashboard.grid.column.delete({ title: 'new_test_column' });
- // Verify in Settings ERD and table ERD
- await openProjectErd();
- await dashboard.settings.dataSources.erd.verifyNode({
+ await dashboard.grid.topbar.btn_details.click();
+ await openErdOfATable('Country');
+ await erd.clickShowColumnNames();
+
+ await erd.verifyNode({
tableName: `country`,
columnNameShouldNotExist: 'new_test_column',
});
- await dashboard.settings.close();
});
test('Verify table operations sync with ERD', async () => {
await openProjectErd();
- await dashboard.settings.close();
-
- await dashboard.treeView.openTable({ title: 'Country' });
- await dashboard.viewSidebar.openDeveloperTab({ option: 'ERD' });
-
- await dashboard.grid.toolbar.actions.erd.verifyNode({
+ await dashboard.details.relations.verifyNode({
tableName: `country`,
columnNameShouldNotExist: 'new_test_column',
});
- await dashboard.grid.toolbar.actions.erd.close();
+
+ await dashboard.details.relations.close();
// Create table and verify ERD
await dashboard.treeView.createTable({ title: 'Test', projectTitle: context.project.title });
// Verify in Settings ERD and table ERD
+ await dashboard.treeView.openProject({ title: context.project.title });
await openProjectErd();
- await dashboard.settings.dataSources.erd.verifyNode({
+ await dashboard.details.relations.verifyNode({
tableName: `Test`,
});
- await dashboard.settings.close();
+ await dashboard.details.relations.close();
// Delete table and verify ERD
await dashboard.treeView.deleteTable({ title: 'Test' });
await openProjectErd();
- await dashboard.settings.dataSources.erd.verifyNodeDoesNotExist({
+ await dashboard.details.relations.verifyNodeDoesNotExist({
tableName: `Test`,
});
-
- // Verify that `show mm table` option disabled will not trigger easter in ERD options
- await dashboard.settings.dataSources.erd.dbClickShowColumnNames();
- await dashboard.settings.dataSources.erd.verifyEasterEggNotShown();
- await dashboard.settings.close();
+ await dashboard.details.relations.close();
});
});
@@ -343,12 +324,8 @@ const pgPaymentTableColumns = [
];
const actorLTARColumns = ['filmactors', 'films'];
-
const actorNonPkFkColumns = ['first_name', 'last_name', 'last_update', 'films', 'filmactors'];
-
const paymentLTARColumns = ['customer', 'rental', 'staff'];
-
const pgPaymentNonPkFkColumns = ['amount', 'payment_date', 'customer', 'rental', 'staff'];
const paymentNonPkFkColumns = [...pgPaymentNonPkFkColumns, 'last_update'];
-
const salesByStoreColumns = ['store', 'manager', 'total_sales'];
diff --git a/tests/playwright/tests/db/features/filters.spec.ts b/tests/playwright/tests/db/features/filters.spec.ts
index af751aee29..2a95b8aab4 100644
--- a/tests/playwright/tests/db/features/filters.spec.ts
+++ b/tests/playwright/tests/db/features/filters.spec.ts
@@ -98,6 +98,7 @@ async function verifyFilter(param: {
locallySaved: false,
dataType: param?.dataType,
openModal: true,
+ skipWaitingResponse: true,
});
// verify filtered rows
@@ -257,7 +258,7 @@ test.describe('Filter Tests: Numerical', () => {
await numBasedFilterTest('Rating', '3', '2');
});
- test.skip('Filter: Duration', async () => {
+ test('Filter: Duration', async () => {
await numBasedFilterTest('Duration', '00:01', '01:03');
});
diff --git a/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts b/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts
index a565b4e65f..eff00c706a 100644
--- a/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts
+++ b/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts
@@ -3,7 +3,7 @@ import { DashboardPage } from '../../../pages/Dashboard';
import { GridPage } from '../../../pages/Dashboard/Grid';
import setup, { unsetup } from '../../../setup';
import { Api, UITypes } from 'nocodb-sdk';
-import { isEE, isHub } from '../../../setup/db';
+import { isEE } from '../../../setup/db';
import { getDefaultPwd } from '../../utils/general';
import config from '../../../playwright.config';
@@ -76,43 +76,27 @@ test.describe('Verify shortcuts', () => {
// fullscreen
// to be implemented for hub
- if (!isHub()) {
- await page.keyboard.press('Alt+f');
- await dashboard.treeView.verifyVisibility({
- isVisible: false,
- });
- await dashboard.viewSidebar.verifyVisibility({
- isVisible: false,
- });
- await page.keyboard.press('Alt+f');
- await dashboard.treeView.verifyVisibility({
- isVisible: true,
- });
- await dashboard.viewSidebar.verifyVisibility({
- isVisible: true,
- });
- }
+ // await page.keyboard.press('Alt+f');
+ // await dashboard.treeView.verifyVisibility({
+ // isVisible: false,
+ // });
+ // await dashboard.viewSidebar.verifyVisibility({
+ // isVisible: false,
+ // });
+ // await page.keyboard.press('Alt+f');
+ // await dashboard.treeView.verifyVisibility({
+ // isVisible: true,
+ // });
+ // await dashboard.viewSidebar.verifyVisibility({
+ // isVisible: true,
+ // });
// disabled temporarily for hub. Clipboard access to be fixed.
- if (!isHub()) {
- // invite team member
- await page.keyboard.press('Alt+i');
- if (isHub()) {
- await dashboard.grid.toolbar.share.invite({ email: 'new@example.com', role: 'editor' });
- const url = await dashboard.grid.toolbar.share.getInvitationUrl();
- expect(url).toContain('signup');
- } else {
- await dashboard.settings.teams.invite({
- email: 'new@example.com',
- role: 'editor',
- skipOpeningModal: true,
- });
- const url = await dashboard.settings.teams.getInvitationUrl();
- expect(url).toContain('signup');
- await page.waitForTimeout(1000);
- await dashboard.settings.teams.closeInvite();
- }
- }
+ // invite team member
+ // await page.keyboard.press('Alt+i');
+ // await dashboard.grid.toolbar.share.invite({ email: 'new@example.com', role: 'editor' });
+ // const url = await dashboard.grid.toolbar.share.getInvitationUrl();
+ // expect(url).toContain('signup');
// Cmd + Right arrow
await dashboard.treeView.openTable({ title: 'Country' });
diff --git a/tests/playwright/tests/db/features/language.spec.ts b/tests/playwright/tests/db/features/language.spec.ts
index dbea871edb..4c48384260 100644
--- a/tests/playwright/tests/db/features/language.spec.ts
+++ b/tests/playwright/tests/db/features/language.spec.ts
@@ -41,6 +41,8 @@ const langMenu = [
'zh-Hant.json',
];
+// i18n menu not enabled for EE
+//
test.describe.skip('Common', () => {
let context: any;
let dashboard: DashboardPage;
diff --git a/tests/playwright/tests/db/features/metaSync.spec.ts b/tests/playwright/tests/db/features/metaSync.spec.ts
index db55e8fc2d..2ead58f960 100644
--- a/tests/playwright/tests/db/features/metaSync.spec.ts
+++ b/tests/playwright/tests/db/features/metaSync.spec.ts
@@ -1,19 +1,19 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../../../pages/Dashboard';
-import { SettingsPage, SettingTab } from '../../../pages/Dashboard/Settings';
import setup, { NcContext, unsetup } from '../../../setup';
import { isMysql, isPg, isSqlite, mysqlExec, pgExec, sqliteExec } from '../../../setup/db';
+import { MetaDataPage } from '../../../pages/Dashboard/ProjectView/Metadata';
-test.describe.skip('Meta sync', () => {
+test.describe('Meta sync', () => {
let dashboard: DashboardPage;
- let settings: SettingsPage;
let context: NcContext;
let dbExec;
+ let metaData: MetaDataPage;
test.beforeEach(async ({ page }) => {
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
- settings = dashboard.settings;
+ metaData = dashboard.projectView.dataSources.metaData;
switch (context.dbType) {
case 'sqlite':
@@ -35,34 +35,33 @@ test.describe.skip('Meta sync', () => {
test('Meta sync', async () => {
test.setTimeout(process.env.CI ? 100000 : 70000);
- await dashboard.gotoSettings();
- await settings.selectTab({ tab: SettingTab.DataSources });
- await settings.dataSources.openMetaSync();
+ await dashboard.projectView.tab_dataSources.click();
+ await dashboard.projectView.dataSources.openMetaSync({ rowIndex: 0 });
await dbExec(`CREATE TABLE table1 (id INT NOT NULL, col1 INT NULL, PRIMARY KEY (id))`);
await dbExec(`CREATE TABLE table2 (id INT NOT NULL, col1 INT NULL, PRIMARY KEY (id))`);
- await settings.dataSources.metaData.clickReload();
+ await metaData.clickReload();
await dashboard.rootPage.waitForTimeout(1000);
- await settings.dataSources.metaData.verifyRow({
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: `table1`,
state: 'New table',
});
- await settings.dataSources.metaData.verifyRow({
+ await metaData.verifyRow({
index: isPg(context) ? 22 : 17,
model: `table2`,
state: 'New table',
});
- await settings.dataSources.metaData.sync();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.sync();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: 'Table1',
state: 'No change identified',
});
- await settings.dataSources.metaData.verifyRow({
+ await metaData.verifyRow({
index: isPg(context) ? 22 : 17,
model: 'Table2',
state: 'No change identified',
@@ -78,16 +77,16 @@ test.describe.skip('Meta sync', () => {
`ALTER TABLE table1 ADD CONSTRAINT fk1 FOREIGN KEY (col1) REFERENCES table2 (id) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
}
- await settings.dataSources.metaData.clickReload();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.clickReload();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: 'Table1',
state: 'New relation added',
});
//verify after sync
- await settings.dataSources.metaData.sync();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.sync();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: 'Table1',
state: 'No change identified',
@@ -100,16 +99,16 @@ test.describe.skip('Meta sync', () => {
await dbExec(`ALTER TABLE table1 DROP FOREIGN KEY fk1`);
await dbExec(`ALTER TABLE table1 DROP INDEX fk1_idx`);
}
- await settings.dataSources.metaData.clickReload();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.clickReload();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: 'Table1',
state: 'Relation removed',
});
//verify after sync
- await settings.dataSources.metaData.sync();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.sync();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: 'Table1',
state: 'No change identified',
@@ -125,16 +124,16 @@ test.describe.skip('Meta sync', () => {
await dbExec(`ALTER TABLE table1 ADD COLUMN newCol INT`);
}
- await settings.dataSources.metaData.clickReload();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.clickReload();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: `Table1`,
state: 'New column(newCol)',
});
//verify after sync
- await settings.dataSources.metaData.sync();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.sync();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: 'Table1',
state: 'No change identified',
@@ -149,16 +148,16 @@ test.describe.skip('Meta sync', () => {
await dbExec(`ALTER TABLE table1 RENAME COLUMN newCol TO newColName`);
}
- await settings.dataSources.metaData.clickReload();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.clickReload();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: `Table1`,
state: 'New column(newColName), Column removed(newCol)',
});
//verify after sync
- await settings.dataSources.metaData.sync();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.sync();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: 'Table1',
state: 'No change identified',
@@ -168,16 +167,16 @@ test.describe.skip('Meta sync', () => {
// todo: Add for sqlite
if (!isSqlite(context)) {
await dbExec(`ALTER TABLE table1 DROP COLUMN newColName`);
- await settings.dataSources.metaData.clickReload();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.clickReload();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: `Table1`,
state: 'Column removed(newColName)',
});
//verify after sync
- await settings.dataSources.metaData.sync();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.sync();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: 'Table1',
state: 'No change identified',
@@ -187,51 +186,51 @@ test.describe.skip('Meta sync', () => {
// Delete table
await dbExec(`DROP TABLE table1`);
await dbExec(`DROP TABLE table2`);
- await settings.dataSources.metaData.clickReload();
- await settings.dataSources.metaData.verifyRow({
+ await metaData.clickReload();
+ await metaData.verifyRow({
index: isPg(context) ? 21 : 16,
model: `table1`,
state: 'Table removed',
});
- await settings.dataSources.metaData.verifyRow({
+ await metaData.verifyRow({
index: isPg(context) ? 22 : 17,
model: `table2`,
state: 'Table removed',
});
//verify after sync
- await settings.dataSources.metaData.sync();
+ await metaData.sync();
if (isSqlite(context)) {
- await settings.dataSources.metaData.verifyRow({
+ await metaData.verifyRow({
index: 16,
model: 'CustomerList',
state: 'No change identified',
});
- await settings.dataSources.metaData.verifyRow({
+ await metaData.verifyRow({
index: 17,
model: 'FilmList',
state: 'No change identified',
});
}
if (isPg(context)) {
- await settings.dataSources.metaData.verifyRow({
+ await metaData.verifyRow({
index: 21,
model: 'ActorInfo',
state: 'No change identified',
});
- await settings.dataSources.metaData.verifyRow({
+ await metaData.verifyRow({
index: 22,
model: 'CustomerList',
state: 'No change identified',
});
} else if (isMysql(context)) {
- await settings.dataSources.metaData.verifyRow({
+ await metaData.verifyRow({
index: 16,
model: 'ActorInfo',
state: 'No change identified',
});
- await settings.dataSources.metaData.verifyRow({
+ await metaData.verifyRow({
index: 17,
model: 'CustomerList',
state: 'No change identified',
@@ -247,13 +246,12 @@ test.describe.skip('Meta sync', () => {
`INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (1,1,1,1,1), (2,2,2,2,2), (3,3,3,3,3), (4,4,4,4,4), (5,5,5,5,5), (6,6,6,6,6), (7,7,7,7,7), (8,8,8,8,8), (9,9,9,9,9);`
);
- await dashboard.gotoSettings();
- await settings.selectTab({ tab: SettingTab.DataSources });
- await settings.dataSources.openMetaSync();
+ await dashboard.projectView.tab_dataSources.click();
+ await dashboard.projectView.dataSources.openMetaSync({ rowIndex: 0 });
- await settings.dataSources.metaData.clickReload();
- await settings.dataSources.metaData.sync();
- await settings.close();
+ await metaData.clickReload();
+ await metaData.sync();
+ await metaData.close();
await dashboard.treeView.openTable({ title: 'Table1' });
diff --git a/tests/playwright/tests/db/features/swagger.spec.ts b/tests/playwright/tests/db/features/swagger.spec.ts
index d162aee0af..9d934c4a2e 100644
--- a/tests/playwright/tests/db/features/swagger.spec.ts
+++ b/tests/playwright/tests/db/features/swagger.spec.ts
@@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test';
import { DashboardPage } from '../../../pages/Dashboard';
import setup, { unsetup } from '../../../setup';
-test.describe.skip('Table Column Operations', () => {
+test.describe('Swagger', () => {
let dashboard: DashboardPage;
let context: any;
@@ -20,7 +20,7 @@ test.describe.skip('Table Column Operations', () => {
const link = `http://localhost:8080/api/v1/db/meta/projects/${context.project.id}/swagger`;
await dashboard.rootPage.goto(link);
- const swagger = await dashboard.rootPage;
+ const swagger = dashboard.rootPage;
// authorize with token information
await swagger.locator('.btn.authorize').click();
diff --git a/tests/playwright/tests/db/features/timezone.spec.ts b/tests/playwright/tests/db/features/timezone.spec.ts
index 687e17c548..1e2ea3c63b 100644
--- a/tests/playwright/tests/db/features/timezone.spec.ts
+++ b/tests/playwright/tests/db/features/timezone.spec.ts
@@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test';
import { DashboardPage } from '../../../pages/Dashboard';
import setup, { NcContext, unsetup } from '../../../setup';
import { Api, ProjectListType, UITypes } from 'nocodb-sdk';
-import { isEE, isHub, isMysql, isPg, isSqlite } from '../../../setup/db';
+import { isEE, isMysql, isPg, isSqlite } from '../../../setup/db';
import { getKnexConfig } from '../../utils/config';
import { getBrowserTimezoneOffset } from '../../utils/general';
import config from '../../../playwright.config';
@@ -116,7 +116,7 @@ test.describe.serial('Timezone-XCDB : Japan/Tokyo', () => {
test.beforeEach(async ({ page }) => {
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
- if (!isSqlite(context) && !isHub()) return;
+ if (!isSqlite(context)) return;
try {
const { project, table, api } = await timezoneSuite(`xcdb${context.workerId}`, context);
@@ -153,7 +153,7 @@ test.describe.serial('Timezone-XCDB : Japan/Tokyo', () => {
* Display value is converted to Asia/Tokyo
*/
test('API insert, verify display value', async () => {
- if (!isSqlite(context) && !isHub()) return;
+ if (!isSqlite(context)) return;
await dashboard.treeView.openBase({ title: `xcdb${context.workerId}` });
await dashboard.treeView.openTable({ title: 'dateTimeTable' });
@@ -189,7 +189,7 @@ test.describe.serial('Timezone-XCDB : Japan/Tokyo', () => {
*/
test('API Insert, verify API read response', async () => {
- if (!isSqlite(context) && !isHub()) return;
+ if (!isSqlite(context)) return;
const dateInserted = new Date(`2021-01-01 00:00:00${getBrowserTimezoneOffset()}`);
// translate dateInserted to UTC in YYYY-MM-DD HH:mm format
diff --git a/tests/playwright/tests/db/features/undo-redo.spec.ts b/tests/playwright/tests/db/features/undo-redo.spec.ts
index c2dc81c5ff..cfea48dd8f 100644
--- a/tests/playwright/tests/db/features/undo-redo.spec.ts
+++ b/tests/playwright/tests/db/features/undo-redo.spec.ts
@@ -327,18 +327,24 @@ test.describe('Undo Redo', () => {
await verifyRowHeight({ height: '1.8rem' });
});
- // fix me! is flaky, and need to be validated
- test.skip('Column width', async ({ page }) => {
- // close 'Team & Auth' tab
- await dashboard.closeTab({ title: 'Team & Auth' });
+ test('Column width', async ({ page }) => {
await dashboard.treeView.openTable({ title: 'numberBased' });
const originalWidth = await dashboard.grid.column.getWidth({ title: 'Number' });
await dashboard.grid.column.resize({ src: 'Number', dst: 'Decimal' });
- await dashboard.rootPage.waitForTimeout(100);
+ let modifiedWidth = await dashboard.grid.column.getWidth({ title: 'Number' });
+ let retryCounter = 0;
+ while (modifiedWidth === originalWidth) {
+ retryCounter++;
+ await dashboard.rootPage.waitForTimeout(500 * retryCounter);
+ if (retryCounter > 5) {
+ break;
+ }
+
+ modifiedWidth = await dashboard.grid.column.getWidth({ title: 'Number' });
+ }
- const modifiedWidth = await dashboard.grid.column.getWidth({ title: 'Number' });
expect(modifiedWidth).toBeGreaterThan(originalWidth);
await undo({ page, dashboard });
diff --git a/tests/playwright/tests/db/features/webhook.spec.ts b/tests/playwright/tests/db/features/webhook.spec.ts
index 5fc9b8ef3a..0593536e28 100644
--- a/tests/playwright/tests/db/features/webhook.spec.ts
+++ b/tests/playwright/tests/db/features/webhook.spec.ts
@@ -16,11 +16,11 @@ async function clearServerData({ request }) {
// ensure stored message count is 0
const response = await request.get(hookPath + '/count');
- await expect(await response.json()).toBe(0);
+ expect(await response.json()).toBe(0);
}
async function getWebhookResponses({ request, count = 1 }) {
- let response;
+ let response: { json: () => any };
// kludge- add delay to allow server to process webhook
await new Promise(resolve => setTimeout(resolve, 1000));
@@ -33,7 +33,7 @@ async function getWebhookResponses({ request, count = 1 }) {
}
await new Promise(resolve => setTimeout(resolve, 100));
}
- await expect(await response.json()).toBe(count);
+ expect(await response.json()).toBe(count);
response = await request.get(hookPath + '/all');
return await response.json();
@@ -41,7 +41,7 @@ async function getWebhookResponses({ request, count = 1 }) {
async function verifyHookTrigger(count: number, value: string, request, expectedData?: any) {
// Retry since there can be lag between the time the hook is triggered and the time the server receives the request
- let response;
+ let response: { json: () => any };
// retry since there can be lag between the time the hook is triggered and the time the server receives the request
for (let i = 0; i < 20; i++) {
@@ -70,7 +70,7 @@ async function verifyHookTrigger(count: number, value: string, request, expected
const rspJson = await response.json();
expect(rspJson?.data?.rows[0]?.Title).toBe(value);
if (expectedData) {
- await expect(isSubset(rspJson, expectedData)).toBe(true);
+ expect(isSubset(rspJson, expectedData)).toBe(true);
}
}
}
diff --git a/tests/playwright/tests/db/general/projectOperations.spec.ts b/tests/playwright/tests/db/general/projectOperations.spec.ts
index 6dc22ad9ca..92b153c64e 100644
--- a/tests/playwright/tests/db/general/projectOperations.spec.ts
+++ b/tests/playwright/tests/db/general/projectOperations.spec.ts
@@ -2,26 +2,30 @@ import { expect, test } from '@playwright/test';
import { DashboardPage } from '../../../pages/Dashboard';
import { airtableApiBase, airtableApiKey } from '../../../constants';
import setup, { unsetup } from '../../../setup';
-import { ToolbarPage } from '../../../pages/Dashboard/common/Toolbar';
-import { ProjectsPage } from '../../../pages/ProjectsPage';
import { Api, ProjectListType } from 'nocodb-sdk';
import { ProjectInfo, ProjectInfoApiUtil } from '../../../tests/utils/projectInfoApiUtil';
import { deepCompare } from '../../../tests/utils/objectCompareUtil';
-import { isHub } from '../../../setup/db';
+import { isEE } from '../../../setup/db';
-// tests covered under tests/playwright/tests/nocohub/hubDashboard.spec.ts
-test.describe.skip('Project operations', () => {
+test.describe('Project operations', () => {
let dashboard: DashboardPage;
- let toolbar: ToolbarPage;
let context: any;
let api: Api;
- let projectPage: ProjectsPage;
test.setTimeout(100000);
+ async function getProjectList() {
+ let projectList: ProjectListType;
+ if (isEE() && api['workspaceProject']) {
+ const ws = await api['workspace'].list();
+ projectList = await api['workspaceProject'].list(ws.list[1].id);
+ } else {
+ projectList = await api.project.list();
+ }
+ return projectList;
+ }
async function deleteIfExists(name: string) {
try {
- const ws = await api.workspace.list();
- const projectList = await api.workspaceProject.list(ws.list[0].id);
+ const projectList = await getProjectList();
const project = projectList.list.find((p: any) => p.title === name);
if (project) {
@@ -34,29 +38,25 @@ test.describe.skip('Project operations', () => {
}
async function createTestProjectWithData(testProjectName: string) {
- await dashboard.clickHome();
- await projectPage.createProject({ name: testProjectName, withoutPrefix: true });
- await dashboard.treeView.quickImport({ title: 'Airtable', projectTitle: context.project.title });
+ await dashboard.leftSidebar.createProject({ title: testProjectName });
+ await dashboard.treeView.openProject({ title: testProjectName });
+ await dashboard.treeView.quickImport({ title: 'Airtable', projectTitle: testProjectName });
await dashboard.importAirtable.import({
key: airtableApiKey,
baseId: airtableApiBase,
});
await dashboard.rootPage.waitForTimeout(1000);
- // await quickVerify({ dashboard, airtableImport: true, context });
}
async function cleanupTestData(dupeProjectName: string, testProjectName: string) {
- await dashboard.clickHome();
- await projectPage.deleteProject({ title: dupeProjectName, withoutPrefix: true });
- await projectPage.deleteProject({ title: testProjectName, withoutPrefix: true });
+ await dashboard.treeView.deleteProject({ title: dupeProjectName });
+ await dashboard.treeView.deleteProject({ title: testProjectName });
}
test.beforeEach(async ({ page }) => {
page.setDefaultTimeout(70000);
context = await setup({ page });
dashboard = new DashboardPage(page, context.project);
- projectPage = new ProjectsPage(page);
- toolbar = dashboard.grid.toolbar;
api = new Api({
baseURL: `http://localhost:8080/`,
@@ -70,57 +70,35 @@ test.describe.skip('Project operations', () => {
await unsetup(context);
});
- test.skip('rename, delete', async () => {
- // Already verified as part of workspace tests
-
+ test('rename, delete', async () => {
// if project already exists, delete it
await deleteIfExists('project-firstName');
- await dashboard.clickHome();
- await projectPage.createProject({ name: 'project-firstName', withoutPrefix: true });
- await dashboard.clickHome();
- await projectPage.renameProject({
- title: 'project-firstName',
- newTitle: 'project-rename',
- withoutPrefix: true,
- });
- await dashboard.clickHome();
- await projectPage.openProject({ title: 'project-rename', withoutPrefix: true });
- await dashboard.clickHome();
- await projectPage.deleteProject({ title: 'project-rename', withoutPrefix: true });
+ await dashboard.leftSidebar.createProject({ title: 'project-firstName' });
+ await dashboard.treeView.renameProject({ title: 'project-firstName', newTitle: 'project-rename' });
+ await dashboard.treeView.openProject({ title: 'project-rename' });
+ await dashboard.treeView.deleteProject({ title: 'project-rename' });
});
test('project_duplicate', async () => {
// if project already exists, delete it to avoid test failures due to residual data
- const testProjectName = 'project-to-imexp';
+ const testProjectName = 'Project-To-Import-Export';
const dupeProjectName: string = testProjectName + ' copy';
await deleteIfExists(testProjectName);
await deleteIfExists(dupeProjectName);
- // // data creation for orginial test project
+ // // data creation for original test project
await createTestProjectWithData(testProjectName);
- // create duplicate
- await dashboard.clickHome();
- await projectPage.duplicateProject({
- name: testProjectName,
- withoutPrefix: true,
- includeData: true,
- includeViews: true,
- });
- await projectPage.openProject({ title: dupeProjectName, withoutPrefix: true });
- // await quickVerify({ dashboard, airtableImport: true, context });
+ // duplicate duplicate
+ await dashboard.treeView.duplicateProject({ title: testProjectName });
+ await dashboard.treeView.openProject({ title: testProjectName });
// compare
- let projectList: ProjectListType;
- if (isHub()) {
- const ws = await api.workspace.list();
- projectList = await api.workspaceProject.list(ws.list[0].id);
- } else {
- projectList = await api.project.list();
- }
- const testProjectId = await projectList.list.find((p: any) => p.title === testProjectName);
- const dupeProjectId = await projectList.list.find((p: any) => p.title === dupeProjectName);
+ const projectList = await getProjectList();
+
+ const testProjectId = projectList.list.find((p: any) => p.title === testProjectName);
+ const dupeProjectId = projectList.list.find((p: any) => p.title.startsWith(testProjectName + ' copy'));
const projectInfoOp: ProjectInfoApiUtil = new ProjectInfoApiUtil(context.token);
const original: Promise = projectInfoOp.extractProjectInfo(testProjectId.id);
const duplicate: Promise = projectInfoOp.extractProjectInfo(dupeProjectId.id);
@@ -163,12 +141,15 @@ test.describe.skip('Project operations', () => {
'.users.1.roles',
'.users.2.roles',
]);
- const orginalProjectInfo: ProjectInfo = arr[0];
+ const originalProjectInfo: ProjectInfo = arr[0];
const duplicateProjectInfo: ProjectInfo = arr[1];
- expect(deepCompare(orginalProjectInfo, duplicateProjectInfo, ignoredFields, ignoredKeys, '', false)).toBeTruthy();
+ expect(
+ deepCompare(originalProjectInfo, duplicateProjectInfo, ignoredFields, ignoredKeys, '', false)
+ ).toBeTruthy();
});
// cleanup test-data
- await cleanupTestData(dupeProjectName, testProjectName);
+ // fix me! skip project cleanup
+ // await cleanupTestData(dupeProjectId.title, testProjectId.title);
});
});
diff --git a/tests/playwright/tests/db/general/tableOperations.spec.ts b/tests/playwright/tests/db/general/tableOperations.spec.ts
index 3204d79607..b52fcf5b5f 100644
--- a/tests/playwright/tests/db/general/tableOperations.spec.ts
+++ b/tests/playwright/tests/db/general/tableOperations.spec.ts
@@ -1,20 +1,20 @@
import { expect, test } from '@playwright/test';
import { Api, TableListType, TableType } from 'nocodb-sdk';
import { DashboardPage } from '../../../pages/Dashboard';
-import { SettingsPage, SettingTab } from '../../../pages/Dashboard/Settings';
import { deepCompare } from '../../../tests/utils/objectCompareUtil';
import setup, { unsetup } from '../../../setup';
import { ProjectInfoApiUtil, TableInfo } from '../../../tests/utils/projectInfoApiUtil';
-import { isHub } from '../../../setup/db';
+import { isEE } from '../../../setup/db';
+import { AuditPage } from '../../../pages/Dashboard/ProjectView/Audit';
test.describe('Table Operations', () => {
- let dashboard: DashboardPage, settings: SettingsPage;
+ let dashboard: DashboardPage, audit: AuditPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
- settings = dashboard.settings;
+ audit = dashboard.projectView.dataSources.audit;
});
test.afterEach(async () => {
@@ -28,23 +28,26 @@ test.describe('Table Operations', () => {
await dashboard.treeView.deleteTable({ title: 'tablex' });
await dashboard.treeView.verifyTable({ title: 'tablex', exists: false });
- if (!isHub()) {
+ if (!isEE()) {
// Audit logs in clickhouse; locally wont be accessible
- await dashboard.gotoSettings();
- await settings.selectTab({ tab: SettingTab.Audit });
- await settings.audit.verifyRow({
+
+ await dashboard.treeView.openProject({ title: context.project.title });
+ await dashboard.projectView.tab_dataSources.click();
+ await dashboard.projectView.dataSources.openAudit({ rowIndex: 0 });
+
+ await audit.verifyRow({
index: 0,
opType: 'TABLE',
opSubtype: 'DELETE',
- user: 'user@nocodb.com',
+ user: `user-${process.env.TEST_PARALLEL_INDEX}@nocodb.com`,
});
- await settings.audit.verifyRow({
+ await audit.verifyRow({
index: 1,
opType: 'TABLE',
opSubtype: 'CREATE',
- user: 'user@nocodb.com',
+ user: `user-${process.env.TEST_PARALLEL_INDEX}@nocodb.com`,
});
- await settings.close();
+ await audit.close();
}
await dashboard.treeView.renameTable({ title: 'City', newTitle: 'Cityx' });
@@ -71,7 +74,7 @@ test.describe('Table Operations', () => {
await dashboard.treeView.duplicateTable(orginalTableName, true, true);
await dashboard.treeView.verifyTable({ title: dupTableName });
// let projectInfoApiUtil: ProjectInfoApiUtil = new ProjectInfoApiUtil(context.token);
- // let orginalTable: Promise = projectInfoApiUtil.extractTableInfo(context.project_id, 'Address');
+ // let originalTable: Promise = projectInfoApiUtil.extractTableInfo(context.project_id, 'Address');
// let duplicateTable: Promise = await this.api.dbTable.list(projectId);.extractTableInfo(context.project_id, 'Address copy');
const api: Api = new Api({
baseURL: `http://localhost:8080/`,
@@ -80,11 +83,11 @@ test.describe('Table Operations', () => {
},
});
const tables: TableListType = await api.dbTable.list(context.project.id);
- const orginalTable: TableType = await tables.list.filter(t => t.title === orginalTableName)[0];
- const duplicateTable: TableType = await tables.list.filter(t => t.title === dupTableName)[0];
+ const originalTable: TableType = tables.list.filter(t => t.title === orginalTableName)[0];
+ const duplicateTable: TableType = tables.list.filter(t => t.title === dupTableName)[0];
expect(
deepCompare(
- orginalTable,
+ originalTable,
duplicateTable,
undefined,
new Set(['.id', '.table_name', '.title', '.order', '.created_at', '.updated_at'])
@@ -94,13 +97,13 @@ test.describe('Table Operations', () => {
});
test.skip('duplicate_table_with_no_data_views', async () => {
- const orginalTableName = 'Actor';
+ const originalTableName = 'Actor';
const dupTableName = 'Actor copy';
// verify table icon customization
- await dashboard.treeView.duplicateTable(orginalTableName, false, false);
+ await dashboard.treeView.duplicateTable(originalTableName, false, false);
await dashboard.treeView.verifyTable({ title: dupTableName });
// let projectInfoApiUtil: ProjectInfoApiUtil = new ProjectInfoApiUtil(context.token);
- // let orginalTable: Promise = projectInfoApiUtil.extractTableInfo(context.project_id, 'Address');
+ // let originalTable: Promise = projectInfoApiUtil.extractTableInfo(context.project_id, 'Address');
// let duplicateTable: Promise = await this.api.dbTable.list(projectId);.extractTableInfo(context.project_id, 'Address copy');
const api: Api = new Api({
baseURL: `http://localhost:8080/`,
@@ -109,14 +112,14 @@ test.describe('Table Operations', () => {
},
});
const tables: TableListType = await api.dbTable.list(context.project.id);
- const orginalTable: TableType = await tables.list.filter(t => t.title === orginalTableName)[0];
- const duplicateTable: TableType = await tables.list.filter(t => t.title === dupTableName)[0];
+ const originalTable: TableType = tables.list.filter(t => t.title === originalTableName)[0];
+ const duplicateTable: TableType = tables.list.filter(t => t.title === dupTableName)[0];
const p: ProjectInfoApiUtil = new ProjectInfoApiUtil(context.token);
- const orginalTableInfo: TableInfo = await p.extractTableInfo(orginalTable, context.project.id);
+ const originalTableInfo: TableInfo = await p.extractTableInfo(originalTable, context.project.id);
const duplicateTableInfo: TableInfo = await p.extractTableInfo(duplicateTable, context.project.id);
expect(
deepCompare(
- orginalTableInfo,
+ originalTableInfo,
duplicateTableInfo,
new Set(['created_at']),
new Set([
diff --git a/tests/playwright/tests/db/general/viewMenu.spec.ts b/tests/playwright/tests/db/general/viewMenu.spec.ts
index fe7ec8767d..97c949bac7 100644
--- a/tests/playwright/tests/db/general/viewMenu.spec.ts
+++ b/tests/playwright/tests/db/general/viewMenu.spec.ts
@@ -16,12 +16,10 @@ test.describe('Grid view locked', () => {
await unsetup(context);
});
- test.skip('ReadOnly lock & collaboration mode', async () => {
- // close 'Team & Auth' tab
- await dashboard.closeTab({ title: 'Team & Auth' });
+ test('ReadOnly lock & collaboration mode', async () => {
await dashboard.treeView.openTable({ title: 'Country' });
- await dashboard.grid.toolbar.viewsMenu.verifyCollaborativeMode();
+ await dashboard.grid.verifyCollaborativeMode();
// enable view lock
await dashboard.grid.toolbar.viewsMenu.click({
@@ -30,7 +28,7 @@ test.describe('Grid view locked', () => {
});
// verify view lock
- await dashboard.grid.toolbar.viewsMenu.verifyLockMode();
+ await dashboard.grid.verifyLockMode();
// enable collaborative view
await dashboard.grid.toolbar.viewsMenu.click({
@@ -38,7 +36,7 @@ test.describe('Grid view locked', () => {
subMenu: 'Collaborative View',
});
- await dashboard.grid.toolbar.viewsMenu.verifyCollaborativeMode();
+ await dashboard.grid.verifyCollaborativeMode();
});
test('Download CSV', async () => {
diff --git a/tests/playwright/tests/db/general/views.spec.ts b/tests/playwright/tests/db/general/views.spec.ts
index 44b01e980a..29c0604254 100644
--- a/tests/playwright/tests/db/general/views.spec.ts
+++ b/tests/playwright/tests/db/general/views.spec.ts
@@ -1,4 +1,4 @@
-import { expect, test } from '@playwright/test';
+import { test } from '@playwright/test';
import { DashboardPage } from '../../../pages/Dashboard';
import { ToolbarPage } from '../../../pages/Dashboard/common/Toolbar';
import setup, { unsetup } from '../../../setup';
diff --git a/tests/playwright/tests/db/usersAccounts/accountUserManagement.spec.ts b/tests/playwright/tests/db/usersAccounts/accountUserManagement.spec.ts
index 6d472f570a..25dd5ace47 100644
--- a/tests/playwright/tests/db/usersAccounts/accountUserManagement.spec.ts
+++ b/tests/playwright/tests/db/usersAccounts/accountUserManagement.spec.ts
@@ -1,7 +1,6 @@
import { test } from '@playwright/test';
import { AccountPage } from '../../../pages/Account';
import { AccountUsersPage } from '../../../pages/Account/Users';
-import { ProjectsPage } from '../../../pages/ProjectsPage';
import { SignupPage } from '../../../pages/SignupPage';
import setup, { unsetup } from '../../../setup';
import { WorkspacePage } from '../../../pages/WorkspacePage';
@@ -17,7 +16,6 @@ test.describe.skip('User roles', () => {
let accountUsersPage: AccountUsersPage;
let accountPage: AccountPage;
let signupPage: SignupPage;
- let projectsPage: ProjectsPage;
let workspacePage: WorkspacePage;
// @ts-ignore
let context: any;
@@ -28,7 +26,6 @@ test.describe.skip('User roles', () => {
accountUsersPage = new AccountUsersPage(accountPage);
signupPage = new SignupPage(accountPage.rootPage);
- projectsPage = new ProjectsPage(accountPage.rootPage);
workspacePage = new WorkspacePage(accountPage.rootPage);
});
diff --git a/tests/playwright/tests/db/usersAccounts/authChangePassword.spec.ts b/tests/playwright/tests/db/usersAccounts/authChangePassword.spec.ts
index 4ca8ade995..79197134ca 100644
--- a/tests/playwright/tests/db/usersAccounts/authChangePassword.spec.ts
+++ b/tests/playwright/tests/db/usersAccounts/authChangePassword.spec.ts
@@ -2,7 +2,6 @@ import { test } from '@playwright/test';
import { DashboardPage } from '../../../pages/Dashboard';
import setup, { unsetup } from '../../../setup';
import { LoginPage } from '../../../pages/LoginPage';
-import { SettingsPage, SettingTab } from '../../../pages/Dashboard/Settings';
import { SignupPage } from '../../../pages/SignupPage';
import { ProjectsPage } from '../../../pages/ProjectsPage';
import { AccountPage } from '../../../pages/Account';
@@ -13,9 +12,7 @@ import { WorkspacePage } from '../../../pages/WorkspacePage';
test.describe.skip('Auth', () => {
let context: any;
let dashboard: DashboardPage;
- let settings: SettingsPage;
let signupPage: SignupPage;
- let projectsPage: ProjectsPage;
let accountPage: AccountPage;
let workspacePage: WorkspacePage;
@@ -23,11 +20,8 @@ test.describe.skip('Auth', () => {
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
signupPage = new SignupPage(page);
- projectsPage = new ProjectsPage(page);
accountPage = new AccountPage(page);
workspacePage = new WorkspacePage(page);
-
- settings = dashboard.settings;
});
test.afterEach(async () => {
diff --git a/tests/playwright/tests/db/usersAccounts/rolesCreate.spec.ts b/tests/playwright/tests/db/usersAccounts/rolesCreate.spec.ts
index e83c7da5f2..42305c7229 100644
--- a/tests/playwright/tests/db/usersAccounts/rolesCreate.spec.ts
+++ b/tests/playwright/tests/db/usersAccounts/rolesCreate.spec.ts
@@ -1,7 +1,7 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../../../pages/Dashboard';
import setup, { unsetup } from '../../../setup';
-import { SettingsPage, SettingTab } from '../../../pages/Dashboard/Settings';
+import { SettingsPage } from '../../../pages/Dashboard/Settings';
import { SignupPage } from '../../../pages/SignupPage';
import { ProjectsPage } from '../../../pages/ProjectsPage';
import { getDefaultPwd } from '../../../tests/utils/general';
diff --git a/tests/playwright/tests/db/usersAccounts/rolesSuperUser.spec.ts b/tests/playwright/tests/db/usersAccounts/rolesSuperUser.spec.ts
index 0f1360035f..e132686a28 100644
--- a/tests/playwright/tests/db/usersAccounts/rolesSuperUser.spec.ts
+++ b/tests/playwright/tests/db/usersAccounts/rolesSuperUser.spec.ts
@@ -38,7 +38,7 @@ test.describe.skip('Super user', () => {
// Modify configuration
await card.click();
await card.locator('.nc-app-store-card-edit').click();
- slackModal = await appPage.locator('.nc-modal-plugin-install');
+ slackModal = appPage.locator('.nc-modal-plugin-install');
await slackModal.locator('[placeholder="Channel Name"]').fill('Test Channel 2');
await slackModal.locator('[placeholder="Webhook URL"]').fill('http://test2.com');
await slackModal.locator('button:has-text("Save")').click();
@@ -47,7 +47,7 @@ test.describe.skip('Super user', () => {
// Uninstall
await card.click();
await card.locator('.nc-app-store-card-reset').click();
- slackModal = await appPage.locator('.nc-modal-plugin-uninstall');
+ slackModal = appPage.locator('.nc-modal-plugin-uninstall');
await slackModal.locator('button:has-text("Confirm")').click();
await dashboard.verifyToast({ message: 'Plugin uninstalled successfully' });
});
diff --git a/tests/playwright/tests/db/views/viewForm.spec.ts b/tests/playwright/tests/db/views/viewForm.spec.ts
index 44864d173d..6e27cc7ecc 100644
--- a/tests/playwright/tests/db/views/viewForm.spec.ts
+++ b/tests/playwright/tests/db/views/viewForm.spec.ts
@@ -3,28 +3,22 @@ import { DashboardPage } from '../../../pages/Dashboard';
import setup, { unsetup } from '../../../setup';
import { FormPage } from '../../../pages/Dashboard/Form';
import { SharedFormPage } from '../../../pages/SharedForm';
-import { AccountPage } from '../../../pages/Account';
-import { AccountAppStorePage } from '../../../pages/Account/AppStore';
import { Api, UITypes } from 'nocodb-sdk';
import { LoginPage } from '../../../pages/LoginPage';
import { getDefaultPwd } from '../../../tests/utils/general';
import { WorkspacePage } from '../../../pages/WorkspacePage';
-import { isEE, isHub } from '../../../setup/db';
+import { isEE } from '../../../setup/db';
// todo: Move most of the ui actions to page object and await on the api response
test.describe('Form view', () => {
let dashboard: DashboardPage;
let form: FormPage;
- let accountAppStorePage: AccountAppStorePage;
- let accountPage: AccountPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
form = dashboard.form;
- accountPage = new AccountPage(page);
- accountAppStorePage = accountPage.appStore;
});
test.afterEach(async () => {
@@ -188,44 +182,39 @@ test.describe('Form view', () => {
});
const url = dashboard.rootPage.url();
- // fix me! for app store, need super admin login.
- if (isHub()) {
- return;
- }
-
// activate SMTP plugin
- await accountAppStorePage.goto();
-
- // install SMTP
- await accountAppStorePage.install({ name: 'SMTP' });
- await accountAppStorePage.configureSMTP({
- email: 'a@b.com',
- host: 'smtp.gmail.com',
- port: '587',
- });
- await dashboard.verifyToast({
- message: 'Successfully installed and email notification will use SMTP configuration',
- });
-
- // revisit form view
- await page.goto(url);
-
- // enable 'email-me' option
- await dashboard.viewSidebar.openView({ title: 'CountryForm' });
- await form.emailMeRadioButton.click();
- await form.verifyAfterSubmitMenuState({
- emailMe: true,
- submitAnotherForm: false,
- showBlankForm: false,
- });
-
- // Uninstall SMTP
- await accountAppStorePage.goto();
- await accountAppStorePage.uninstall({ name: 'SMTP' });
-
- await dashboard.verifyToast({
- message: 'Plugin uninstalled successfully',
- });
+ // await accountAppStorePage.goto();
+ //
+ // // install SMTP
+ // await accountAppStorePage.install({ name: 'SMTP' });
+ // await accountAppStorePage.configureSMTP({
+ // email: 'a@b.com',
+ // host: 'smtp.gmail.com',
+ // port: '587',
+ // });
+ // await dashboard.verifyToast({
+ // message: 'Successfully installed and email notification will use SMTP configuration',
+ // });
+ //
+ // // revisit form view
+ // await page.goto(url);
+ //
+ // // enable 'email-me' option
+ // await dashboard.viewSidebar.openView({ title: 'CountryForm' });
+ // await form.emailMeRadioButton.click();
+ // await form.verifyAfterSubmitMenuState({
+ // emailMe: true,
+ // submitAnotherForm: false,
+ // showBlankForm: false,
+ // });
+ //
+ // // Uninstall SMTP
+ // await accountAppStorePage.goto();
+ // await accountAppStorePage.uninstall({ name: 'SMTP' });
+ //
+ // await dashboard.verifyToast({
+ // message: 'Plugin uninstalled successfully',
+ // });
});
test('Form share, verify attachment file', async () => {