Browse Source

Merge branch 'develop' into feat/keyboard-manoeuvre

pull/4482/head
Wing-Kam Wong 2 years ago
parent
commit
d5097bbfca
  1. 5
      .github/workflows/ci-cd.yml
  2. 1
      .github/workflows/dco-check.yml
  3. 7
      .github/workflows/release-nocodb.yml
  4. 6
      .gitignore
  5. 23
      markdown/readme/languages/chinese.md
  6. 24
      markdown/readme/languages/german.md
  7. 2174
      package-lock.json
  8. 19
      package.json
  9. 12
      packages/nc-cli/package-lock.json
  10. 2
      packages/nc-gui/app.vue
  11. 2
      packages/nc-gui/components/cell/MultiSelect.vue
  12. 2
      packages/nc-gui/components/cell/SingleSelect.vue
  13. 7
      packages/nc-gui/components/smartsheet/Gallery.vue
  14. 7
      packages/nc-gui/components/smartsheet/Kanban.vue
  15. 1
      packages/nc-gui/components/smartsheet/header/CellIcon.ts
  16. 21
      packages/nc-gui/components/virtual-cell/Lookup.vue
  17. 74
      packages/nc-gui/lang/vi.json
  18. 2
      packages/nc-gui/lang/zh-Hans.json
  19. 2
      packages/nc-gui/layouts/default.vue
  20. 2
      packages/nc-gui/package-lock.json
  21. 5
      packages/nc-gui/pages/account/index/users.vue
  22. 30
      packages/nc-gui/pages/account/index/users/[[nestedPage]].vue
  23. 156
      packages/nc-gui/pages/index/index/index.vue
  24. 2
      packages/nc-gui/windi.config.ts
  25. 2
      packages/nc-lib-gui/package.json
  26. 12
      packages/nc-plugin/package-lock.json
  27. 3
      packages/noco-docs/content/en/engineering/translation.md
  28. 12
      packages/noco-docs/package-lock.json
  29. 4
      packages/nocodb-sdk/package-lock.json
  30. 2
      packages/nocodb-sdk/package.json
  31. 15
      packages/nocodb/docker-compose.yml
  32. 357
      packages/nocodb/package-lock.json
  33. 13
      packages/nocodb/package.json
  34. 120
      packages/nocodb/src/lib/db/sql-client/lib/mssql/MssqlClient.ts
  35. 41
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts
  36. 7
      packages/nocodb/src/lib/meta/api/swagger/helpers/getSwaggerColumnMetas.ts
  37. 21
      packages/nocodb/src/lib/meta/api/swagger/helpers/swagger-base.json
  38. 9
      packages/nocodb/src/lib/meta/helpers/populateSamplePayload.ts
  39. 6
      packages/nocodb/src/lib/models/SelectOption.ts
  40. 53
      scripts/cypress/cypress.json
  41. 67
      scripts/cypress/docker-compose-cypress.yml
  42. 17
      scripts/cypress/docker-compose-pg-cy-quick.yml
  43. 17
      scripts/cypress/docker-compose-pg.yml
  44. 5
      scripts/cypress/fixtures/example.json
  45. BIN
      scripts/cypress/fixtures/images/avatar-1.JPG
  46. BIN
      scripts/cypress/fixtures/images/avatar-2.JPG
  47. BIN
      scripts/cypress/fixtures/images/avatar-3.JPG
  48. BIN
      scripts/cypress/fixtures/images/avatar-4.JPG
  49. BIN
      scripts/cypress/fixtures/images/avatar-5.JPG
  50. BIN
      scripts/cypress/fixtures/images/avatar-6.JPG
  51. 5
      scripts/cypress/fixtures/sampleFiles/1.json
  52. 4
      scripts/cypress/fixtures/sampleFiles/2.json
  53. 4
      scripts/cypress/fixtures/sampleFiles/3.json
  54. 4
      scripts/cypress/fixtures/sampleFiles/4.json
  55. 4
      scripts/cypress/fixtures/sampleFiles/5.json
  56. 4
      scripts/cypress/fixtures/sampleFiles/6.json
  57. BIN
      scripts/cypress/fixtures/sampleFiles/Financial Sample.xlsx
  58. 16
      scripts/cypress/fixtures/sampleFiles/iFrame.html
  59. BIN
      scripts/cypress/fixtures/sampleFiles/sample.xlsx
  60. BIN
      scripts/cypress/fixtures/sampleFiles/simple.xlsx
  61. 1250
      scripts/cypress/fixtures/sqlite-sakila/regenFiles/index.js
  62. 854
      scripts/cypress/fixtures/sqlite-sakila/regenFiles/package-lock.json
  63. 14
      scripts/cypress/fixtures/sqlite-sakila/regenFiles/package.json
  64. 6
      scripts/cypress/fixtures/sqlite-sakila/regenFiles/recreate_sakila_sqlite.sh
  65. 45
      scripts/cypress/fixtures/sqlite-sakila/regenFiles/sqlite-sakila-delete-data.sql
  66. 70
      scripts/cypress/fixtures/sqlite-sakila/regenFiles/sqlite-sakila-drop-objects.sql
  67. 231504
      scripts/cypress/fixtures/sqlite-sakila/regenFiles/sqlite-sakila-insert-data.sql
  68. 647
      scripts/cypress/fixtures/sqlite-sakila/regenFiles/sqlite-sakila-schema.sql
  69. BIN
      scripts/cypress/fixtures/sqlite-sakila/sakila.db
  70. 261
      scripts/cypress/integration/common/00_pre_configurations.js
  71. 106
      scripts/cypress/integration/common/1a_table_operations.js
  72. 200
      scripts/cypress/integration/common/1b_table_column_operations.js
  73. 143
      scripts/cypress/integration/common/1c_sql_view.js
  74. 147
      scripts/cypress/integration/common/1d_pg_table_view_drag_drop_reorder.js
  75. 156
      scripts/cypress/integration/common/1d_table_view_drag_drop_reorder.js
  76. 169
      scripts/cypress/integration/common/1e_meta_sync.js
  77. 182
      scripts/cypress/integration/common/1e_pg_meta_sync.js
  78. 118
      scripts/cypress/integration/common/2a_table_with_belongs_to_colulmn.js
  79. 130
      scripts/cypress/integration/common/2b_table_with_m2m_column.js
  80. 198
      scripts/cypress/integration/common/3a_filter_sort_fields_operations.js
  81. 369
      scripts/cypress/integration/common/3b_formula_column.js
  82. 110
      scripts/cypress/integration/common/3c_lookup_column.js
  83. 147
      scripts/cypress/integration/common/3d_rollup_column.js
  84. 296
      scripts/cypress/integration/common/3e_duration_column.js
  85. 380
      scripts/cypress/integration/common/3f_link_to_another_record.js
  86. 95
      scripts/cypress/integration/common/4a_table_view_grid_gallery_form.js
  87. 120
      scripts/cypress/integration/common/4b_table_view_share.js
  88. 406
      scripts/cypress/integration/common/4c_form_view_detailed.js
  89. 107
      scripts/cypress/integration/common/4d_table_view_grid_locked.js
  90. 226
      scripts/cypress/integration/common/4e_form_view_share.js
  91. 477
      scripts/cypress/integration/common/4f_grid_view_share.js
  92. 504
      scripts/cypress/integration/common/4f_pg_grid_view_share.js
  93. 210
      scripts/cypress/integration/common/4g_table_view_expanded_form.js
  94. 541
      scripts/cypress/integration/common/4h_kanban.js
  95. 191
      scripts/cypress/integration/common/4i_survey_form.js
  96. 285
      scripts/cypress/integration/common/5a_user_role.js
  97. 172
      scripts/cypress/integration/common/5b_preview_role.js
  98. 165
      scripts/cypress/integration/common/5c_super_user_role.js
  99. 76
      scripts/cypress/integration/common/6b_downloadCsv.js
  100. 236
      scripts/cypress/integration/common/6c_swagger_api.js
  101. Some files were not shown because too many files have changed in this diff Show More

5
.github/workflows/ci-cd.yml

@ -8,7 +8,6 @@ on:
branches: [develop]
paths:
- "packages/nc-gui/**"
- "scripts/cypress/**"
- "packages/nocodb/**"
- ".github/workflows/ci-cd.yml"
pull_request:
@ -16,7 +15,6 @@ on:
branches: [develop]
paths:
- "packages/nc-gui/**"
- "scripts/cypress/**"
- "packages/nocodb/**"
- ".github/workflows/ci-cd.yml"
@ -60,9 +58,6 @@ jobs:
- name: Install dependencies
working-directory: ./packages/nocodb
run: npm install
- name: setup mysql
working-directory: ./
run: docker-compose -f ./scripts/cypress/docker-compose-cypress.yml up -d
- name: run unit tests
working-directory: ./packages/nocodb
run: npm run test:unit

1
.github/workflows/dco-check.yml

@ -10,7 +10,6 @@ on:
- "packages/nc-lib-gui/**"
- "packages/nc-plugin/**"
- "packages/nocodb/**"
- "scripts/cypress/**"
jobs:
commits_check_job:

7
.github/workflows/release-nocodb.yml

@ -117,12 +117,7 @@ jobs:
secrets:
GH_TOKEN: "${{ secrets.GH_TOKEN }}"
# Change nocodb-sdk back to local path
update-sdk-path:
needs: publish-docs
uses: ./.github/workflows/update-sdk-path.yml
# Sync changes to develop
sync-to-develop:
needs: update-sdk-path
needs: close-issues
uses: ./.github/workflows/sync-to-develop.yml

6
.gitignore vendored

@ -84,12 +84,6 @@ mongod
/packages/nocodb/docker/main.js.LICENSE.txt
/packages/nocodb/noco_log.db
# Cypress
#=========
shared.json
/scripts/Cypress/screenshots
/scripts/exp/
# NC_DBs
#=========
nc_minimal_dbs/

23
markdown/readme/languages/chinese.md

@ -255,29 +255,6 @@ npm run dev
> nocodb/packages/nocodb 包括 nc-lib-gui,它是 npm 源中托管的 nc-gui 的预构建版本。如果您只想修改后端,则可以在本地启动后端后在浏览器中访问 localhost:8000/dashboard
## 本地运行 Cypress 测试
```shell
# 安装依赖 (cypress)
npm install
# 使用 docker compose 运行带有所需数据库的 mysql 数据库
docker-compose -f ./scripts/cypress/docker-compose-cypress.yml up
# 使用以下命令运行后端 api
npm run start:api
# 使用以下命令运行前端 Web UI
npm run start:web
# 等待 3000 和 8080 端口都可用后
# 使用以下命令运行 cypress 测试
npm run cypress:run
# 或运行以下命令以使用 GUI 运行它
npm run cypress:open
```
# 贡献
参见[贡献指南](https://github.com/nocodb/nocodb/blob/master/.github/CONTRIBUTING.md).

24
markdown/readme/languages/german.md

@ -236,30 +236,6 @@ npm run dev
> nocodb/packages/nocodb enthält nc-lib-gui, die entwickelte Version von nc-gui, die in der npm-Registry gehostet wird. Sie können localhost:8000/dashboard im Browser aufrufen, nachdem Sie das Backend lokal gestartet haben, wenn Sie nur das Backend ändern möchten.
## Cypress-Tests lokal ausführen
```shell
# install dependencies (cypress)
npm install
# MySQL-Datenbank mit der benötigten Datenbank mit Docker Compose ausführen
docker-compose -f ./scripts/cypress/docker-compose-cypress.yml up
# Backend API mit folgendem Befehl ausführen
npm run start:api
# Frontend Web-UI mit folgendem Befehl ausführen
npm run start:web
# Warten, bis die beiden Ports 3000 und 8000 verfügbar sind,
# dann Cypress Test mit diesem Befehl ausführen
npm run cypress:run
# Oder diesen Befehl ausführen, um die GUI auszuführen
npm run cypress:open
```
# Beiträge
Siehe [Contribution Guide](https://github.com/nocodb/nocodb/blob/master/.github/CONTRIBUTING.md).

2174
package-lock.json generated

File diff suppressed because it is too large Load Diff

19
package.json

@ -16,10 +16,6 @@
},
"license": "AGPL-3.0-or-later",
"devDependencies": {
"@4tw/cypress-drag-drop": "^2.0.0",
"cypress": "^9.2.0",
"cypress-file-upload": "^5.0.8",
"cypress-iframe": "^1.0.1",
"fs": "0.0.1-security",
"lerna": "^3.20.1",
"husky": "^8.0.0",
@ -39,25 +35,16 @@
"lint:staged:playwright": "cd ./tests/playwright; npx lint-staged; cd -",
"build:common": "cd ./packages/nocodb-sdk; npm install; npm run build",
"install:common": "cd ./packages/nocodb; npm install ../nocodb-sdk; cd ../nc-gui; npm install ../nocodb-sdk",
"start:api": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk; npm install; NC_DISABLE_CACHE=true NC_DISABLE_TELE=true npm run watch:run:cypress",
"start:xcdb-api": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk;npm install; NC_EXPORT_MAX_TIMEOUT=60000 NC_DISABLE_CACHE=true NC_DISABLE_TELE=true NC_INFLECTION=camelize DATABASE_URL=sqlite:../../../scripts/cypress/fixtures/sqlite-sakila/sakila.db npm run watch:run:cypress",
"start:api:cache": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk;npm install; NC_EXPORT_MAX_TIMEOUT=60000 NC_DISABLE_TELE=true npm run watch:run:cypress",
"start:api:cache:pg": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk; npm install; NC_DISABLE_TELE=true npm run watch:run:cypress:pg",
"start:api:cache:pg:cyquick": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk; npm install; NC_DISABLE_TELE=true npm run watch:run:cypress:pg:cyquick",
"start:xcdb-api:cache": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk; npm install; NC_EXPORT_MAX_TIMEOUT=60000 NC_DISABLE_TELE=true NC_INFLECTION=camelize DATABASE_URL=sqlite:../../../scripts/cypress/fixtures/sqlite-sakila/sakila.db npm run watch:run:cypress",
"start:web": "npm run build:common ; cd ./packages/nc-gui; npm install ../nocodb-sdk; npm install; ANT_MESSAGE_DURATION=0.5 npm run dev",
"cypress:run": "cypress run --config-file ./scripts/cypress/cypress.json",
"cypress:open": "cypress open --config-file ./scripts/cypress/cypress.json",
"cypress:clear": "cypress cache clear",
"test:travis": "git log --pretty=format:'%h' -n 1 --skip 1 | xargs lerna run test:travis --since",
"lerna:install": "git log --pretty=format:'%h' -n 1 --skip 1 | xargs lerna bootstrap --ignore nc-cli --since",
"updated:xc-migrator": "lerna run publish --scope xc-migrator && lerna run xc && lerna publish && npm install -f xc-cli",
"doc": "lerna run doc",
"install:local:dep": "cd packages/nc-lib-gui;npm uninstall -S xc-lib;rm package-lock.json; npm i ../../../xc-lib-private; cd ../xc-instant;npm uninstall -S xc-lib xc-lib-gui;npm i ../../../xc-lib-private;npm i ../xc-lib-gui",
"install:npm:dep": "cd packages/nc-lib-gui;npm uninstall -S xc-lib; npm i -S xc-lib@latest; cd ../xc-instant;npm uninstall -S xc-lib xc-lib-gui;npm i -S xc-lib@latest xc-lib-gui@latest;npm i ../xc-lib-gui",
"start:pg": "docker-compose -f ./scripts/cypress/docker-compose-pg.yml up -d",
"stop:pg": "docker-compose -f ./scripts/cypress/docker-compose-pg.yml down",
"prepare": "husky install"
"prepare": "husky install",
"start:pg": "docker-compose -f ./tests/playwright/scripts/docker-compose-pg.yml up -d",
"stop:pg": "docker-compose -f ./tests/playwright/scripts/docker-compose-pg.yml down"
},
"dependencies": {
"express": "^4.18.1",

12
packages/nc-cli/package-lock.json generated

@ -9465,9 +9465,9 @@
}
},
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@ -22773,9 +22773,9 @@
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": {
"brace-expansion": "^1.1.7"
}

2
packages/nc-gui/app.vue

@ -36,7 +36,7 @@ if (typeof window !== 'undefined') {
<template>
<a-config-provider>
<NuxtLayout :name="disableBaseLayout ? false : 'base'">
<NuxtPage :key="key" />
<NuxtPage :key="key" :transition="false" />
</NuxtLayout>
</a-config-provider>
</template>

2
packages/nc-gui/components/cell/MultiSelect.vue

@ -271,8 +271,8 @@ const onTagClick = (e: Event, onClose: Function) => {
class="w-full"
:bordered="false"
clear-icon
show-search
:show-arrow="!readOnly"
:show-search="active || editable"
:open="isOpen && (active || editable)"
:disabled="readOnly"
:class="{ '!ml-[-8px]': readOnly }"

2
packages/nc-gui/components/cell/SingleSelect.vue

@ -189,7 +189,7 @@ const toggleMenu = (e: Event) => {
:disabled="readOnly"
:show-arrow="!readOnly && (active || editable || vModel === null)"
:dropdown-class-name="`nc-dropdown-single-select-cell ${isOpen ? 'active' : ''}`"
:show-search="active || editable"
show-search
@select="isOpen = false"
@keydown.stop
@search="search"

7
packages/nc-gui/components/smartsheet/Gallery.vue

@ -80,7 +80,12 @@ const isRowEmpty = (record: any, col: any) => {
const attachments = (record: any): Attachment[] => {
try {
return coverImageColumn?.title && record.row[coverImageColumn.title] ? JSON.parse(record.row[coverImageColumn.title]) : []
if (coverImageColumn?.title && record.row[coverImageColumn.title]) {
return typeof record.row[coverImageColumn.title] === 'string'
? JSON.parse(record.row[coverImageColumn.title])
: record.row[coverImageColumn.title]
}
return []
} catch (e) {
return []
}

7
packages/nc-gui/components/smartsheet/Kanban.vue

@ -116,7 +116,12 @@ reloadViewDataHook?.on(async () => {
const attachments = (record: any): Attachment[] => {
try {
return coverImageColumn?.title && record.row[coverImageColumn.title] ? JSON.parse(record.row[coverImageColumn.title]) : []
if (coverImageColumn?.title && record.row[coverImageColumn.title]) {
return typeof record.row[coverImageColumn.title] === 'string'
? JSON.parse(record.row[coverImageColumn.title])
: record.row[coverImageColumn.title]
}
return []
} catch (e) {
return []
}

1
packages/nc-gui/components/smartsheet/header/CellIcon.ts

@ -19,7 +19,6 @@ import {
isJSON,
isPercent,
isPhoneNumber,
isPrimary,
isRating,
isSet,
isSingleSelect,

21
packages/nc-gui/components/virtual-cell/Lookup.vue

@ -27,14 +27,6 @@ const meta = inject(MetaInj, ref())
const cellValue = inject(CellValueInj, ref())
const arrValue = computed(() => {
if (!cellValue.value) return []
if (Array.isArray(cellValue.value)) return cellValue.value
return [cellValue.value]
})
const relationColumn = computed(
() =>
meta.value?.columns?.find((c) => c.id === (column.value?.colOptions as LookupType)?.fk_relation_column_id) as
@ -66,6 +58,19 @@ const lookupColumn = computed(
| undefined,
)
const arrValue = computed(() => {
if (!cellValue.value) return []
// if lookup column is Attachment and relation type is Belongs to wrap the value in an array
// since the attachment component expects an array or JSON string array
if (lookupColumn.value?.uidt === UITypes.Attachment && relationColumn.value?.colOptions?.type === RelationTypes.BELONGS_TO)
return [cellValue.value]
if (Array.isArray(cellValue.value)) return cellValue.value
return [cellValue.value]
})
provide(MetaInj, lookupTableMeta)
provide(CellUrlDisableOverlayInj, ref(true))

74
packages/nc-gui/lang/vi.json

@ -16,7 +16,7 @@
"cancel": "Hủy bỏ",
"submit": "Nộp",
"create": "Tạo ra",
"duplicate": "Duplicate",
"duplicate": "Tạo bản sao",
"insert": "Chèn",
"delete": "Xóa bỏ",
"update": "Cập nhật",
@ -56,20 +56,20 @@
"notification": "Thông báo",
"reference": "Thẩm quyền giải quyết",
"function": "Chức năng",
"confirm": "Confirm",
"confirm": "Xác nhận",
"generate": "Generate",
"copy": "Copy",
"misc": "Miscellaneous",
"lock": "Lock",
"unlock": "Unlock",
"credentials": "Credentials",
"help": "Help",
"questions": "Questions",
"reachOut": "Reach out here",
"betaNote": "This feature is currently in beta.",
"copy": "Sao Chép",
"misc": "Các tùy chọn khác",
"lock": "Khoá",
"unlock": "Mở khoá",
"credentials": "Thông tin đăng nhập",
"help": "Trợ giúp",
"questions": "Câu hỏi",
"reachOut": "Tiếp cận sau",
"betaNote": "Tính năng này hiện không được hỗ trợ.",
"moreInfo": "More information can be found here",
"logs": "Logs",
"groupingField": "Grouping Field"
"logs": "Nhật ký",
"groupingField": "Nhóm theo trường"
},
"objects": {
"project": "Dự định",
@ -189,20 +189,20 @@
"headCreateProject": "Tạo dự án |. NOCODB.",
"headLogin": "Đăng nhập |. NOCODB.",
"resetPassword": "Đặt lại mật khẩu của bạn",
"teamAndSettings": "Team & Settings",
"apiDocs": "API Docs",
"importFromAirtable": "Import From Airtable",
"generateToken": "Generate Token",
"APIsAndSupport": "APIs & Support",
"teamAndSettings": "Cài đặt nhóm",
"apiDocs": "Văn bản API",
"importFromAirtable": "Nhập khẩu từ CSDL Airtable",
"generateToken": "Tạo mã Token",
"APIsAndSupport": "APIS và Hỗ trợ",
"helpCenter": "Help center",
"swaggerDocumentation": "Swagger Documentation",
"quickImportFrom": "Quick Import From",
"swaggerDocumentation": "Tài liệu Swagger",
"quickImportFrom": "Kết nhập nhanh dữ liệu từ",
"quickImport": "Quick Import",
"advancedSettings": "Advanced Settings",
"codeSnippet": "Code Snippet"
"advancedSettings": "Cài đặt Nâng cao",
"codeSnippet": "Thư viện mã"
},
"labels": {
"createdBy": "Created By",
"createdBy": "Đã tạo bởi",
"notifyVia": "Thông báo qua",
"projName": "Tên dự án",
"tableName": "Tên bảng.",
@ -220,7 +220,7 @@
"port": "Cổng số",
"username": "tên tài khoản",
"password": "Mật khẩu",
"schemaName": "Schema name",
"schemaName": "Tên lược đồ",
"database": "Cơ sở dữ liệu",
"action": "Hoạt động",
"actions": "Hành động",
@ -233,7 +233,7 @@
"where": "Ở đâu",
"cache": "Cache.",
"chat": "Trò chuyện",
"email": "E-mail",
"email": "Thư điện tử",
"storage": "Kho",
"uiAcl": "UI-ACL",
"models": "Mô hình",
@ -258,7 +258,7 @@
"bookDemo": "Đặt một bản demo miễn phí",
"getAnswered": "Nhận câu hỏi của bạn được trả lời",
"joinDiscord": "Tham gia thông minh",
"joinCommunity": "Join NocoDB Community",
"joinCommunity": "Tham gia cộng đồng",
"joinReddit": "Tham gia /r/NocoDB",
"followNocodb": "Theo dõi NocoDB"
},
@ -268,21 +268,21 @@
"childColumn": "Cột trẻ con.",
"onUpdate": "Trên bản cập nhật",
"onDelete": "Trên xóa",
"account": "Account",
"account": "Tài khoản",
"language": "Language",
"primaryColor": "Primary Color",
"accentColor": "Accent Color",
"customTheme": "Custom Theme",
"primaryColor": "Màu chính",
"accentColor": "Màu phụ",
"customTheme": "Giao diện tùy chỉnh",
"requestDataSource": "Request a data source you need?",
"apiKey": "API Key",
"sharedBase": "Shared Base",
"importData": "Import Data",
"importSecondaryViews": "Import Secondary Views",
"importData": "Kết nhập dữ liệu",
"importSecondaryViews": "Kết nhập Hiển thị thứ hai",
"importRollupColumns": "Import Rollup Columns",
"importLookupColumns": "Import Lookup Columns",
"importAttachmentColumns": "Import Attachment Columns",
"importFormulaColumns": "Import Formula Columns",
"noData": "No Data",
"importAttachmentColumns": "Kết nhập cột tệp đính kèm",
"importFormulaColumns": "Kết nhập cột công thức",
"noData": "Không có dữ liệu",
"goToDashboard": "Go to Dashboard",
"importing": "Importing",
"flattenNested": "Flatten Nested",
@ -426,13 +426,13 @@
"erd": {
"showColumns": "Show Columns",
"showPkAndFk": "Show Primary and Foreign Keys",
"showSqlViews": "Show SQL Views",
"showSqlViews": "Hiển thị truy vấn SQL",
"showMMTables": "Show Many to Many tables",
"showJunctionTableNames": "Show Junction Table Names"
},
"kanban": {
"collapseStack": "Collapse Stack",
"deleteStack": "Delete Stack",
"deleteStack": "Xóa ngăn xếp",
"stackedBy": "Stacked By",
"chooseGroupingField": "Choose a Grouping Field",
"addOrEditStack": "Add / Edit Stack"

2
packages/nc-gui/lang/zh-Hans.json

@ -368,7 +368,7 @@
"setPrimary": "设置为主要值",
"addRow": "添加新行",
"saveRow": "保存行",
"saveAndExit": "Save & Exit",
"saveAndExit": "保存并退出",
"saveAndStay": "Save & Stay",
"insertRow": "插入新行",
"deleteRow": "删除行",

2
packages/nc-gui/layouts/default.vue

@ -20,7 +20,7 @@ export default {
<template>
<div class="w-full h-full">
<Teleport :to="hasSidebar ? '#nc-sidebar-left' : null" :disabled="!hasSidebar">
<slot name="sidebar" />
<slot :key="$route.name" name="sidebar" />
</Teleport>
<a-layout-content>

2
packages/nc-gui/package-lock.json generated

@ -85,7 +85,7 @@
}
},
"../nocodb-sdk": {
"version": "0.99.0",
"version": "0.99.2",
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",

5
packages/nc-gui/pages/account/index/users.vue

@ -1,5 +0,0 @@
<template>
<div class="h-full overflow-y-scroll scrollbar-thin-dull pt-2">
<NuxtPage />
</div>
</template>

30
packages/nc-gui/pages/account/index/users/[[nestedPage]].vue

@ -5,18 +5,20 @@ const { isUIAllowed } = useUIPermission()
</script>
<template>
<template
v-if="
$route.params.nestedPage === 'password-reset' ||
(!isUIAllowed('superAdminUserManagement') && !isUIAllowed('superAdminAppSettings'))
"
>
<LazyAccountResetPassword />
</template>
<template v-else-if="$route.params.nestedPage === 'settings'">
<LazyAccountSignupSettings />
</template>
<template v-else-if="isUIAllowed('superAdminUserManagement')">
<LazyAccountUserList />
</template>
<div class="h-full overflow-y-scroll scrollbar-thin-dull pt-2">
<template
v-if="
$route.params.nestedPage === 'password-reset' ||
(!isUIAllowed('superAdminUserManagement') && !isUIAllowed('superAdminAppSettings'))
"
>
<LazyAccountResetPassword />
</template>
<template v-else-if="$route.params.nestedPage === 'settings'">
<LazyAccountSignupSettings />
</template>
<template v-else-if="isUIAllowed('superAdminUserManagement')">
<LazyAccountUserList />
</template>
</div>
</template>

156
packages/nc-gui/pages/index/index/index.vue

@ -212,92 +212,94 @@ const copyProjectMeta = async () => {
</a-dropdown>
</div>
<Transition name="layout" mode="out-in">
<div v-if="isLoading">
<a-skeleton />
</div>
<a-table
v-else
:custom-row="customRow"
:data-source="filteredProjects"
:pagination="{ position: ['bottomCenter'] }"
:table-layout="md ? 'auto' : 'fixed'"
>
<template #emptyText>
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" :description="$t('labels.noData')" />
</template>
<!--
TODO: bring back transition after fixing the bug with navigation
<Transition name="layout" mode="out-in"> -->
<div v-if="isLoading">
<a-skeleton />
</div>
<!-- Title -->
<a-table-column key="title" :title="$t('general.title')" data-index="title">
<template #default="{ text, record }">
<div class="flex items-center">
<div @click.stop>
<a-menu class="!border-0 !m-0 !p-0" trigger-sub-menu-action="click">
<template v-if="isUIAllowed('projectTheme')">
<a-sub-menu key="theme" popup-class-name="custom-color">
<template #title>
<div
class="color-selector"
:style="{
'background-color': getProjectPrimary(record),
'width': '8px',
'height': '100%',
}"
/>
</template>
<a-table
v-else
:custom-row="customRow"
:data-source="filteredProjects"
:pagination="{ position: ['bottomCenter'] }"
:table-layout="md ? 'auto' : 'fixed'"
>
<template #emptyText>
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" :description="$t('labels.noData')" />
</template>
<!-- Title -->
<a-table-column key="title" :title="$t('general.title')" data-index="title">
<template #default="{ text, record }">
<div class="flex items-center">
<div @click.stop>
<a-menu class="!border-0 !m-0 !p-0" trigger-sub-menu-action="click">
<template v-if="isUIAllowed('projectTheme')">
<a-sub-menu key="theme" popup-class-name="custom-color">
<template #title>
<div
class="color-selector"
:style="{
'background-color': getProjectPrimary(record),
'width': '8px',
'height': '100%',
}"
/>
</template>
<template #expandIcon></template>
<template #expandIcon></template>
<LazyGeneralColorPicker
:model-value="getProjectPrimary(record)"
:colors="projectThemeColors"
:row-size="9"
:advanced="false"
@input="handleProjectColor(record.id, $event)"
/>
<LazyGeneralColorPicker
:model-value="getProjectPrimary(record)"
:colors="projectThemeColors"
:row-size="9"
:advanced="false"
@input="handleProjectColor(record.id, $event)"
/>
<a-sub-menu key="pick-primary">
<template #title>
<div class="nc-project-menu-item group !py-0">
<ClarityColorPickerSolid class="group-hover:text-accent" />
Custom Color
</div>
</template>
<a-sub-menu key="pick-primary">
<template #title>
<div class="nc-project-menu-item group !py-0">
<ClarityColorPickerSolid class="group-hover:text-accent" />
Custom Color
</div>
</template>
<template #expandIcon></template>
<template #expandIcon></template>
<LazyGeneralChromeWrapper @input="handleProjectColor(record.id, $event)" />
</a-sub-menu>
<LazyGeneralChromeWrapper @input="handleProjectColor(record.id, $event)" />
</a-sub-menu>
</template>
</a-menu>
</div>
<div
class="capitalize color-transition group-hover:text-primary !w-[400px] h-full overflow-hidden overflow-ellipsis whitespace-nowrap pl-2"
>
{{ text }}
</div>
</a-sub-menu>
</template>
</a-menu>
</div>
</template>
</a-table-column>
<!-- Actions -->
<a-table-column key="id" :title="$t('labels.actions')" data-index="id">
<template #default="{ text, record }">
<div class="flex items-center gap-2">
<MdiEditOutline v-e="['c:project:edit:rename']" class="nc-action-btn" @click.stop="navigateTo(`/${text}`)" />
<MdiDeleteOutline
class="nc-action-btn"
:data-testid="`delete-project-${record.title}`"
@click.stop="deleteProject(record)"
/>
<div
class="capitalize color-transition group-hover:text-primary !w-[400px] h-full overflow-hidden overflow-ellipsis whitespace-nowrap pl-2"
>
{{ text }}
</div>
</template>
</a-table-column>
</a-table>
</Transition>
</div>
</template>
</a-table-column>
<!-- Actions -->
<a-table-column key="id" :title="$t('labels.actions')" data-index="id">
<template #default="{ text, record }">
<div class="flex items-center gap-2">
<MdiEditOutline v-e="['c:project:edit:rename']" class="nc-action-btn" @click.stop="navigateTo(`/${text}`)" />
<MdiDeleteOutline
class="nc-action-btn"
:data-testid="`delete-project-${record.title}`"
@click.stop="deleteProject(record)"
/>
</div>
</template>
</a-table-column>
</a-table>
<!-- </Transition> -->
</div>
</template>

2
packages/nc-gui/windi.config.ts

@ -20,7 +20,7 @@ export default defineConfig({
},
darkMode: 'class',
safelist: ['text-yellow-500', 'text-sky-500', 'text-red-500'],
plugins: [
scrollbar,
animations,

2
packages/nc-lib-gui/package.json

@ -1,6 +1,6 @@
{
"name": "nc-lib-gui",
"version": "0.99.0",
"version": "0.99.2",
"description": "NocoDB GUI",
"author": {
"name": "NocoDB",

12
packages/nc-plugin/package-lock.json generated

@ -8353,9 +8353,9 @@
}
},
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
@ -18648,9 +18648,9 @@
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"

3
packages/noco-docs/content/en/engineering/translation.md

@ -53,12 +53,11 @@ Refer following articles to get additional details about Crowdin Portal usage
#### GitHub changes
- Update enumeration in `enums.ts` [packages/nc-gui/lib/enums.ts]
- Map JSON path in `a.i18n.ts` [packages/nc-gui/plugins/a.i18n.ts]
- Update array in `6d_language_validation.js` [scripts/cypress/integration/common/6d_language_validation.js]
#### Crowdin changes [admin only]
- Open `NocoDB` project
- Click on `Language` on the home tab
- Select target language, `Update`
- Update array in `tests/playwright/tests/language.spec.ts`
![Screenshot 2022-09-08 at 10 52 59 PM](https://user-images.githubusercontent.com/86527202/189186570-5c1c7cad-6d3f-4937-ab4d-fa7ebe022cb1.png)

12
packages/noco-docs/package-lock.json generated

@ -10047,9 +10047,9 @@
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
},
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@ -24670,9 +24670,9 @@
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": {
"brace-expansion": "^1.1.7"
}

4
packages/nocodb-sdk/package-lock.json generated

@ -1,12 +1,12 @@
{
"name": "nocodb-sdk",
"version": "0.99.0",
"version": "0.99.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "nocodb-sdk",
"version": "0.99.0",
"version": "0.99.2",
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",

2
packages/nocodb-sdk/package.json

@ -1,6 +1,6 @@
{
"name": "nocodb-sdk",
"version": "0.99.0",
"version": "0.99.2",
"description": "NocoDB SDK",
"main": "build/main/index.js",
"typings": "build/main/index.d.ts",

15
packages/nocodb/docker-compose.yml

@ -447,18 +447,3 @@ services:
cp /home/app/tests/sqlite-dump/sakila.db /home/sakila.db
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"#
cd /home/app/ && npm i && npm run test:graphql
xc-cypress-test:
image: node:12.22.1-slim
ports:
- 8080:8080
volumes:
- ./:/home/app
command:
- /bin/bash
- -c
- |
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
cd /home/app/ && npm i && npm run run

357
packages/nocodb/package-lock.json generated

@ -1,12 +1,12 @@
{
"name": "nocodb",
"version": "0.99.0",
"version": "0.99.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "nocodb",
"version": "0.99.0",
"version": "0.99.2",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@google-cloud/storage": "^5.7.2",
@ -64,7 +64,7 @@
"mysql2": "^2.2.5",
"nanoid": "^3.1.20",
"nc-help": "0.2.79",
"nc-lib-gui": "0.99.0",
"nc-lib-gui": "0.99.2",
"nc-plugin": "0.1.2",
"ncp": "^2.0.0",
"nocodb-sdk": "file:../nocodb-sdk",
@ -119,7 +119,7 @@
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"mocha": "^8.1.1",
"mocha": "^10.1.0",
"nodemon": "^2.0.7",
"npm-run-all": "^4.1.5",
"prettier": "^2.7.1",
@ -152,7 +152,7 @@
}
},
"../nocodb-sdk": {
"version": "0.99.0",
"version": "0.99.2",
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",
@ -1629,12 +1629,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
"integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
"dev": true
},
"node_modules/@webassemblyjs/ast": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
@ -7369,15 +7363,6 @@
"graphql": ">=0.8.0"
}
},
"node_modules/growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true,
"engines": {
"node": ">=4.x"
}
},
"node_modules/gtoken": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz",
@ -8473,6 +8458,18 @@
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
},
"node_modules/is-unicode-supported": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
"dev": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-utf8": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
@ -9300,15 +9297,19 @@
"integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw=="
},
"node_modules/log-symbols": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
"integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
"dev": true,
"dependencies": {
"chalk": "^4.0.0"
"chalk": "^4.1.0",
"is-unicode-supported": "^0.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/log-symbols/node_modules/chalk": {
@ -9957,43 +9958,39 @@
}
},
"node_modules/mocha": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz",
"integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==",
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz",
"integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==",
"dev": true,
"dependencies": {
"@ungap/promise-all-settled": "1.1.2",
"ansi-colors": "4.1.1",
"browser-stdout": "1.3.1",
"chokidar": "3.5.1",
"debug": "4.3.1",
"chokidar": "3.5.3",
"debug": "4.3.4",
"diff": "5.0.0",
"escape-string-regexp": "4.0.0",
"find-up": "5.0.0",
"glob": "7.1.6",
"growl": "1.10.5",
"glob": "7.2.0",
"he": "1.2.0",
"js-yaml": "4.0.0",
"log-symbols": "4.0.0",
"minimatch": "3.0.4",
"js-yaml": "4.1.0",
"log-symbols": "4.1.0",
"minimatch": "5.0.1",
"ms": "2.1.3",
"nanoid": "3.1.20",
"serialize-javascript": "5.0.1",
"nanoid": "3.3.3",
"serialize-javascript": "6.0.0",
"strip-json-comments": "3.1.1",
"supports-color": "8.1.1",
"which": "2.0.2",
"wide-align": "1.1.3",
"workerpool": "6.1.0",
"workerpool": "6.2.1",
"yargs": "16.2.0",
"yargs-parser": "20.2.4",
"yargs-unparser": "2.0.0"
},
"bin": {
"_mocha": "bin/_mocha",
"mocha": "bin/mocha"
"mocha": "bin/mocha.js"
},
"engines": {
"node": ">= 10.12.0"
"node": ">= 14.0.0"
},
"funding": {
"type": "opencollective",
@ -10015,50 +10012,6 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"node_modules/mocha/node_modules/chokidar": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
"integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
"dev": true,
"dependencies": {
"anymatch": "~3.1.1",
"braces": "~3.0.2",
"glob-parent": "~5.1.0",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.5.0"
},
"engines": {
"node": ">= 8.10.0"
},
"optionalDependencies": {
"fsevents": "~2.3.1"
}
},
"node_modules/mocha/node_modules/debug": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/mocha/node_modules/debug/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/mocha/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@ -10088,9 +10041,9 @@
}
},
"node_modules/mocha/node_modules/glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
@ -10107,10 +10060,22 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/mocha/node_modules/glob/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/mocha/node_modules/js-yaml": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz",
"integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"dependencies": {
"argparse": "^2.0.1"
@ -10135,21 +10100,30 @@
}
},
"node_modules/mocha/node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
"integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
"brace-expansion": "^2.0.1"
},
"engines": {
"node": "*"
"node": ">=10"
}
},
"node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/mocha/node_modules/nanoid": {
"version": "3.1.20",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz",
"integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==",
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
"integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
"dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
@ -10173,16 +10147,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mocha/node_modules/readdirp": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
"integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
"node_modules/mocha/node_modules/serialize-javascript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
"integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
"dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
"randombytes": "^2.1.0"
}
},
"node_modules/mocha/node_modules/supports-color": {
@ -10632,9 +10603,9 @@
}
},
"node_modules/nc-lib-gui": {
"version": "0.99.0",
"resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.99.0.tgz",
"integrity": "sha512-3wSn6FVTffbPHpV0OWJeaHJ4q9/1y3QM5ltVNf5sIjunN9HEBstvf+EjJ5UGQIad6/CYKGR5DnIi0uUux/ymOA==",
"version": "0.99.2",
"resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.99.2.tgz",
"integrity": "sha512-ghjsyPGHGMMx4zvHqX5mhJhW/DZuZd1pYh8+zm9eru6v/xx6QUQAfcpZrMCfy9MadIY8haiva1khGAZMnY6grQ==",
"dependencies": {
"express": "^4.17.1"
}
@ -17458,9 +17429,9 @@
}
},
"node_modules/workerpool": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz",
"integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
"integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
"dev": true
},
"node_modules/wrap-ansi": {
@ -18954,12 +18925,6 @@
"eslint-visitor-keys": "^2.0.0"
}
},
"@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
"integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
"dev": true
},
"@webassemblyjs/ast": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
@ -23542,12 +23507,6 @@
"integrity": "sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg==",
"requires": {}
},
"growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true
},
"gtoken": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz",
@ -24325,6 +24284,12 @@
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
},
"is-unicode-supported": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
"dev": true
},
"is-utf8": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
@ -24989,12 +24954,13 @@
"integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw=="
},
"log-symbols": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
"integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
"dev": true,
"requires": {
"chalk": "^4.0.0"
"chalk": "^4.1.0",
"is-unicode-supported": "^0.1.0"
},
"dependencies": {
"chalk": {
@ -25534,33 +25500,29 @@
}
},
"mocha": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz",
"integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==",
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz",
"integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==",
"dev": true,
"requires": {
"@ungap/promise-all-settled": "1.1.2",
"ansi-colors": "4.1.1",
"browser-stdout": "1.3.1",
"chokidar": "3.5.1",
"debug": "4.3.1",
"chokidar": "3.5.3",
"debug": "4.3.4",
"diff": "5.0.0",
"escape-string-regexp": "4.0.0",
"find-up": "5.0.0",
"glob": "7.1.6",
"growl": "1.10.5",
"glob": "7.2.0",
"he": "1.2.0",
"js-yaml": "4.0.0",
"log-symbols": "4.0.0",
"minimatch": "3.0.4",
"js-yaml": "4.1.0",
"log-symbols": "4.1.0",
"minimatch": "5.0.1",
"ms": "2.1.3",
"nanoid": "3.1.20",
"serialize-javascript": "5.0.1",
"nanoid": "3.3.3",
"serialize-javascript": "6.0.0",
"strip-json-comments": "3.1.1",
"supports-color": "8.1.1",
"which": "2.0.2",
"wide-align": "1.1.3",
"workerpool": "6.1.0",
"workerpool": "6.2.1",
"yargs": "16.2.0",
"yargs-parser": "20.2.4",
"yargs-unparser": "2.0.0"
@ -25578,39 +25540,6 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"chokidar": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
"integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
"dev": true,
"requires": {
"anymatch": "~3.1.1",
"braces": "~3.0.2",
"fsevents": "~2.3.1",
"glob-parent": "~5.1.0",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.5.0"
}
},
"debug": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true,
"requires": {
"ms": "2.1.2"
},
"dependencies": {
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
}
}
},
"escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@ -25628,9 +25557,9 @@
}
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
@ -25639,12 +25568,23 @@
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"dependencies": {
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
}
}
},
"js-yaml": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz",
"integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"requires": {
"argparse": "^2.0.1"
@ -25660,18 +25600,29 @@
}
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
"integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
"brace-expansion": "^2.0.1"
},
"dependencies": {
"brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0"
}
}
}
},
"nanoid": {
"version": "3.1.20",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz",
"integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==",
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
"integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
"dev": true
},
"p-locate": {
@ -25683,13 +25634,13 @@
"p-limit": "^3.0.2"
}
},
"readdirp": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
"integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
"serialize-javascript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
"integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
"dev": true,
"requires": {
"picomatch": "^2.2.1"
"randombytes": "^2.1.0"
}
},
"supports-color": {
@ -26064,9 +26015,9 @@
}
},
"nc-lib-gui": {
"version": "0.99.0",
"resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.99.0.tgz",
"integrity": "sha512-3wSn6FVTffbPHpV0OWJeaHJ4q9/1y3QM5ltVNf5sIjunN9HEBstvf+EjJ5UGQIad6/CYKGR5DnIi0uUux/ymOA==",
"version": "0.99.2",
"resolved": "https://registry.npmjs.org/nc-lib-gui/-/nc-lib-gui-0.99.2.tgz",
"integrity": "sha512-ghjsyPGHGMMx4zvHqX5mhJhW/DZuZd1pYh8+zm9eru6v/xx6QUQAfcpZrMCfy9MadIY8haiva1khGAZMnY6grQ==",
"requires": {
"express": "^4.17.1"
}
@ -31434,9 +31385,9 @@
}
},
"workerpool": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz",
"integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
"integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
"dev": true
},
"wrap-ansi": {

13
packages/nocodb/package.json

@ -1,6 +1,6 @@
{
"name": "nocodb",
"version": "0.99.0",
"version": "0.99.2",
"description": "NocoDB Backend",
"main": "dist/bundle.js",
"author": {
@ -37,11 +37,8 @@
"watch:build": "nodemon -e ts,js -w ./src -x npm run build",
"watch:run": "cross-env NC_DISABLE_TELE1=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"",
"watch:run:playwright": "rm -f ./test_noco.db; cross-env DATABASE_URL=sqlite:./test_noco.db PLAYWRIGHT_TEST=true NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/testDocker --log-error --project tsconfig.json\"",
"watch:run:playwright:quick": "rm -f ./test_noco.db; cp ../../scripts/cypress/fixtures/quickTest/noco_0_91_7.db ./test_noco.db; cross-env DATABASE_URL=sqlite:./test_noco.db NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"",
"watch:run:playwright:pg:cyquick": "rm -f ./test_noco.db; cp ../../scripts/cypress/fixtures/quickTest/noco_0_91_7.db ./test_noco.db; cross-env NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG_CyQuick.ts --log-error --project tsconfig.json\"",
"watch:run:cypress": "cross-env EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"",
"watch:run:cypress:pg": "cross-env EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG --log-error --project tsconfig.json\"",
"watch:run:cypress:pg:cyquick": "cross-env EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG_CyQuick.ts --log-error --project tsconfig.json\"",
"watch:run:playwright:quick": "rm -f ./test_noco.db; cp ../../tests/playwright/fixtures/noco_0_91_7.db ./test_noco.db; cross-env DATABASE_URL=sqlite:./test_noco.db NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"",
"watch:run:playwright:pg:cyquick": "rm -f ./test_noco.db; cp ../../tests/playwright/fixtures/noco_0_91_7.db ./test_noco.db; cross-env NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG_CyQuick.ts --log-error --project tsconfig.json\"",
"watch:run:mysql": "cross-env NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunMysql --log-error --project tsconfig.json\"",
"watch:run:pg": "cross-env NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG --log-error --project tsconfig.json\"",
"run": "ts-node src/run/docker",
@ -107,7 +104,7 @@
"mysql2": "^2.2.5",
"nanoid": "^3.1.20",
"nc-help": "0.2.79",
"nc-lib-gui": "0.99.0",
"nc-lib-gui": "0.99.2",
"nc-plugin": "0.1.2",
"ncp": "^2.0.0",
"nocodb-sdk": "file:../nocodb-sdk",
@ -162,7 +159,7 @@
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"mocha": "^8.1.1",
"mocha": "^10.1.0",
"nodemon": "^2.0.7",
"npm-run-all": "^4.1.5",
"prettier": "^2.7.1",

120
packages/nocodb/src/lib/db/sql-client/lib/mssql/MssqlClient.ts

@ -356,12 +356,14 @@ class MssqlClient extends KnexClient {
try {
/** ************** START : create _evolution table if not exists *************** */
const exists = await this.sqlClient.schema.withSchema(this.schema).hasTable(args.tn);
const exists = await this.sqlClient.schema
.withSchema(this.schema)
.hasTable(args.tn);
if (!exists) {
await this.sqlClient.schema.withSchema(this.schema).createTable(
args.tn,
function (table) {
await this.sqlClient.schema
.withSchema(this.schema)
.createTable(args.tn, function (table) {
table.increments();
table.string('title').notNullable();
table.string('titleDown').nullable();
@ -371,8 +373,7 @@ class MssqlClient extends KnexClient {
table.integer('status').nullable();
table.dateTime('created');
table.timestamps();
}
);
});
log.debug('Table created:', `${this.getTnPath(args.tn)}`);
} else {
log.debug(`${this.getTnPath(args.tn)} tables exists`);
@ -394,7 +395,9 @@ class MssqlClient extends KnexClient {
log.api(`${_func}:args:`, args);
try {
result.data.value = await this.sqlClient.schema.withSchema(this.schema).hasTable(args.tn);
result.data.value = await this.sqlClient.schema
.withSchema(this.schema)
.hasTable(args.tn);
} catch (e) {
log.ppe(e, _func);
throw e;
@ -1343,8 +1346,9 @@ class MssqlClient extends KnexClient {
log.api(`${func}:args:`, args);
// `DROP TRIGGER ${args.trigger_name}`
try {
const query = `${this.querySeparator()}DROP TRIGGER IF EXISTS ${args.trigger_name
}`;
const query = `${this.querySeparator()}DROP TRIGGER IF EXISTS ${
args.trigger_name
}`;
await this.sqlClient.raw(query);
@ -1474,7 +1478,8 @@ class MssqlClient extends KnexClient {
{
sql:
this.querySeparator() +
`DROP FUNCTION IF EXISTS ${args.function_name
`DROP FUNCTION IF EXISTS ${
args.function_name
};${this.querySeparator()}\n${args.create_function}`,
},
],
@ -1482,7 +1487,8 @@ class MssqlClient extends KnexClient {
{
sql:
this.querySeparator() +
`DROP FUNCTION IF EXISTS ${args.function_name
`DROP FUNCTION IF EXISTS ${
args.function_name
};${this.querySeparator()} ${args.oldCreateFunction}`,
},
],
@ -1554,7 +1560,8 @@ class MssqlClient extends KnexClient {
{
sql:
this.querySeparator() +
`DROP PROCEDURE IF EXISTS ${args.procedure_name
`DROP PROCEDURE IF EXISTS ${
args.procedure_name
};${this.querySeparator()}\n${args.create_procedure}`,
},
],
@ -1562,7 +1569,8 @@ class MssqlClient extends KnexClient {
{
sql:
this.querySeparator() +
`DROP PROCEDURE IF EXISTS ${args.procedure_name
`DROP PROCEDURE IF EXISTS ${
args.procedure_name
};${this.querySeparator()}${args.oldCreateProcedure}`,
},
],
@ -1627,7 +1635,8 @@ class MssqlClient extends KnexClient {
try {
// await this.sqlClient.raw(`DROP TRIGGER ${args.trigger_name}`);
await this.sqlClient.raw(
`ALTER TRIGGER ${args.trigger_name} ON ${this.getTnPath(args.tn)} \n${args.timing
`ALTER TRIGGER ${args.trigger_name} ON ${this.getTnPath(args.tn)} \n${
args.timing
} ${args.event}\n AS\n${args.statement}`
);
@ -1712,7 +1721,8 @@ class MssqlClient extends KnexClient {
{
sql:
this.querySeparator() +
`DROP VIEW ${args.view_name} ; ${this.querySeparator()}${args.oldViewDefination
`DROP VIEW ${args.view_name} ; ${this.querySeparator()}${
args.oldViewDefination
};`,
},
],
@ -1807,7 +1817,10 @@ class MssqlClient extends KnexClient {
const downStatement =
this.querySeparator() +
this.sqlClient.schema.withSchema(this.schema).dropTable(args.table).toString();
this.sqlClient.schema
.withSchema(this.schema)
.dropTable(args.table)
.toString();
this.emit(`Success : ${upQuery}`);
@ -1849,7 +1862,15 @@ class MssqlClient extends KnexClient {
BEGIN
SET NOCOUNT ON;
UPDATE ?? Set ?? = GetDate() WHERE ?? in (SELECT ?? FROM Inserted)
END;`, [triggerName, this.getTnPath(args.table_name), this.getTnPath(args.table_name), column.column_name, pk.column_name, pk.column_name]
END;`,
[
triggerName,
this.getTnPath(args.table_name),
this.getTnPath(args.table_name),
column.column_name,
pk.column_name,
pk.column_name,
]
);
upQuery += triggerCreateQuery;
@ -2051,7 +2072,10 @@ class MssqlClient extends KnexClient {
/** ************** create up & down statements *************** */
const upStatement =
this.querySeparator() +
this.sqlClient.schema.withSchema(this.schema).dropTable(args.tn).toString();
this.sqlClient.schema
.withSchema(this.schema)
.dropTable(args.tn)
.toString();
let downQuery = this.querySeparator() + this.createTable(args.tn, args);
this.emit(`Success : ${upStatement}`);
@ -2065,8 +2089,9 @@ class MssqlClient extends KnexClient {
for (const relation of relationsList) {
downQuery +=
this.querySeparator() +
(await this.sqlClient.withSchema(this.schema).schema
.table(relation.tn, (table) => {
(await this.sqlClient
.withSchema(this.schema)
.schema.table(relation.tn, (table) => {
table = table
.foreign(relation.cn, null)
.references(relation.rcn)
@ -2109,7 +2134,8 @@ class MssqlClient extends KnexClient {
)) {
downQuery +=
this.querySeparator() +
this.sqlClient.schema.withSchema(this.schema)
this.sqlClient.schema
.withSchema(this.schema)
.table(tn, function (table) {
if (non_unique) {
table.index(columns, key_name);
@ -2154,19 +2180,22 @@ class MssqlClient extends KnexClient {
try {
const self = this;
await this.sqlClient.schema.table(this.getTnPath(args.childTable), function (table) {
table = table
.foreign(args.childColumn, foreignKeyName)
.references(args.parentColumn)
.on(self.getTnPath(args.parentTable));
if (args.onUpdate) {
table = table.onUpdate(args.onUpdate);
}
if (args.onDelete) {
table = table.onDelete(args.onDelete);
await this.sqlClient.schema.table(
this.getTnPath(args.childTable),
function (table) {
table = table
.foreign(args.childColumn, foreignKeyName)
.references(args.parentColumn)
.on(self.getTnPath(args.parentTable));
if (args.onUpdate) {
table = table.onUpdate(args.onUpdate);
}
if (args.onDelete) {
table = table.onDelete(args.onDelete);
}
}
});
);
const upStatement =
this.querySeparator() +
@ -2227,9 +2256,12 @@ class MssqlClient extends KnexClient {
try {
const self = this;
await this.sqlClient.schema.table(this.getTnPath(args.childTable), function (table) {
table.dropForeign(args.childColumn, foreignKeyName);
});
await this.sqlClient.schema.table(
this.getTnPath(args.childTable),
function (table) {
table.dropForeign(args.childColumn, foreignKeyName);
}
);
const upStatement =
this.querySeparator() +
@ -2557,10 +2589,10 @@ class MssqlClient extends KnexClient {
query += n.ai ? ' IDENTITY(1,1)' : ' ';
query += defaultValue
? this.genQuery(
` CONSTRAINT ?? DEFAULT ${defaultValue}`,
[`DF_${t}_${n.cn}`],
shouldSanitize
)
` CONSTRAINT ?? DEFAULT ${defaultValue}`,
[`DF_${t}_${n.cn}`],
shouldSanitize
)
: '';
if (defaultValue) {
n.default_constraint_name = `DF_${t}_${n.cn}`;
@ -2572,10 +2604,10 @@ class MssqlClient extends KnexClient {
query += n.ai ? ' IDENTITY(1,1)' : ' ';
query += defaultValue
? this.genQuery(
` CONSTRAINT ?? DEFAULT ${defaultValue}`,
[`DF_${t}_${n.cn}`],
shouldSanitize
)
` CONSTRAINT ?? DEFAULT ${defaultValue}`,
[`DF_${t}_${n.cn}`],
shouldSanitize
)
: ' ';
// shouldSanitize = false;
query = this.genQuery(

41
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts

@ -158,9 +158,10 @@ class BaseModelSqlv2 {
qb.orderBy(this.model.primaryKey.column_name);
}
const data = await qb.first();
let data = await qb.first();
if (data) {
data = this.convertAttachmentType(data);
const proto = await this.getProto();
data.__proto__ = proto;
}
@ -365,8 +366,7 @@ class BaseModelSqlv2 {
qb.groupBy(args.column_name);
if (sorts) await sortV2(sorts, qb, this.dbDriver);
applyPaginate(qb, rest);
const data = await qb;
return data;
return this.convertAttachmentType(await qb);
}
async multipleHmList({ colId, ids }, args: { limit?; offset? } = {}) {
@ -672,10 +672,10 @@ class BaseModelSqlv2 {
);
let children = await this.extractRawQueryAndExec(finalQb);
children = this.convertAttachmentType(children);
if (this.isMySQL) {
children = children[0];
}
children = this.convertAttachmentType(children);
const proto = await (
await Model.getBaseModelSQL({
id: rtnId,
@ -967,8 +967,8 @@ class BaseModelSqlv2 {
applyPaginate(qb, rest);
const proto = await childModel.getProto();
const data = await qb;
let data = await qb;
data = this.convertAttachmentType(data);
return data.map((c) => {
c.__proto__ = proto;
return c;
@ -2617,7 +2617,9 @@ class BaseModelSqlv2 {
const proto = await this.getProto();
const result = (await groupedQb)?.map((d) => {
let data = await groupedQb;
data = this.convertAttachmentType(data);
const result = data?.map((d) => {
d.__proto__ = proto;
return d;
});
@ -2735,6 +2737,15 @@ class BaseModelSqlv2 {
: await this.dbDriver.raw(query);
}
private _convertAttachmentType(attachmentColumns, d) {
attachmentColumns.forEach((col) => {
if (d[col.title] && typeof d[col.title] === 'string') {
d[col.title] = JSON.parse(d[col.title]);
}
});
return d;
}
private convertAttachmentType(data) {
// attachment is stored in text and parse in UI
// convertAttachmentType is used to convert the response in string to array of object in API response
@ -2743,17 +2754,13 @@ class BaseModelSqlv2 {
(c) => c.uidt === UITypes.Attachment
);
if (attachmentColumns.length) {
if (!Array.isArray(data)) {
data = [data];
if (Array.isArray(data)) {
data = data.map((d) =>
this._convertAttachmentType(attachmentColumns, d)
);
} else {
this._convertAttachmentType(attachmentColumns, data);
}
data = data.map((d) => {
attachmentColumns.forEach((col) => {
if (d[col.title] && typeof d[col.title] === 'string') {
d[col.title] = JSON.parse(d[col.title]);
}
});
return d;
});
}
}
return data;

7
packages/nocodb/src/lib/meta/api/swagger/helpers/getSwaggerColumnMetas.ts

@ -40,6 +40,12 @@ export default async (
case UITypes.Rollup:
field.type = 'number';
break;
case UITypes.Attachment:
field.type = 'array';
field.items = {
$ref: `#/components/schemas/Attachment`,
};
break;
default:
field.virtual = false;
SwaggerTypes.setSwaggerType(c, field, dbType);
@ -58,4 +64,5 @@ export interface SwaggerColumn {
virtual?: boolean;
$ref?: any;
column: Column;
items?: any;
}

21
packages/nocodb/src/lib/meta/api/swagger/helpers/swagger-base.json

@ -34,6 +34,27 @@
}
}
},
"Attachment": {
"title": "Attachment",
"type": "object",
"properties": {
"mimetype": {
"type": "string"
},
"size": {
"type": "integer"
},
"title": {
"type": "string"
},
"url": {
"type": "string"
},
"icon": {
"type": "string"
}
}
},
"Groupby": {
"title": "Groupby",
"type": "object",

9
packages/nocodb/src/lib/meta/helpers/populateSamplePayload.ts

@ -94,7 +94,14 @@ async function getSampleColumnValue(column: Column): Promise<any> {
break;
case UITypes.Attachment:
{
return '[{"url":"https://nocodb.com/dummy.png","title":"image.png","mimetype":"image/png","size":0}]';
return [
{
url: 'https://nocodb.com/dummy.png',
title: 'image.png',
mimetype: 'image/png',
size: 0,
},
];
}
break;
case UITypes.Checkbox:

6
packages/nocodb/src/lib/models/SelectOption.ts

@ -77,9 +77,9 @@ export default class SelectOption {
return options?.length
? {
options: options.map(
({ created_at, updated_at, ...c }) => new SelectOption(c)
),
options: options
.map(({ created_at, updated_at, ...c }) => new SelectOption(c))
.sort((x, y) => x.order - y.order),
}
: null;
}

53
scripts/cypress/cypress.json

@ -1,53 +0,0 @@
{
"baseUrl": "http://localhost:3000/",
"testFiles": [
"test/restTableOps.js",
"test/restViews.js",
"test/restRoles.js",
"test/restMisc.js",
"test/xcdb-restTableOps.js",
"test/xcdb-restViews.js",
"test/xcdb-restRoles.js",
"test/xcdb-restMisc.js",
"test/pg-restTableOps.js",
"test/pg-restViews.js",
"test/pg-restRoles.js",
"test/pg-restMisc.js",
"test/quickTest.js",
"test/db-independent.js"
],
"defaultCommandTimeout": 13000,
"pageLoadTimeout": 600000,
"viewportWidth": 1980,
"viewportHeight": 1000,
"video": false,
"retries": 0,
"screenshotOnRunFailure": true,
"numTestsKeptInMemory": 0,
"experimentalInteractiveRunEvents": true,
"env": {
"testMode": [
{ "apiType": "rest", "dbType": "xcdb" },
{ "apiType": "rest", "dbType": "mysql" },
{ "apiType": "rest", "dbType": "postgres" }
],
"db": {
"host": "127.0.0.1",
"user": "root",
"password": "password"
},
"screenshot": false,
"airtable": {
"apiKey": "keyn1MR87qgyUsYg4",
"sharedBase": "https://airtable.com/shr4z0qmh6dg5s3eB"
}
},
"fixturesFolder": "scripts/cypress/fixtures",
"integrationFolder": "scripts/cypress/integration",
"pluginsFile": "scripts/cypress/plugins/index.js",
"screenshotsFolder": "scripts/cypress/screenshots",
"videosFolder": "scripts/cypress/videos",
"downloadsFolder": "scripts/cypress/downloads",
"supportFile": "scripts/cypress/support/index.js",
"chromeWebSecurity": false
}

67
scripts/cypress/docker-compose-cypress.yml

@ -1,67 +0,0 @@
version: "3.5"
# https://github.com/docker-library/mysql/issues/149
# disabling default sql-mode set to only_full_group_by
services:
xc-mysql-sakila-db:
network_mode: host
image: mysql:8.0
command: mysqld --sql_mode=""
restart: always
environment:
MYSQL_ROOT_PASSWORD: password
volumes:
- ../../packages/nocodb/tests/mysql-sakila-db:/docker-entrypoint-initdb.d
# xc-cypress-nocodb:
# network_mode: host
# image: node:14-alpine
# environment:
# - EE=true
# volumes:
# - ./packages/nocodb:/home/app
# command:
# - /bin/sh
# - -c
# - |
# echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
# # cp -r /home/app1/ /home/app/
# rm /home/app/package-lock.json
# rm /home/app/noco.db
# cd /home/app/ && npm i && EE=true npm run run
# # cd /home/app/ && npm i && EE=true npm run watch:run
# xc-cypress-nc-gui:
# network_mode: host
# image: node:14-alpine
# environment:
# - HOST=0.0.0.0
# - PORT=3000
# - EE=true
# volumes:
# - ./packages/nc-gui:/home/app
# - ./packages/nc-lib-gui:/home/nc-lib-gui
# command:
# - /bin/sh
# - -c
# - |
# echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
# apk --update --no-cache add git
# # cp -r /home/app1/ /home/app/
# rm /home/app/package-lock.json
# cd /home/app/ && npm i && npm run dev
# # cd /home/app/ && npm i && NODE_ENV=development npm run build && npm start

17
scripts/cypress/docker-compose-pg-cy-quick.yml

@ -1,17 +0,0 @@
version: "2.1"
services:
pg96:
image: postgres:9.6
restart: always
environment:
POSTGRES_PASSWORD: password
ports:
- 5432:5432
volumes:
- ../../packages/nocodb/tests/pg-cy-quick:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5

17
scripts/cypress/docker-compose-pg.yml

@ -1,17 +0,0 @@
version: "2.1"
services:
pg96:
image: postgres:9.6
restart: always
environment:
POSTGRES_PASSWORD: password
ports:
- 5432:5432
volumes:
- ../../packages/nocodb/tests/pg-sakila-db:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5

5
scripts/cypress/fixtures/example.json

@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

BIN
scripts/cypress/fixtures/images/avatar-1.JPG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

BIN
scripts/cypress/fixtures/images/avatar-2.JPG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

BIN
scripts/cypress/fixtures/images/avatar-3.JPG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

BIN
scripts/cypress/fixtures/images/avatar-4.JPG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
scripts/cypress/fixtures/images/avatar-5.JPG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

BIN
scripts/cypress/fixtures/images/avatar-6.JPG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

5
scripts/cypress/fixtures/sampleFiles/1.json

@ -1,5 +0,0 @@
{
"country": "Afghanistan",
"city": ["Kabul"]
}

4
scripts/cypress/fixtures/sampleFiles/2.json

@ -1,4 +0,0 @@
{
"country": "Algeria",
"city": ["Batna", "Bchar", "Skikda"]
}

4
scripts/cypress/fixtures/sampleFiles/3.json

@ -1,4 +0,0 @@
{
"country": "Americal Samoa",
"city": ["Tafuna"]
}

4
scripts/cypress/fixtures/sampleFiles/4.json

@ -1,4 +0,0 @@
{
"country": "Angola",
"city": ["Benguela", "Namibe"]
}

4
scripts/cypress/fixtures/sampleFiles/5.json

@ -1,4 +0,0 @@
{
"country": "Anguilla",
"city": ["South Hill"]
}

4
scripts/cypress/fixtures/sampleFiles/6.json

@ -1,4 +0,0 @@
{
"country": "Argentina",
"city": ["Almirante Brown", "Avellaneda", "Beha Blanca", "Crdoba"]
}

BIN
scripts/cypress/fixtures/sampleFiles/Financial Sample.xlsx

Binary file not shown.

16
scripts/cypress/fixtures/sampleFiles/iFrame.html

@ -1,16 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<iframe
class="nc-embed"
src="http://localhost:3000/#/nc/base/7d4b551c-b5e0-41c9-a87b-f3984c21d2c7?embed"
frameborder="0"
width="100%"
height="700"
style="background: transparent; "></iframe>
</body>
</html>

BIN
scripts/cypress/fixtures/sampleFiles/sample.xlsx

Binary file not shown.

BIN
scripts/cypress/fixtures/sampleFiles/simple.xlsx

Binary file not shown.

1250
scripts/cypress/fixtures/sqlite-sakila/regenFiles/index.js

File diff suppressed because it is too large Load Diff

854
scripts/cypress/fixtures/sqlite-sakila/regenFiles/package-lock.json generated

@ -1,854 +0,0 @@
{
"name": "nc-xcdb",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
"integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
"optional": true
},
"@mapbox/node-pre-gyp": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz",
"integrity": "sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==",
"requires": {
"detect-libc": "^2.0.0",
"https-proxy-agent": "^5.0.0",
"make-dir": "^3.1.0",
"node-fetch": "^2.6.7",
"nopt": "^5.0.0",
"npmlog": "^5.0.1",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.11"
}
},
"@npmcli/fs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz",
"integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==",
"optional": true,
"requires": {
"@gar/promisify": "^1.0.1",
"semver": "^7.3.5"
}
},
"@npmcli/move-file": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz",
"integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
"optional": true,
"requires": {
"mkdirp": "^1.0.4",
"rimraf": "^3.0.2"
}
},
"@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
"optional": true
},
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"requires": {
"debug": "4"
}
},
"agentkeepalive": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz",
"integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==",
"optional": true,
"requires": {
"debug": "^4.1.0",
"depd": "^1.1.2",
"humanize-ms": "^1.2.1"
}
},
"aggregate-error": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
"optional": true,
"requires": {
"clean-stack": "^2.0.0",
"indent-string": "^4.0.0"
}
},
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"aproba": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
},
"are-we-there-yet": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^3.6.0"
}
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"cacache": {
"version": "15.3.0",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz",
"integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==",
"optional": true,
"requires": {
"@npmcli/fs": "^1.0.0",
"@npmcli/move-file": "^1.0.1",
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"glob": "^7.1.4",
"infer-owner": "^1.0.4",
"lru-cache": "^6.0.0",
"minipass": "^3.1.1",
"minipass-collect": "^1.0.2",
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.2",
"mkdirp": "^1.0.3",
"p-map": "^4.0.0",
"promise-inflight": "^1.0.1",
"rimraf": "^3.0.2",
"ssri": "^8.0.1",
"tar": "^6.0.2",
"unique-filename": "^1.1.1"
}
},
"chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
},
"clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
"optional": true
},
"color-support": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"requires": {
"ms": "2.1.2"
}
},
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"optional": true
},
"detect-libc": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
"integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w=="
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"encoding": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"optional": true,
"requires": {
"iconv-lite": "^0.6.2"
}
},
"env-paths": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"optional": true
},
"err-code": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
"optional": true
},
"fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"requires": {
"minipass": "^3.0.0"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"gauge": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
"requires": {
"aproba": "^1.0.3 || ^2.0.0",
"color-support": "^1.1.2",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.1",
"object-assign": "^4.1.1",
"signal-exit": "^3.0.0",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"wide-align": "^1.1.2"
}
},
"glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"optional": true
},
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"http-cache-semantics": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
"optional": true
},
"http-proxy-agent": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
"integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
"optional": true,
"requires": {
"@tootallnate/once": "1",
"agent-base": "6",
"debug": "4"
}
},
"https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"requires": {
"agent-base": "6",
"debug": "4"
}
},
"humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
"optional": true,
"requires": {
"ms": "^2.0.0"
}
},
"iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"optional": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
},
"imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"optional": true
},
"indent-string": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"optional": true
},
"infer-owner": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
"integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
"optional": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"optional": true
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"is-lambda": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
"integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=",
"optional": true
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"optional": true
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"requires": {
"semver": "^6.0.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
"make-fetch-happen": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz",
"integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==",
"optional": true,
"requires": {
"agentkeepalive": "^4.1.3",
"cacache": "^15.2.0",
"http-cache-semantics": "^4.1.0",
"http-proxy-agent": "^4.0.1",
"https-proxy-agent": "^5.0.0",
"is-lambda": "^1.0.1",
"lru-cache": "^6.0.0",
"minipass": "^3.1.3",
"minipass-collect": "^1.0.2",
"minipass-fetch": "^1.3.2",
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.4",
"negotiator": "^0.6.2",
"promise-retry": "^2.0.1",
"socks-proxy-agent": "^6.0.0",
"ssri": "^8.0.0"
}
},
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minipass": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz",
"integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==",
"requires": {
"yallist": "^4.0.0"
}
},
"minipass-collect": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
"integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
"optional": true,
"requires": {
"minipass": "^3.0.0"
}
},
"minipass-fetch": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz",
"integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==",
"optional": true,
"requires": {
"encoding": "^0.1.12",
"minipass": "^3.1.0",
"minipass-sized": "^1.0.3",
"minizlib": "^2.0.0"
}
},
"minipass-flush": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
"integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
"optional": true,
"requires": {
"minipass": "^3.0.0"
}
},
"minipass-pipeline": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
"integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
"optional": true,
"requires": {
"minipass": "^3.0.0"
}
},
"minipass-sized": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
"integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
"optional": true,
"requires": {
"minipass": "^3.0.0"
}
},
"minizlib": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"requires": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
}
},
"mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"optional": true
},
"node-addon-api": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
"integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="
},
"node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": {
"whatwg-url": "^5.0.0"
}
},
"node-gyp": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz",
"integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==",
"optional": true,
"requires": {
"env-paths": "^2.2.0",
"glob": "^7.1.4",
"graceful-fs": "^4.2.6",
"make-fetch-happen": "^9.1.0",
"nopt": "^5.0.0",
"npmlog": "^6.0.0",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.2",
"which": "^2.0.2"
},
"dependencies": {
"are-we-there-yet": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz",
"integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==",
"optional": true,
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^3.6.0"
}
},
"gauge": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
"integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
"optional": true,
"requires": {
"aproba": "^1.0.3 || ^2.0.0",
"color-support": "^1.1.3",
"console-control-strings": "^1.1.0",
"has-unicode": "^2.0.1",
"signal-exit": "^3.0.7",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"wide-align": "^1.1.5"
}
},
"npmlog": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
"integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
"optional": true,
"requires": {
"are-we-there-yet": "^3.0.0",
"console-control-strings": "^1.1.0",
"gauge": "^4.0.3",
"set-blocking": "^2.0.0"
}
}
}
},
"nopt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
"requires": {
"abbrev": "1"
}
},
"npmlog": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
"requires": {
"are-we-there-yet": "^2.0.0",
"console-control-strings": "^1.1.0",
"gauge": "^3.0.0",
"set-blocking": "^2.0.0"
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"p-map": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"optional": true,
"requires": {
"aggregate-error": "^3.0.0"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"promise-inflight": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"optional": true
},
"promise-retry": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
"integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
"optional": true,
"requires": {
"err-code": "^2.0.2",
"retry": "^0.12.0"
}
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
"optional": true
},
"rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"requires": {
"glob": "^7.1.3"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"optional": true
},
"semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^6.0.0"
}
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
},
"smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"optional": true
},
"socks": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz",
"integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==",
"optional": true,
"requires": {
"ip": "^1.1.5",
"smart-buffer": "^4.2.0"
}
},
"socks-proxy-agent": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.0.tgz",
"integrity": "sha512-wWqJhjb32Q6GsrUqzuFkukxb/zzide5quXYcMVpIjxalDBBYy2nqKCFQ/9+Ie4dvOYSQdOk3hUlZSdzZOd3zMQ==",
"optional": true,
"requires": {
"agent-base": "^6.0.2",
"debug": "^4.3.3",
"socks": "^2.6.2"
}
},
"sqlite3": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.3.tgz",
"integrity": "sha512-/cDwes7XtTOtKH5zYeJSuiavuaJ6jXxPjebw9lDFxBAwR/DvP0tnJ5MPZQ3zpnNzJBa1G6mPTpB+5O1T+AoSdQ==",
"requires": {
"@mapbox/node-pre-gyp": "^1.0.0",
"node-addon-api": "^4.2.0",
"node-gyp": "8.x",
"tar": "^6.1.11"
}
},
"ssri": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
"integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
"optional": true,
"requires": {
"minipass": "^3.1.1"
}
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
}
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"requires": {
"ansi-regex": "^5.0.1"
}
},
"tar": {
"version": "6.1.11",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
"requires": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"minipass": "^3.0.0",
"minizlib": "^2.1.1",
"mkdirp": "^1.0.3",
"yallist": "^4.0.0"
}
},
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
},
"unique-filename": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
"integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
"optional": true,
"requires": {
"unique-slug": "^2.0.0"
}
},
"unique-slug": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
"integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
"optional": true,
"requires": {
"imurmurhash": "^0.1.4"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"optional": true,
"requires": {
"isexe": "^2.0.0"
}
},
"wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"requires": {
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
}

14
scripts/cypress/fixtures/sqlite-sakila/regenFiles/package.json

@ -1,14 +0,0 @@
{
"name": "nc-xcdb",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"sqlite3": "^5.0.3"
}
}

6
scripts/cypress/fixtures/sqlite-sakila/regenFiles/recreate_sakila_sqlite.sh

@ -1,6 +0,0 @@
#!/bin/bash
set -v
rm sakila.db
sqlite3 sakila.db < ./sqlite-sakila-schema.sql
sqlite3 sakila.db < ./sqlite-sakila-insert-data.sql

45
scripts/cypress/fixtures/sqlite-sakila/regenFiles/sqlite-sakila-delete-data.sql

@ -1,45 +0,0 @@
/*
Sakila for SQLite is a port of the Sakila example database available for MySQL, which was originally developed by Mike Hillyer of the MySQL AB documentation team.
This project is designed to help database administrators to decide which database to use for development of new products
The user can run the same SQL against different kind of databases and compare the performance
License: BSD
Copyright DB Software Laboratory
http://www.etl-tools.com
*/
-- Delete data
DELETE FROM payment
;
DELETE FROM rental
;
DELETE FROM customer
;
DELETE FROM film_category
;
DELETE FROM film_text
;
DELETE FROM film_actor
;
DELETE FROM inventory
;
DELETE FROM film
;
DELETE FROM category
;
DELETE FROM staff
;
DELETE FROM store
;
DELETE FROM actor
;
DELETE FROM address
;
DELETE FROM city
;
DELETE FROM country
;
DELETE FROM language
;

70
scripts/cypress/fixtures/sqlite-sakila/regenFiles/sqlite-sakila-drop-objects.sql

@ -1,70 +0,0 @@
/*
Sakila for SQLite is a port of the Sakila example database available for MySQL, which was originally developed by Mike Hillyer of the MySQL AB documentation team.
This project is designed to help database administrators to decide which database to use for development of new products
The user can run the same SQL against different kind of databases and compare the performance
License: BSD
Copyright DB Software Laboratory
http://www.etl-tools.com
*/
-- Drop Views
DROP VIEW customer_list
;
DROP VIEW film_list
;
--DROP VIEW nicer_but_slower_film_list;
DROP VIEW sales_by_film_category
;
DROP VIEW sales_by_store
;
DROP VIEW staff_list
;
-- Drop Tables
DROP TABLE payment
;
DROP TABLE rental
;
DROP TABLE inventory
;
DROP TABLE film_text
;
DROP TABLE film_category
;
DROP TABLE film_actor
;
DROP TABLE film
;
DROP TABLE language
;
DROP TABLE customer
;
DROP TABLE actor
;
DROP TABLE category
;
DROP TABLE store
;
DROP TABLE address
;
DROP TABLE staff
;
DROP TABLE city
;
DROP TABLE country
;
-- Procedures and views
--drop procedure film_in_stock;
--drop procedure film_not_in_stock;
--drop function get_customer_balance;
--drop function inventory_held_by_customer;
--drop function inventory_in_stock;
--drop procedure rewards_report;

231504
scripts/cypress/fixtures/sqlite-sakila/regenFiles/sqlite-sakila-insert-data.sql

File diff suppressed because it is too large Load Diff

647
scripts/cypress/fixtures/sqlite-sakila/regenFiles/sqlite-sakila-schema.sql

@ -1,647 +0,0 @@
/*
Sakila for SQLite is a port of the Sakila example database available for MySQL, which was originally developed by Mike Hillyer of the MySQL AB documentation team.
This project is designed to help database administrators to decide which database to use for development of new products
The user can run the same SQL against different kind of databases and compare the performance
License: BSD
Copyright DB Software Laboratory
http://www.etl-tools.com
*/
--
-- Table structure for table actor
--
--DROP TABLE actor;
BEGIN TRANSACTION;
CREATE TABLE actor (
actor_id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
first_name VARCHAR(45) NOT NULL,
last_name VARCHAR(45) NOT NULL,
last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)
;
CREATE INDEX idx_actor_last_name ON actor(last_name)
;
CREATE TRIGGER actor_trigger_ai AFTER INSERT ON actor
BEGIN
UPDATE actor SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER actor_trigger_au AFTER UPDATE ON actor
BEGIN
UPDATE actor SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table country
--
CREATE TABLE country (
country_id INTEGER NOT NULL,
country VARCHAR(50) NOT NULL,
last_update TIMESTAMP,
PRIMARY KEY (country_id)
)
;
CREATE TRIGGER country_trigger_ai AFTER INSERT ON country
BEGIN
UPDATE country SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER country_trigger_au AFTER UPDATE ON country
BEGIN
UPDATE country SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table city
--
CREATE TABLE city (
city_id INTEGER NOT NULL,
city VARCHAR(50) NOT NULL,
country_id INTEGER NOT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (city_id),
CONSTRAINT fk_city_country FOREIGN KEY (country_id) REFERENCES country (country_id) ON DELETE NO ACTION ON UPDATE CASCADE
)
;
CREATE INDEX idx_fk_country_id ON city(country_id)
;
CREATE TRIGGER city_trigger_ai AFTER INSERT ON city
BEGIN
UPDATE city SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER city_trigger_au AFTER UPDATE ON city
BEGIN
UPDATE city SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table address
--
CREATE TABLE address (
address_id INTEGER NOT NULL,
address VARCHAR(50) NOT NULL,
address2 VARCHAR(50) DEFAULT NULL,
district VARCHAR(20) NOT NULL,
city_id INTEGER NOT NULL,
postal_code VARCHAR(10) DEFAULT NULL,
phone VARCHAR(20) NOT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (address_id),
CONSTRAINT fk_address_city FOREIGN KEY (city_id) REFERENCES city (city_id) ON DELETE NO ACTION ON UPDATE CASCADE
)
;
CREATE INDEX idx_fk_city_id ON address(city_id)
;
CREATE TRIGGER address_trigger_ai AFTER INSERT ON address
BEGIN
UPDATE address SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER address_trigger_au AFTER UPDATE ON address
BEGIN
UPDATE address SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table language
--
CREATE TABLE language (
language_id INTEGER NOT NULL ,
name CHAR(20) NOT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (language_id)
)
;
CREATE TRIGGER language_trigger_ai AFTER INSERT ON language
BEGIN
UPDATE language SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER language_trigger_au AFTER UPDATE ON language
BEGIN
UPDATE language SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table category
--
CREATE TABLE category (
category_id INTEGER NOT NULL,
name VARCHAR(25) NOT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (category_id)
);
CREATE TRIGGER category_trigger_ai AFTER INSERT ON category
BEGIN
UPDATE category SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER category_trigger_au AFTER UPDATE ON category
BEGIN
UPDATE category SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table customer
--
CREATE TABLE customer (
customer_id INTEGER NOT NULL,
store_id INTEGER NOT NULL,
first_name VARCHAR(45) NOT NULL,
last_name VARCHAR(45) NOT NULL,
email VARCHAR(50) DEFAULT NULL,
address_id INTEGER NOT NULL,
active CHAR(1) DEFAULT 'Y' NOT NULL,
create_date TIMESTAMP NOT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (customer_id),
CONSTRAINT fk_customer_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE NO ACTION ON UPDATE CASCADE,
CONSTRAINT fk_customer_address FOREIGN KEY (address_id) REFERENCES address (address_id) ON DELETE NO ACTION ON UPDATE CASCADE
)
;
CREATE INDEX idx_customer_fk_store_id ON customer(store_id)
;
CREATE INDEX idx_customer_fk_address_id ON customer(address_id)
;
CREATE INDEX idx_customer_last_name ON customer(last_name)
;
CREATE TRIGGER customer_trigger_ai AFTER INSERT ON customer
BEGIN
UPDATE customer SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER customer_trigger_au AFTER UPDATE ON customer
BEGIN
UPDATE customer SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table film
--
CREATE TABLE film (
film_id INTEGER NOT NULL,
title VARCHAR(255) NOT NULL,
description BLOB SUB_TYPE TEXT DEFAULT NULL,
release_year VARCHAR(4) DEFAULT NULL,
language_id INTEGER NOT NULL,
original_language_id INTEGER DEFAULT NULL,
rental_duration INTEGER DEFAULT 3 NOT NULL,
rental_rate DECIMAL(4,2) DEFAULT 4.99 NOT NULL,
length INTEGER DEFAULT NULL,
replacement_cost DECIMAL(5,2) DEFAULT 19.99 NOT NULL,
rating VARCHAR(10) DEFAULT 'G',
special_features VARCHAR(100) DEFAULT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (film_id),
CONSTRAINT CHECK_special_features CHECK(special_features is null or
special_features like '%Trailers%' or
special_features like '%Commentaries%' or
special_features like '%Deleted Scenes%' or
special_features like '%Behind the Scenes%'),
CONSTRAINT CHECK_special_rating CHECK(rating in ('G','PG','PG-13','R','NC-17')),
CONSTRAINT fk_film_language FOREIGN KEY (language_id) REFERENCES language (language_id) ,
CONSTRAINT fk_film_language_original FOREIGN KEY (original_language_id) REFERENCES language (language_id)
)
;
CREATE INDEX idx_fk_language_id ON film(language_id)
;
CREATE INDEX idx_fk_original_language_id ON film(original_language_id)
;
CREATE TRIGGER film_trigger_ai AFTER INSERT ON film
BEGIN
UPDATE film SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER film_trigger_au AFTER UPDATE ON film
BEGIN
UPDATE film SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table film_actor
--
CREATE TABLE film_actor (
actor_id INTEGER NOT NULL,
film_id INTEGER NOT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (actor_id,film_id),
CONSTRAINT fk_film_actor_actor FOREIGN KEY (actor_id) REFERENCES actor (actor_id) ON DELETE NO ACTION ON UPDATE CASCADE,
CONSTRAINT fk_film_actor_film FOREIGN KEY (film_id) REFERENCES film (film_id) ON DELETE NO ACTION ON UPDATE CASCADE
)
;
CREATE INDEX idx_fk_film_actor_film ON film_actor(film_id)
;
CREATE INDEX idx_fk_film_actor_actor ON film_actor(actor_id)
;
CREATE TRIGGER film_actor_trigger_ai AFTER INSERT ON film_actor
BEGIN
UPDATE film_actor SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER film_actor_trigger_au AFTER UPDATE ON film_actor
BEGIN
UPDATE film_actor SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table film_category
--
CREATE TABLE film_category (
film_id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (film_id, category_id),
CONSTRAINT fk_film_category_film FOREIGN KEY (film_id) REFERENCES film (film_id) ON DELETE NO ACTION ON UPDATE CASCADE,
CONSTRAINT fk_film_category_category FOREIGN KEY (category_id) REFERENCES category (category_id) ON DELETE NO ACTION ON UPDATE CASCADE
)
;
CREATE INDEX idx_fk_film_category_film ON film_category(film_id)
;
CREATE INDEX idx_fk_film_category_category ON film_category(category_id)
;
CREATE TRIGGER film_category_trigger_ai AFTER INSERT ON film_category
BEGIN
UPDATE film_category SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER film_category_trigger_au AFTER UPDATE ON film_category
BEGIN
UPDATE film_category SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table film_text
--
CREATE TABLE film_text (
film_id INTEGER NOT NULL,
title VARCHAR(255) NOT NULL,
description BLOB SUB_TYPE TEXT,
PRIMARY KEY (film_id)
)
;
--
-- Table structure for table inventory
--
CREATE TABLE inventory (
inventory_id INTEGER NOT NULL,
film_id INTEGER NOT NULL,
store_id INTEGER NOT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (inventory_id),
CONSTRAINT fk_inventory_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE NO ACTION ON UPDATE CASCADE,
CONSTRAINT fk_inventory_film FOREIGN KEY (film_id) REFERENCES film (film_id) ON DELETE NO ACTION ON UPDATE CASCADE
)
;
CREATE INDEX idx_fk_film_id ON inventory(film_id)
;
CREATE INDEX idx_fk_film_id_store_id ON inventory(store_id,film_id)
;
CREATE TRIGGER inventory_trigger_ai AFTER INSERT ON inventory
BEGIN
UPDATE inventory SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER inventory_trigger_au AFTER UPDATE ON inventory
BEGIN
UPDATE inventory SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table staff
--
CREATE TABLE staff (
staff_id INTEGER NOT NULL,
first_name VARCHAR(45) NOT NULL,
last_name VARCHAR(45) NOT NULL,
address_id INTEGER NOT NULL,
picture BLOB DEFAULT NULL,
email VARCHAR(50) DEFAULT NULL,
store_id INTEGER NOT NULL,
active INTEGER DEFAULT 1 NOT NULL,
username VARCHAR(16) NOT NULL,
password VARCHAR(40) DEFAULT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (staff_id),
CONSTRAINT fk_staff_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE NO ACTION ON UPDATE CASCADE,
CONSTRAINT fk_staff_address FOREIGN KEY (address_id) REFERENCES address (address_id) ON DELETE NO ACTION ON UPDATE CASCADE
)
;
CREATE INDEX idx_fk_staff_store_id ON staff(store_id)
;
CREATE INDEX idx_fk_staff_address_id ON staff(address_id)
;
CREATE TRIGGER staff_trigger_ai AFTER INSERT ON staff
BEGIN
UPDATE staff SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER staff_trigger_au AFTER UPDATE ON staff
BEGIN
UPDATE staff SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table store
--
CREATE TABLE store (
store_id INTEGER NOT NULL,
manager_staff_id INTEGER NOT NULL,
address_id INTEGER NOT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (store_id),
CONSTRAINT fk_store_staff FOREIGN KEY (manager_staff_id) REFERENCES staff (staff_id) ,
CONSTRAINT fk_store_address FOREIGN KEY (address_id) REFERENCES address (address_id)
)
;
CREATE INDEX idx_store_fk_manager_staff_id ON store(manager_staff_id)
;
CREATE INDEX idx_fk_store_address ON store(address_id)
;
CREATE TRIGGER store_trigger_ai AFTER INSERT ON store
BEGIN
UPDATE store SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER store_trigger_au AFTER UPDATE ON store
BEGIN
UPDATE store SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- Table structure for table payment
--
CREATE TABLE payment (
payment_id INTEGER NOT NULL,
customer_id INTEGER NOT NULL,
staff_id INTEGER NOT NULL,
rental_id INTEGER DEFAULT NULL,
amount DECIMAL(5,2) NOT NULL,
payment_date TIMESTAMP NOT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (payment_id),
CONSTRAINT fk_payment_rental FOREIGN KEY (rental_id) REFERENCES rental (rental_id) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT fk_payment_customer FOREIGN KEY (customer_id) REFERENCES customer (customer_id) ,
CONSTRAINT fk_payment_staff FOREIGN KEY (staff_id) REFERENCES staff (staff_id)
)
;
CREATE INDEX idx_fk_staff_id ON payment(staff_id)
;
CREATE INDEX idx_fk_customer_id ON payment(customer_id)
;
CREATE TRIGGER payment_trigger_ai AFTER INSERT ON payment
BEGIN
UPDATE payment SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER payment_trigger_au AFTER UPDATE ON payment
BEGIN
UPDATE payment SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TABLE rental (
rental_id INTEGER NOT NULL,
rental_date TIMESTAMP NOT NULL,
inventory_id INTEGER NOT NULL,
customer_id INTEGER NOT NULL,
return_date TIMESTAMP DEFAULT NULL,
staff_id INTEGER NOT NULL,
last_update TIMESTAMP NOT NULL,
PRIMARY KEY (rental_id),
CONSTRAINT fk_rental_staff FOREIGN KEY (staff_id) REFERENCES staff (staff_id) ,
CONSTRAINT fk_rental_inventory FOREIGN KEY (inventory_id) REFERENCES inventory (inventory_id) ,
CONSTRAINT fk_rental_customer FOREIGN KEY (customer_id) REFERENCES customer (customer_id)
)
;
CREATE INDEX idx_rental_fk_inventory_id ON rental(inventory_id)
;
CREATE INDEX idx_rental_fk_customer_id ON rental(customer_id)
;
CREATE INDEX idx_rental_fk_staff_id ON rental(staff_id)
;
CREATE UNIQUE INDEX idx_rental_uq ON rental (rental_date,inventory_id,customer_id)
;
CREATE TRIGGER rental_trigger_ai AFTER INSERT ON rental
BEGIN
UPDATE rental SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
CREATE TRIGGER rental_trigger_au AFTER UPDATE ON rental
BEGIN
UPDATE rental SET last_update = DATETIME('NOW') WHERE rowid = new.rowid;
END
;
--
-- View structure for view customer_list
--
CREATE VIEW customer_list
AS
SELECT cu.customer_id AS ID,
cu.first_name||' '||cu.last_name AS name,
a.address AS address,
a.postal_code AS zip_code,
a.phone AS phone,
city.city AS city,
country.country AS country,
case when cu.active=1 then 'active' else '' end AS notes,
cu.store_id AS SID
FROM customer AS cu JOIN address AS a ON cu.address_id = a.address_id JOIN city ON a.city_id = city.city_id
JOIN country ON city.country_id = country.country_id
;
--
-- View structure for view film_list
--
CREATE VIEW film_list
AS
SELECT film.film_id AS FID,
film.title AS title,
film.description AS description,
category.name AS category,
film.rental_rate AS price,
film.length AS length,
film.rating AS rating,
actor.first_name||' '||actor.last_name AS actors
FROM category LEFT JOIN film_category ON category.category_id = film_category.category_id LEFT JOIN film ON film_category.film_id = film.film_id
JOIN film_actor ON film.film_id = film_actor.film_id
JOIN actor ON film_actor.actor_id = actor.actor_id
;
--
-- View structure for view staff_list
--
CREATE VIEW staff_list
AS
SELECT s.staff_id AS ID,
s.first_name||' '||s.last_name AS name,
a.address AS address,
a.postal_code AS zip_code,
a.phone AS phone,
city.city AS city,
country.country AS country,
s.store_id AS SID
FROM staff AS s JOIN address AS a ON s.address_id = a.address_id JOIN city ON a.city_id = city.city_id
JOIN country ON city.country_id = country.country_id
;
--
-- View structure for view sales_by_store
--
CREATE VIEW sales_by_store
AS
SELECT
s.store_id
,c.city||','||cy.country AS store
,m.first_name||' '||m.last_name AS manager
,SUM(p.amount) AS total_sales
FROM payment AS p
INNER JOIN rental AS r ON p.rental_id = r.rental_id
INNER JOIN inventory AS i ON r.inventory_id = i.inventory_id
INNER JOIN store AS s ON i.store_id = s.store_id
INNER JOIN address AS a ON s.address_id = a.address_id
INNER JOIN city AS c ON a.city_id = c.city_id
INNER JOIN country AS cy ON c.country_id = cy.country_id
INNER JOIN staff AS m ON s.manager_staff_id = m.staff_id
GROUP BY
s.store_id
, c.city||','||cy.country
, m.first_name||' '||m.last_name
;
--
-- View structure for view sales_by_film_category
--
-- Note that total sales will add up to >100% because
-- some titles belong to more than 1 category
--
CREATE VIEW sales_by_film_category
AS
SELECT
c.name AS category
, SUM(p.amount) AS total_sales
FROM payment AS p
INNER JOIN rental AS r ON p.rental_id = r.rental_id
INNER JOIN inventory AS i ON r.inventory_id = i.inventory_id
INNER JOIN film AS f ON i.film_id = f.film_id
INNER JOIN film_category AS fc ON f.film_id = fc.film_id
INNER JOIN category AS c ON fc.category_id = c.category_id
GROUP BY c.name
;
--
-- View structure for view actor_info
--
/*
CREATE VIEW actor_info
AS
SELECT
a.actor_id,
a.first_name,
a.last_name,
GROUP_CONCAT(DISTINCT CONCAT(c.name, ': ',
(SELECT GROUP_CONCAT(f.title ORDER BY f.title SEPARATOR ', ')
FROM sakila.film f
INNER JOIN sakila.film_category fc
ON f.film_id = fc.film_id
INNER JOIN sakila.film_actor fa
ON f.film_id = fa.film_id
WHERE fc.category_id = c.category_id
AND fa.actor_id = a.actor_id
)
)
ORDER BY c.name SEPARATOR '; ')
AS film_info
FROM sakila.actor a
LEFT JOIN sakila.film_actor fa
ON a.actor_id = fa.actor_id
LEFT JOIN sakila.film_category fc
ON fa.film_id = fc.film_id
LEFT JOIN sakila.category c
ON fc.category_id = c.category_id
GROUP BY a.actor_id, a.first_name, a.last_name;
*/
-- TO DO PROCEDURES
-- TO DO TRIGGERS
END TRANSACTION;

BIN
scripts/cypress/fixtures/sqlite-sakila/sakila.db

Binary file not shown.

261
scripts/cypress/integration/common/00_pre_configurations.js

@ -1,261 +0,0 @@
// Cypress test suite: project pre-configurations
//
import { loginPage, projectsPage } from "../../support/page_objects/navigation";
import { mainPage } from "../../support/page_objects/mainPage";
import {
staticProjects,
roles,
isTestSuiteActive,
getCurrentMode,
isXcdb,
setProjectString,
} from "../../support/page_objects/projectConstants";
function prepareSqliteQuery(projId) {
let sqliteQuery = [
`ALTER TABLE "actor" RENAME TO "${projId}actor"`,
`ALTER TABLE "address" RENAME TO "${projId}address"`,
`ALTER TABLE "category" RENAME TO "${projId}category"`,
`ALTER TABLE "city" RENAME TO "${projId}city"`,
`ALTER TABLE "country" RENAME TO "${projId}country"`,
`ALTER TABLE "customer" RENAME TO "${projId}customer"`,
`ALTER TABLE "film" RENAME TO "${projId}film"`,
`ALTER TABLE "film_actor" RENAME TO "${projId}film_actor"`,
`ALTER TABLE "film_category" RENAME TO "${projId}film_category"`,
`ALTER TABLE "film_text" RENAME TO "${projId}film_text"`,
`ALTER TABLE "inventory" RENAME TO "${projId}inventory"`,
`ALTER TABLE "language" RENAME TO "${projId}language"`,
`ALTER TABLE "payment" RENAME TO "${projId}payment"`,
`ALTER TABLE "rental" RENAME TO "${projId}rental"`,
`ALTER TABLE "staff" RENAME TO "${projId}staff"`,
`ALTER TABLE "store" RENAME TO "${projId}store"`,
`CREATE VIEW ${projId}customer_list
AS
SELECT cu.customer_id AS ID,
cu.first_name||' '||cu.last_name AS name,
a.address AS address,
a.postal_code AS zip_code,
a.phone AS phone,
"${projId}city".city AS city,
"${projId}country".country AS country,
case when cu.active=1 then 'active' else '' end AS notes,
cu.store_id AS SID
FROM "${projId}customer" AS cu JOIN "${projId}address" AS a ON cu.address_id = a.address_id JOIN "${projId}city" ON a.city_id = "${projId}city".city_id
JOIN "${projId}country" ON "${projId}city".country_id = "${projId}country".country_id`,
`CREATE VIEW ${projId}film_list
AS
SELECT "${projId}film".film_id AS FID,
"${projId}film".title AS title,
"${projId}film".description AS description,
"${projId}category".name AS category,
"${projId}film".rental_rate AS price,
"${projId}film".length AS length,
"${projId}film".rating AS rating,
"${projId}actor".first_name||' '||"${projId}actor".last_name AS actors
FROM "${projId}category" LEFT JOIN "${projId}film_category" ON "${projId}category".category_id = "${projId}film_category".category_id LEFT JOIN "${projId}film" ON "${projId}Film_category".film_id = "${projId}film".film_id
JOIN "${projId}film_actor" ON "${projId}film".film_id = "${projId}film_actor".film_id
JOIN "${projId}actor" ON "${projId}film_actor".actor_id = "${projId}actor".actor_id`,
`CREATE VIEW ${projId}sales_by_film_category
AS
SELECT
c.name AS category
, SUM(p.amount) AS total_sales
FROM "${projId}payment" AS p
INNER JOIN "${projId}rental" AS r ON p.rental_id = r.rental_id
INNER JOIN "${projId}inventory" AS i ON r.inventory_id = i.inventory_id
INNER JOIN "${projId}film" AS f ON i.film_id = f.film_id
INNER JOIN "${projId}film_category" AS fc ON f.film_id = fc.film_id
INNER JOIN "${projId}category" AS c ON fc.category_id = c.category_id
GROUP BY c.name`,
`CREATE VIEW ${projId}sales_by_store
AS
SELECT
s.store_id
,c.city||','||cy.country AS store
,m.first_name||' '||m.last_name AS manager
,SUM(p.amount) AS total_sales
FROM "${projId}payment" AS p
INNER JOIN "${projId}rental" AS r ON p.rental_id = r.rental_id
INNER JOIN "${projId}inventory" AS i ON r.inventory_id = i.inventory_id
INNER JOIN "${projId}store" AS s ON i.store_id = s.store_id
INNER JOIN "${projId}address" AS a ON s.address_id = a.address_id
INNER JOIN "${projId}city" AS c ON a.city_id = c.city_id
INNER JOIN "${projId}country" AS cy ON c.country_id = cy.country_id
INNER JOIN "${projId}staff" AS m ON s.manager_staff_id = m.staff_id
GROUP BY
s.store_id
, c.city||','||cy.country
, m.first_name||' '||m.last_name`,
`CREATE VIEW ${projId}staff_list
AS
SELECT s.staff_id AS ID,
s.first_name||' '||s.last_name AS name,
a.address AS address,
a.postal_code AS zip_code,
a.phone AS phone,
"${projId}city".city AS city,
"${projId}country".country AS country,
s.store_id AS SID
FROM "${projId}staff" AS s JOIN "${projId}address" AS a ON s.address_id = a.address_id JOIN "${projId}city" ON a.city_id = "${projId}city".city_id
JOIN "${projId}country" ON "${projId}city".country_id = "${projId}country".country_id`,
// below two are dummy entries to ensure view record exists
`CREATE VIEW ${projId}actor_info
AS
SELECT s.staff_id AS ID,
s.first_name||' '||s.last_name AS name,
a.address AS address,
a.postal_code AS zip_code,
a.phone AS phone,
"${projId}city".city AS city,
"${projId}country".country AS country,
s.store_id AS SID
FROM "${projId}staff" AS s JOIN "${projId}address" AS a ON s.address_id = a.address_id JOIN "${projId}city" ON a.city_id = "${projId}city".city_id
JOIN "${projId}country" ON "${projId}city".country_id = "${projId}country".country_id`,
`CREATE VIEW ${projId}nice_but_slower_film_list
AS
SELECT s.staff_id AS ID,
s.first_name||' '||s.last_name AS name,
a.address AS address,
a.postal_code AS zip_code,
a.phone AS phone,
"${projId}city".city AS city,
"${projId}country".country AS country,
s.store_id AS SID
FROM "${projId}staff" AS s JOIN "${projId}address" AS a ON s.address_id = a.address_id JOIN "${projId}city" ON a.city_id = "${projId}city".city_id
JOIN "${projId}country" ON "${projId}city".country_id = "${projId}country".country_id`,
// `CREATE VIEW ${projId}actor_info
// AS
// SELECT
// a.actor_id AS actor_id,
// a.first_name AS first_name,
// a.last_name AS last_name,
// GROUP_CONCAT(DISTINCT CONCAT(c.name,
// ': ',
// (SELECT
// GROUP_CONCAT(f.title
// ORDER BY f.title ASC
// SEPARATOR ', ')
// FROM
// ((${projId}film f
// JOIN ${projId}film_category fc ON ((f.film_id = fc.film_id)))
// JOIN ${projId}film_actor fa ON ((f.film_id = fa.film_id)))
// WHERE
// ((fc.category_id = c.category_id)
// AND (fa.actor_id = a.actor_id))))
// ORDER BY c.name ASC
// SEPARATOR '; ') AS ${projId}film_info
// FROM
// (((actor a
// LEFT JOIN ${projId}film_actor fa ON ((a.actor_id = fa.actor_id)))
// LEFT JOIN ${projId}film_category fc ON ((fa.film_id = fc.film_id)))
// LEFT JOIN ${projId}category c ON ((fc.category_id = c.category_id)))
// GROUP BY a.actor_id , a.first_name , a.last_name`,
];
return sqliteQuery;
}
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`Project pre-configurations`, () => {
before(() => {
cy.fileHook();
});
it("Admin SignUp", () => {
cy.task("log", "This will be output to the terminal");
loginPage.signUp(roles.owner.credentials);
});
function cy_createProjectBlock(proj, apiType, dbType) {
// click home button
cy.getSettled(".nc-noco-brand-icon").click();
cy.get(".ant-table-content").then((obj) => {
// if project already created, open
// else, create a new one
if (true == obj[0].innerHTML.includes(proj.basic.name)) {
projectsPage.openProject(proj.basic.name);
let projId;
if (dbType === "xcdb") {
let query = `SELECT prefix from nc_projects_v2 where title = "sampleREST"; `;
cy.task("sqliteExecReturnValue", query).then((resolve) => {
cy.log(resolve);
projId = resolve.prefix;
setProjectString(projId);
cy.log(projId);
});
}
} else {
projectsPage.createProject(proj.basic, proj.config);
if (dbType === "xcdb") {
// store base URL- to re-visit and delete form view later
let projId;
cy.url()
.then((url) => {
// project prefix code can include "_"
// projId = url.split("_")[1].split("?")[0];
let startIdx = url.indexOf("_");
let endIdx = url.indexOf("?");
projId = url.slice(startIdx + 1, endIdx);
let query = `SELECT prefix from nc_projects_v2 where title = "sampleREST"; `;
cy.task("sqliteExecReturnValue", query)
.then((resolve) => {
cy.log(resolve);
projId = resolve.prefix;
cy.log(projId);
setProjectString(projId);
})
.then(() => {
let query = prepareSqliteQuery(projId);
for (let i = 0; i < query.length; i++) {
cy.task("sqliteExec", query[i]);
cy.wait(1000);
}
});
})
.then(() => {
cy.log(projId);
mainPage.openMetaTab();
mainPage.metaSyncValidate(
`${projId}actor`,
`New table, New relation added`
);
mainPage.closeMetaTab();
});
}
}
});
}
const createProject = (proj) => {
it(`Create ${proj.basic.name} project`, () => {
if (dbType === "postgres") {
// wait for docker compose to start
cy.task("pgExecTest", `SELECT 1+1`, { timeout: 120000 }).then(() =>
cy_createProjectBlock(proj, apiType, dbType)
);
} else {
cy_createProjectBlock(proj, apiType, dbType);
}
// close team & auth tab
// wait for tab to be rendered completely
cy.wait(2000);
cy.closeTableTab();
// first instance of updating local storage information
cy.saveLocalStorage();
});
};
if ("xcdb" === dbType) {
createProject(staticProjects.sampleREST);
} else if (dbType === "mysql") {
createProject(staticProjects.externalREST);
} else if (dbType === "postgres") {
createProject(staticProjects.pgExternalREST);
}
});
};

106
scripts/cypress/integration/common/1a_table_operations.js

@ -1,106 +0,0 @@
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
import { mainPage, settingsPage } from "../../support/page_objects/mainPage";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${
dbType === "xcdb" ? "Meta - " : ""
}${apiType.toUpperCase()} api - Table`, () => {
// before(() => {
// // standalone test
// // loginPage.loginAndOpenProject(apiType, dbType);
//
// // open a table to work on views
// //
// // cy.restoreLocalStorage();
// });
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
// after(() => {
// });
const name = "tablex";
// create a new random table
it("Create Table", () => {
cy.createTable(name);
});
// delete newly created table
it("Delete Table", () => {
cy.deleteTable(name, dbType);
});
const getAuditCell = (row, col) => {
return cy.get("tbody > tr").eq(row).find("td.ant-table-cell").eq(col);
};
describe("Check Audit Tab Cells", () => {
it("Open Audit tab", () => {
// http://localhost:8080/api/v1/db/meta/projects/p_bxp57hmks0n5o2/audits?offset=0&limit=25
cy.intercept("/**/audits?offset=*&limit=*").as("waitForPageLoad");
// mainPage.navigationDraw(mainPage.AUDIT).click();
settingsPage.openMenu(settingsPage.AUDIT);
// wait for column headers to appear
//
cy.get("thead > tr > th.ant-table-cell").should("have.length", 5);
cy.wait("@waitForPageLoad");
// Audit table entries
// [Header] Operation Type, Operation Sub Type, Description, User, Created
// [0] TABLE, DELETED, delete table table-x, user@nocodb.com, ...
// [1] TABLE, Created, created table table-x, user@nocodb.com, ...
getAuditCell(0, 0).contains("TABLE").should("exist");
getAuditCell(0, 1).contains("DELETED").should("exist");
getAuditCell(0, 3).contains("user@nocodb.com").should("exist");
getAuditCell(1, 0).contains("TABLE").should("exist");
getAuditCell(1, 1).contains("CREATED").should("exist");
getAuditCell(1, 3).contains("user@nocodb.com").should("exist");
});
after(() => {
settingsPage.closeMenu();
})
})
it("Table Rename operation", () => {
cy.get(".nc-project-tree-tbl-City").should("exist").click();
cy.renameTable("City", "CityX");
// verify
// 1. Table name in project tree has changed
// cy.get(".nc-tbl-title").contains("CityX").should("exist");
cy.get(".nc-project-tree-tbl-CityX").should("exist");
// 2. Table tab name has changed
cy.get(`.ant-tabs-tab:contains('CityX'):visible`).should("exist");
// Wait for Grid to render
cy.gridWait(25);
// 3. contents of the table are valid
mainPage
.getCell(`City`, 1)
.contains("A Corua (La Corua)")
.should("exist");
cy.closeTableTab("CityX");
// revert re-name operation to not impact rest of test suite
cy.renameTable("CityX", "City");
});
});
};

200
scripts/cypress/integration/common/1b_table_column_operations.js

@ -1,200 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import {
isTestSuiteActive,
isXcdb,
} from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
function addNewRow(index, cellValue) {
mainPage.addNewRowExpand("tablex");
// cy.get("#data-table-form-Title > input").first().type(cellValue);
cy.get(".nc-expand-col-Title")
.find(".nc-cell > input")
.should("exist")
.first()
.clear()
.type(cellValue);
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Save row")
.should("exist")
.click();
cy.toastWait("updated successfully");
cy.get("body").type("{esc}");
mainPage.getCell("Title", index).contains(cellValue).should("exist");
}
describe(`${apiType.toUpperCase()} api - Table Column`, () => {
const name = "tablex";
const colName = "column_name_a";
const updatedColName = "updated_column_name";
const randVal = "Test@1234.com";
const updatedRandVal = "Updated@1234.com";
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
it("Create Table Column", () => {
cy.createTable(name);
mainPage.addColumn(colName, name);
});
// edit the newly created column
it("Edit table column - change datatype", () => {
if (!isXcdb()) {
cy.get(`th:contains(${colName}) .nc-icon.ant-dropdown-trigger`)
.trigger("mouseover", { force: true })
.click({ force: true });
// cy.get(".nc-column-edit").click();
// cy.get(".nc-column-edit").should("not.be.visible");
cy.getActiveMenu(".nc-dropdown-column-operations")
.find(".nc-column-edit")
.click();
cy.inputHighlightRenderWait();
// change column type and verify
// cy.get(".nc-column-type-input").last().click();
cy.getActiveMenu(".nc-dropdown-edit-column")
.find(".nc-column-type-input")
.last()
.click();
cy.getActiveSelection(".nc-dropdown-column-type")
.find(".ant-select-item-option")
.contains("LongText")
.click();
cy.getActiveMenu(".nc-dropdown-edit-column")
.find(".ant-btn-primary:visible")
.contains("Save")
.click();
cy.toastWait("Column updated");
}
});
// edit the newly created column
it("Edit table column - rename", () => {
cy.get(`th:contains(${colName}) .nc-icon.ant-dropdown-trigger`)
.trigger("mouseover", { force: true })
.click({ force: true });
// cy.get(".nc-column-edit").click();
// cy.get(".nc-column-edit").should("not.be.visible");
cy.getActiveMenu(".nc-dropdown-column-operations")
.find(".nc-column-edit")
.click();
// rename column and verify
cy.getActiveMenu(".nc-dropdown-edit-column")
.find("input.nc-column-name-input", { timeout: 3000 })
.should("exist")
.clear()
.type(updatedColName);
// cy.get(".ant-btn-primary:visible").contains("Save").click();
cy.getActiveMenu(".nc-dropdown-edit-column")
.find(".ant-btn-primary:visible")
.contains("Save")
.click();
cy.toastWait("Column updated");
cy.get(`th:contains(${colName})`).should("not.exist");
cy.get(`th:contains(${updatedColName})`).should("exist");
});
// delete the newly created column
it("Delete table column", () => {
mainPage.deleteColumn(updatedColName);
});
it("Add new row", () => {
addNewRow(1, randVal);
});
it("Update row", () => {
mainPage
.getRow(1)
.find(".nc-row-no")
.should("exist")
.eq(0)
.trigger("mouseover", { force: true })
.get(".nc-row-expand")
.click({ force: true });
// wait for page render to complete
cy.get('button:contains("Save row"):visible').should("exist");
cy.get(".nc-expand-col-Title")
.find(".nc-cell > input")
.should("exist")
.first()
.clear()
.type(updatedRandVal);
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Save row")
.should("exist")
.click();
// partial toast message
cy.toastWait("updated successfully");
cy.get("body").type("{esc}");
mainPage.getCell("Title", 1).contains(randVal).should("not.exist");
mainPage.getCell("Title", 1).contains(updatedRandVal).should("exist");
});
it("Delete Row", () => {
mainPage
.getCell("Title", 1)
.contains(updatedRandVal)
.rightclick({ force: true });
// delete row
cy.getActiveMenu(".nc-dropdown-grid-context-menu")
.find('.ant-dropdown-menu-item:contains("Delete Row")')
.first()
.click({ force: true });
cy.get("td").contains(randVal).should("not.exist");
});
it("Select all row check-box validation", () => {
// add multiple rows
addNewRow(1, "a1");
addNewRow(2, "a2");
addNewRow(3, "a3");
addNewRow(4, "a4");
addNewRow(5, "a5");
cy.get(".nc-no-label")
.should("exist")
.eq(0)
.trigger("mouseover", { force: true });
cy.get(".ant-checkbox").should("exist").eq(0).click({ force: true });
// delete selected rows
mainPage.getCell("Title", 3).rightclick({ force: true });
cy.getActiveMenu(".nc-dropdown-grid-context-menu")
.contains("Delete Selected Rows")
.click({ force: true });
// verify if everything is wiped off
mainPage.getCell("Title", 1).contains("a1").should("not.exist");
// clean-up
cy.deleteTable(name, dbType);
});
});
};

143
scripts/cypress/integration/common/1c_sql_view.js

@ -1,143 +0,0 @@
import {
isTestSuiteActive,
isXcdb,
} from "../../support/page_objects/projectConstants";
import { mainPage } from "../../support/page_objects/mainPage";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} SQL Views`, () => {
// Run once before test- create project (rest/graphql)
//
// before(() => {
// cy.fileHook();
// mainPage.tabReset();
// });
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
it(`XCDB: SQL View Column operations`, () => {
// Open one of the views & verify validity of first two entries
if (isXcdb()) {
cy.openViewsTab("CustomerList", 25);
// Record-1 validation
mainPage.getCell(`ID`, 1).contains("1").should("exist");
mainPage.getCell(`Name`, 1).contains("MARY SMITH").should("exist");
mainPage
.getCell(`Address`, 1)
.contains("1913 Hanoi Way")
.should("exist");
mainPage.getCell(`ZipCode`, 1).contains("35200").should("exist");
// Record-2 validation
mainPage.getCell(`ID`, 2).contains("2").should("exist");
mainPage
.getCell(`Name`, 2)
.contains("PATRICIA JOHNSON")
.should("exist");
mainPage
.getCell(`Address`, 2)
.contains("1121 Loja Avenue")
.should("exist");
mainPage.getCell(`ZipCode`, 2).contains("17886").should("exist");
// Column operations: Hide
mainPage.hideField(`ZipCode`);
mainPage.unhideField(`ZipCode`);
// Column operations: Sort
mainPage.sortField("Name", "Z → A");
mainPage.getCell(`Name`, 1).contains("ZACHARY HITE").should("exist");
mainPage.clearSort();
// Column operations: Filter
mainPage.filterField("Name", "is like", "MARY");
mainPage.getCell(`Name`, 1).contains("MARY SMITH").should("exist");
mainPage.filterReset();
cy.closeViewsTab("CustomerList");
}
});
it(`SQL View Column operations`, () => {
if (!isXcdb()) {
// Open one of the views & verify validity of first two entries
cy.openViewsTab("ActorInfo", 25);
// Record-1 validation
mainPage.getCell(`ActorId`, 1).contains("1").should("exist");
mainPage.getCell(`FirstName`, 1).contains("PENELOPE").should("exist");
mainPage.getCell(`LastName`, 1).contains("GUINESS").should("exist");
mainPage
.getCell(`FilmInfo`, 1)
.contains("Animation: ANACONDA CONFESSIONS")
.should("exist");
// Record-2 validation
mainPage.getCell(`ActorId`, 2).contains("2").should("exist");
mainPage.getCell(`FirstName`, 2).contains("NICK").should("exist");
mainPage.getCell(`LastName`, 2).contains("WAHLBERG").should("exist");
mainPage
.getCell(`FilmInfo`, 2)
.contains("Action: BULL SHAWSHANK")
.should("exist");
// Column operations: Hide
mainPage.hideField("FilmInfo");
mainPage.unhideField("FilmInfo");
// Column operations: Sort
mainPage.sortField("FirstName", "Z → A");
mainPage.getCell(`FirstName`, 1).contains("ZERO").should("exist");
mainPage.clearSort();
// Column operations: Filter
mainPage.filterField("FirstName", "is like", "PENELOPE");
mainPage.getCell(`FirstName`, 1).contains("PENELOPE").should("exist");
mainPage.filterReset();
cy.closeViewsTab("ActorInfo");
}
});
it.skip(`SQL View List`, () => {
// confirm if other views exist
//
cy.openViewsTab("CustomerList", 25);
cy.closeViewsTab("CustomerList");
cy.openViewsTab("FilmList", 25);
cy.closeViewsTab("FilmList");
cy.openViewsTab("SalesByFilmCategory", 16);
cy.closeViewsTab("SalesByFilmCategory");
if (!isXcdb()) {
cy.openViewsTab("NicerButSlowerFilmList", 25);
cy.closeViewsTab("NicerButSlowerFilmList");
// SalesByStore && StaffList contain no entries. Hence marking row count to 0
cy.openViewsTab("SalesByStore", 0);
cy.closeViewsTab("SalesByStore");
cy.openViewsTab("StaffList", 0);
cy.closeViewsTab("StaffList");
} else {
cy.openViewsTab("SalesByStore", 2);
cy.closeViewsTab("SalesByStore");
cy.openViewsTab("StaffList", 2);
cy.closeViewsTab("StaffList");
}
});
});
};

147
scripts/cypress/integration/common/1d_pg_table_view_drag_drop_reorder.js

@ -1,147 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import {
isTestSuiteActive,
isXcdb,
getProjectString,
isPostgres,
} from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} Table/view drag-drop reorder`, () => {
function validateTreeField(index, tblName) {
cy.get(`:nth-child(${index}) > .v-list-item__title > .caption`)
.contains(tblName)
.should("exist");
}
before(() => {
cy.fileHook();
mainPage.tabReset();
});
/*
Original order of list items
Actor, Address, Category, City, Country, Customer, FIlm, FilmText, Language, Payment, Rental Staff
ActorInfo, Customer List, Film List, NiceButSlowerFilm List, SalesByFilmCategory, SalesByStore, Staff List
*/
it(`Table & SQL View list, Drag/drop`, () => {
// expand tree-view menu
// cy.get(".nc-project-tree")
// .find(".v-list-item__title:contains(Tables)", { timeout: 10000 })
// .should("exist")
// .first()
// .click({ force: true });
validateTreeField(1, "Actor");
// move Actor field down, above Staff (drag, drop)
cy.get(".nc-child-draggable-icon-Actor").drag(
".nc-child-draggable-icon-Film"
);
validateTreeField(7, "Actor");
// // move ActorInfo (View) field up to first place (drag, drop)
// cy.get(".nc-child-draggable-icon-ActorInfo").drag(
// ".nc-child-draggable-icon-Address"
// );
// validateTreeField(1, "ActorInfo");
// validateTreeField(2, "Address");
// validateTreeField(8, "Actor");
// // restore ActorInfo field (drag, drop)
// cy.get(".nc-child-draggable-icon-ActorInfo").drag(
// ".nc-child-draggable-icon-Staff"
// );
// restore Actor field (drag, drop)
cy.get(".nc-child-draggable-icon-Actor").drag(
".nc-child-draggable-icon-Address"
);
validateTreeField(1, "Actor");
validateTreeField(2, "Address");
// undo project-tree expand operation
cy.get(".nc-project-tree")
.find(".v-list-item__title:contains(Tables)", {
timeout: 10000,
})
.should("exist")
.first()
.click({ force: true });
});
// create new view as specified by 'viewType'
// can be - grid/ gallery/ form
// wait for toast to appear
//
function createView(viewType) {
// click on 'Grid/Gallery' button on Views bar
cy.get(`.nc-create-${viewType}-view`).click();
cy.snipActiveModal(`Modal_createView_${viewType}`);
// Pop up window, click Submit (accepting default name for view)
cy.getActiveModal(".nc-modal-view-create")
.find("button:contains(Submit)")
.click();
cy.toastWait("View created successfully");
}
// verify view 'viewName' to be present at position 'index'
// index starts from 0
function validateViewField(index, viewName) {
cy.get(".nc-view-item.nc-draggable-child")
.eq(index)
.contains(viewName)
.should("exist");
}
it(`View (Gallery/ Grid/ Form) re-order`, () => {
cy.openTableTab("Actor", 25);
// create 3 views, use default names
// Actor1, Actor2, Actor3
createView("grid");
createView("gallery");
createView("form");
// validate position order
validateViewField(0, "Actor");
validateViewField(1, "Actor1");
validateViewField(2, "Actor2");
validateViewField(3, "Actor3");
// move Actor3 field on top (drag, drop)
cy.get(".nc-child-draggable-icon-Actor3").drag(
`.nc-child-draggable-icon-${
isXcdb() ? `${getProjectString()}` : ``
}actor`
);
// validate new position order, Actor3 on top
validateViewField(0, "Actor3");
validateViewField(1, "Actor");
validateViewField(2, "Actor1");
validateViewField(3, "Actor2");
// delete all created views
// click on delete icon (becomes visible on hovering mouse)
cy.get(".nc-view-delete-icon").eq(0).click({ force: true });
cy.toastWait("View deleted successfully");
cy.get(".nc-view-delete-icon").eq(0).click({ force: true });
cy.toastWait("View deleted successfully");
cy.get(".nc-view-delete-icon").eq(0).click({ force: true });
cy.toastWait("View deleted successfully");
// wind up
cy.closeTableTab("Actor");
});
});
};

156
scripts/cypress/integration/common/1d_table_view_drag_drop_reorder.js

@ -1,156 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import {
isTestSuiteActive,
isXcdb,
getProjectString,
} from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} Table/view drag-drop reorder`, () => {
function validateTreeField(index, tblName) {
cy.get(`.nc-project-tree-tbl`)
.eq(index - 1)
.find(".nc-tbl-title")
.contains(tblName)
.should("exist");
}
/*
Original order of list items
Actor, Address, Category, City, Country, Customer, FIlm, FilmText, Language, Payment, Rental Staff
ActorInfo, Customer List, Film List, NiceButSlowerFilm List, SalesByFilmCategory, SalesByStore, Staff List
*/
before(() => {
cy.fileHook();
mainPage.tabReset();
});
beforeEach(() => {
cy.fileHook();
});
it(`Table & SQL View list, Drag/drop`, () => {
// expand tree-view menu
// cy.get(".nc-project-tree")
// .find(".v-list-item__title:contains(Tables)", { timeout: 10000 })
// .should("exist")
// .first()
// .click({ force: true });
validateTreeField(1, "Actor");
// move Actor field down, above Staff (drag, drop)
cy.get(".nc-child-draggable-icon-Actor").click({ force: true });
cy.get(".nc-child-draggable-icon-Actor").should("be.visible");
cy.get(".nc-child-draggable-icon-Actor").drag(
".nc-child-draggable-icon-Staff",
{ force: true }
);
validateTreeField(12, "Actor");
// // move ActorInfo (View) field up to first place (drag, drop)
// cy.get(".nc-child-draggable-icon-ActorInfo").drag(
// ".nc-child-draggable-icon-Address"
// );
//
// validateTreeField(1, "ActorInfo");
// validateTreeField(2, "Address");
// validateTreeField(13, "Actor");
//
// // restore ActorInfo field (drag, drop)
// cy.get(".nc-child-draggable-icon-ActorInfo").drag(
// ".nc-child-draggable-icon-Actor"
// );
//
// // restore Actor field (drag, drop)
// cy.get(".nc-child-draggable-icon-Actor").drag(
// ".nc-child-draggable-icon-Address"
// );
//
// validateTreeField(1, "Actor");
// validateTreeField(2, "Address");
// validateTreeField(12, "Staff");
// validateTreeField(13, "ActorInfo");
// validateTreeField(14, "CustomerList");
//
// // undo project-tree expand operation
// cy.get(".nc-project-tree")
// .should("exist")
// .first()
// .click({ force: true });
});
// create new view as specified by 'viewType'
// can be - grid/ gallery/ form
// wait for toast to appear
//
function createView(viewType) {
// click on 'Grid/Gallery' button on Views bar
cy.get(`.nc-create-${viewType}-view`).click();
cy.snipActiveModal(`Modal_createView_${viewType}`);
// Pop up window, click Submit (accepting default name for view)
cy.getActiveModal(".nc-modal-view-create")
.find("button:contains(Submit)")
.click();
cy.toastWait("View created successfully");
}
// verify view 'viewName' to be present at position 'index'
// index starts from 0
function validateViewField(index, viewName) {
cy.get(".nc-view-item.nc-draggable-child")
.eq(index)
.contains(viewName)
.should("exist");
}
// exclude@ncv2: to be investigated & fixed
it.skip(`View (Gallery/ Grid/ Form) re-order`, () => {
cy.openTableTab("Actor", 25);
// create 3 views, use default names
// Actor1, Actor2, Actor3
createView("grid");
createView("gallery");
createView("form");
// validate position order
validateViewField(0, "Actor");
validateViewField(1, "Actor1");
validateViewField(2, "Actor2");
validateViewField(3, "Actor3");
// move Actor3 field on top (drag, drop)
cy.get(".nc-child-draggable-icon-Actor3").drag(
`.nc-child-draggable-icon-${
isXcdb() ? `${getProjectString()}` : ``
}Actor`
);
// validate new position order, Actor3 on top
validateViewField(0, "Actor3");
validateViewField(1, "Actor");
validateViewField(2, "Actor1");
validateViewField(3, "Actor2");
// delete all created views
// click on delete icon (becomes visible on hovering mouse)
cy.get(".nc-view-delete-icon").eq(0).click({ force: true });
cy.toastWait("View deleted successfully");
cy.get(".nc-view-delete-icon").eq(0).click({ force: true });
cy.toastWait("View deleted successfully");
cy.get(".nc-view-delete-icon").eq(0).click({ force: true });
cy.toastWait("View deleted successfully");
// wind up
cy.closeTableTab("Actor");
});
});
};

169
scripts/cypress/integration/common/1e_meta_sync.js

@ -1,169 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { loginPage } from "../../support/page_objects/navigation";
import {
getCurrentMode,
getProjectString,
isTestSuiteActive,
isXcdb,
} from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
let projPrefix = `sakila.`;
let dbCmd = `queryDb`;
let tblDisplayPrefix = ``;
describe(`${apiType.toUpperCase()} api - Meta Sync`, () => {
// Run once before test- create project (rest/graphql)
//
before(() => {
cy.restoreLocalStorage();
if (isXcdb()) {
cy.log(getProjectString());
projPrefix = `${getProjectString()}`;
dbCmd = `sqliteExec`;
tblDisplayPrefix = `${getProjectString()}`;
}
mainPage.openMetaTab();
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
it(`Create table`, () => {
// Create Table
cy.task(
dbCmd,
`CREATE TABLE ${projPrefix}table1 (id INT NOT NULL, col1 INT NULL, PRIMARY KEY (id))`
);
cy.task(
dbCmd,
`CREATE TABLE ${projPrefix}table2 (id INT NOT NULL, col1 INT NULL, PRIMARY KEY (id))`
);
mainPage.metaSyncValidate(`${tblDisplayPrefix}table1`, "New table");
});
it(`Add relation`, () => {
// working with relations in sqlite requires table to be deleted & recreated
//
if (!isXcdb()) {
// Add relation (FK)
cy.task(
dbCmd,
`ALTER TABLE ${projPrefix}table1 ADD INDEX fk1_idx (col1 ASC) VISIBLE`
);
cy.task(
dbCmd,
`ALTER TABLE ${projPrefix}table1 ADD CONSTRAINT fk1 FOREIGN KEY (col1) REFERENCES ${projPrefix}table2 (id) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"New relation added"
);
}
});
it(`Remove relation`, () => {
// working with relations in sqlite requires table to be deleted & recreated
//
if (!isXcdb()) {
// Remove relation (FK)
cy.task(dbCmd, `ALTER TABLE ${projPrefix}table1 DROP FOREIGN KEY fk1`);
cy.task(dbCmd, `ALTER TABLE ${projPrefix}table1 DROP INDEX fk1_idx`);
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"Relation removed"
);
}
});
it(`Add column`, () => {
// Add Column
let queryString = `ALTER TABLE ${projPrefix}table1 ADD COLUMN newCol VARCHAR(45) NULL AFTER id`;
if (isXcdb())
queryString = `ALTER TABLE ${projPrefix}table1 ADD COLUMN newCol TEXT NULL`;
cy.task(dbCmd, queryString);
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"New column(newCol)"
);
});
it(`Rename column`, () => {
// Rename Column
let queryString = `ALTER TABLE ${projPrefix}table1 CHANGE COLUMN newCol newColName VARCHAR(45) NULL DEFAULT NULL`;
if (isXcdb())
queryString = `ALTER TABLE ${projPrefix}table1 RENAME COLUMN newCol TO newColName`;
cy.task(dbCmd, queryString);
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"New column(newColName), Column removed(newCol)"
);
});
it(`Delete column`, () => {
// Remove Column
// to be fixed for SQLITE
if (!isXcdb()) {
cy.task(
dbCmd,
`ALTER TABLE ${projPrefix}table1 DROP COLUMN newColName`
);
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"Column removed(newColName)"
);
}
});
it(`Delete table`, () => {
// DROP TABLE
cy.task(dbCmd, `DROP TABLE ${projPrefix}table1`);
cy.task(dbCmd, `DROP TABLE ${projPrefix}table2`);
mainPage.metaSyncValidate(`${tblDisplayPrefix}table1`, "Table removed");
});
it(`Hide, Filter, Sort`, () => {
cy.task(
dbCmd,
`CREATE TABLE ${projPrefix}table1 (id INT NOT NULL, col1 INT NULL, col2 INT NULL, col3 INT NULL, col4 INT NULL, PRIMARY KEY (id))`
);
cy.task(
dbCmd,
`INSERT INTO ${projPrefix}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);`
);
mainPage.metaSyncValidate(`${tblDisplayPrefix}table1`, "New table");
mainPage.closeMetaTab();
cy.openTableTab("Table1", 9);
mainPage.hideField("Col1");
mainPage.sortField("Col1", "9 → 1");
mainPage.filterField(`Col1`, ">=", "5");
cy.get(".nc-grid-row").should("have.length", 5);
cy.closeTableTab("Table1");
});
it(`Verify`, () => {
mainPage.openMetaTab();
// Rename Column
let queryString = `ALTER TABLE ${projPrefix}table1 CHANGE COLUMN col1 newCol INT NULL DEFAULT NULL`;
if (isXcdb())
queryString = `ALTER TABLE ${projPrefix}table1 RENAME COLUMN col1 TO newCol`;
cy.task(dbCmd, queryString);
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"New column(newCol), Column removed(col1)"
);
mainPage.closeMetaTab();
cy.openTableTab("Table1", 9);
cy.deleteTable("Table1", dbType);
});
});
};

182
scripts/cypress/integration/common/1e_pg_meta_sync.js

@ -1,182 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { loginPage } from "../../support/page_objects/navigation";
import {
getCurrentMode,
getProjectString,
isTestSuiteActive,
isXcdb,
} from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
let projPrefix = `sakila.`;
let dbCmd = `pgExec`;
let tblDisplayPrefix = ``;
describe(`${apiType.toUpperCase()} api - Meta Sync`, () => {
// Run once before test- create project (rest/graphql)
//
before(() => {
cy.restoreLocalStorage();
mainPage.openMetaTab();
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
it(`Create table`, () => {
cy.log("this works");
// Create Table
cy.task(
dbCmd,
`CREATE TABLE table1( id INT NOT NULL, col1 INT NOT NULL, PRIMARY KEY(id))`
);
cy.task(
dbCmd,
`CREATE TABLE table2( id INT NOT NULL, col1 INT NOT NULL, PRIMARY KEY(id))`
);
mainPage.metaSyncValidate(`${tblDisplayPrefix}table1`, "New table");
});
it(`Add relation`, () => {
// working with relations in sqlite requires table to be deleted & recreated
//
// Add relation (FK)
cy.task(
dbCmd,
`ALTER TABLE table1 ADD CONSTRAINT fk_idx FOREIGN KEY (id) REFERENCES table2 (id);`
);
// cy.task(
// dbCmd,
// `ALTER TABLE ${projPrefix}table1 ADD CONSTRAINT fk1 FOREIGN KEY (col1) REFERENCES ${projPrefix}table2 (id) ON DELETE NO ACTION ON UPDATE NO ACTION`
// );
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"New relation added"
);
});
it(`Remove relation`, () => {
// working with relations in sqlite requires table to be deleted & recreated
//
// Remove relation (FK)
cy.task(dbCmd, `ALTER TABLE table1 DROP CONSTRAINT fk_idx`);
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"Relation removed"
);
});
it(`Add column`, () => {
// Add Column
let queryString = `ALTER TABLE table1 ADD COLUMN newCol INT`;
cy.task(dbCmd, queryString);
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"New column(newcol)"
);
});
it(`Rename column`, () => {
// Rename Column
let queryString = `ALTER TABLE table1 RENAME COLUMN newCol TO newColName`;
cy.task(dbCmd, queryString);
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"New column(newcolname), Column removed(newcol)"
);
});
it(`Delete column`, () => {
// Remove Column
cy.task(dbCmd, `ALTER TABLE table1 DROP COLUMN newColName`);
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"Column removed(newcolname)"
);
});
it(`Delete table`, () => {
// DROP TABLE
cy.task(dbCmd, `DROP TABLE IF EXISTS table1`);
cy.task(dbCmd, `DROP TABLE IF EXISTS table2`);
mainPage.metaSyncValidate(`${tblDisplayPrefix}table1`, "Table removed");
});
it(`Hide, Filter, Sort`, () => {
// kludge: bulk insert fail.
cy.task(
dbCmd,
`CREATE TABLE table1( id INT NOT NULL, col1 INT NOT NULL, col2 INT NOT NULL, col3 INT NOT NULL, col4 INT NOT NULL, PRIMARY KEY(id))`
);
cy.wait(3000);
cy.task(
dbCmd,
`INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (1,1,1,1,1)`
);
cy.task(
dbCmd,
`INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (2,2,2,2,2)`
);
cy.task(
dbCmd,
`INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (3,3,3,3,3)`
);
cy.task(
dbCmd,
`INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (4,4,4,4,4)`
);
cy.task(
dbCmd,
`INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (5,5,5,5,5)`
);
cy.task(
dbCmd,
`INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (6,6,6,6,6)`
);
cy.task(
dbCmd,
`INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (7,7,7,7,7)`
);
cy.task(
dbCmd,
`INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (8,8,8,8,8)`
);
cy.task(
dbCmd,
`INSERT INTO table1 (id, col1, col2, col3, col4) VALUES (9,9,9,9,9)`
);
mainPage.metaSyncValidate(`${tblDisplayPrefix}table1`, "New table");
mainPage.closeMetaTab();
cy.openTableTab("Table1", 9);
mainPage.hideField("Col1");
mainPage.sortField("Col1", "9 → 1");
mainPage.filterField(`Col1`, ">=", "5");
cy.get(".nc-grid-row").should("have.length", 5);
cy.closeTableTab("Table1");
});
it(`Verify`, () => {
mainPage.openMetaTab();
// Rename Column
let queryString = `ALTER TABLE table1 RENAME COLUMN col1 TO newcol`;
cy.task(dbCmd, queryString);
mainPage.metaSyncValidate(
`${tblDisplayPrefix}table1`,
"New column(newcol), Column removed(col1)"
);
mainPage.closeMetaTab();
cy.openTableTab("Table1", 9);
// kludge- delete table triggered post sql backend operations doesnt carry any trigger toast
cy.deleteTable("Table1", "mysql");
});
});
};

118
scripts/cypress/integration/common/2a_table_with_belongs_to_colulmn.js

@ -1,118 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - Table: belongs to, link record`, () => {
// before(() => {
// cy.restoreLocalStorage();
// cy.openTableTab("Country", 25);
// });
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
// after(() => {
// cy.closeTableTab("City");
// });
it("URL validation", () => {
cy.openTableTab("Country", 25);
// column name validation
// cy.get(`.project-tab:contains(Country):visible`).should("exist");
// URL validation
cy.url().should("contain", `table/Country`);
});
it("Grid cell chip content validation", () => {
// grid cell content validation
mainPage
.getCell("City List", 1)
.find(".nc-virtual-cell > .chips-wrapper > .chips > .group > .name")
.contains("Kabul")
.should("exist");
mainPage
.getCell("City List", 2)
.find(".nc-virtual-cell > .chips-wrapper > .chips > .group > .name")
.contains("Batna")
.should("exist");
mainPage
.getCell("City List", 2)
.find(".nc-virtual-cell > .chips-wrapper > .chips > .group > .name")
.contains("Bchar")
.should("exist");
mainPage
.getCell("City List", 2)
.find(".nc-virtual-cell > .chips-wrapper > .chips > .group > .name")
.contains("Skikda")
.should("exist");
});
it("Expand has-many column", () => {
mainPage
.getCell("City List", 1)
.should("exist")
.trigger("mouseover")
.click();
cy.get(".nc-action-icon").eq(0).should("exist").click({ force: true });
});
it("Expand Link record, validate", () => {
cy.getActiveModal(".nc-modal-child-list")
.find("button:contains(Link to 'City')")
.click()
.then(() => {
// Link record form validation
cy.getActiveModal(".nc-modal-link-record")
.contains("Link record")
.should("exist");
cy.getActiveModal(".nc-modal-link-record")
.find(".nc-reload")
.should("exist");
cy.getActiveModal(".nc-modal-link-record")
.find('button:contains("Add new record")')
.should("exist");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.eq(0)
.contains("A Corua (La Corua)")
.should("exist");
cy.getActiveModal(".nc-modal-link-record")
.find("button.ant-modal-close")
.click();
// .then(() => {
// cy.getActiveModal()
// .find("button.ant-modal-close")
// .click();
// });
});
});
it("Belongs to column, validate", () => {
cy.closeTableTab("Country");
cy.openTableTab("City", 25);
cy.url().should("contain", `table/City`);
// grid cell content validation
mainPage
.getCell("Country", 1)
.find(".nc-virtual-cell > .chips-wrapper > .chips > .group > .name")
.contains("Spain")
.should("exist");
mainPage
.getCell("Country", 2)
.find(".nc-virtual-cell > .chips-wrapper > .chips > .group > .name")
.contains("Saudi Arabia")
.should("exist");
cy.closeTableTab("City");
});
});
};

130
scripts/cypress/integration/common/2b_table_with_m2m_column.js

@ -1,130 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - M2M Column validation`, () => {
// before(() => {
// cy.openTableTab("Actor", 25);
// });
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
// after(() => {
// cy.closeTableTab("Actor");
// });
it("Table column header, URL validation", () => {
cy.openTableTab("Actor", 25);
// column name validation
// cy.get(`.project-tab:contains(Actor):visible`).should("exist");
// URL validation
cy.url().should("contain", `table/Actor`);
});
it("M2m chip content validation on grid", () => {
// grid m2m content validation
mainPage
.getCell("Film List", 1)
.find(".nc-virtual-cell > .chips-wrapper > .chips > .group > .name")
.contains("ACADEMY DINOSAUR")
.should("exist");
mainPage
.getCell("Film List", 1)
.find(".nc-virtual-cell > .chips-wrapper > .chips > .group > .name")
.contains("ANACONDA CONFESSIONS")
.should("exist");
});
it("Expand m2m column", () => {
// expand first row
mainPage
.getCell("Film List", 1)
.should("exist")
.trigger("mouseover")
.get(".nc-action-icon").eq(0).should("exist").click({ force: true });
// GUI-v2 Kludge:
// validations
// cy.getActiveModal().contains("Film").should("exist");
// cy.getActiveModal().find("button.mdi-reload").should("exist");
// cy.getActiveModal()
// .find("button:contains(Link to 'Film')")
// .should("exist");
cy.getActiveModal(".nc-modal-child-list")
.find(".ant-card")
.eq(0)
.contains("ACADEMY DINOSAUR")
.should("exist");
});
it('Expand "Link to" record, validate', () => {
cy.getActiveModal(".nc-modal-child-list")
.find("button:contains(Link to 'Film')")
.click()
.then(() => {
// Link record form validation
cy.getActiveModal(".nc-modal-link-record")
.contains("Link record")
.should("exist");
cy.getActiveModal(".nc-modal-link-record")
.find(".nc-reload")
.should("exist");
cy.getActiveModal(".nc-modal-link-record")
.find('button:contains("Add new record")')
.should("exist");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.eq(0)
.contains("ACE GOLDFINGER")
.should("exist");
cy.getActiveModal(".nc-modal-link-record")
.find("button.ant-modal-close")
.click();
});
});
it("Expand first linked card, validate", () => {
// expand first row
mainPage
.getCell("Film List", 1)
.should("exist")
.trigger("mouseover")
.get(".nc-action-icon").eq(0).should("exist").click({ force: true });
cy.getActiveModal(".nc-modal-child-list")
.find(".ant-card")
.eq(0)
.contains("ACADEMY DINOSAUR", { timeout: 2000 })
.click()
.then(() => {
// Link card validation
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find(".text-lg")
.contains("ACADEMY DINOSAUR")
.should("exist");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find('button:contains("Save row")')
.should("exist");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find('button:contains("Cancel")')
.should("exist");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find('button:contains("Cancel")')
.should("exist")
.click();
cy.getActiveModal().find("button.ant-modal-close").click();
});
cy.closeTableTab("Actor");
});
});
};

198
scripts/cypress/integration/common/3a_filter_sort_fields_operations.js

@ -1,198 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { loginPage } from "../../support/page_objects/navigation";
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - Grid operations`, () => {
// before(() => {
// // loginPage.loginAndOpenProject(apiType, dbType);
//
// // open country table
// cy.openTableTab("Country", 25);
// });
//
// after(() => {
// cy.closeTableTab("Country");
// });
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
it("Check country table - Pagination", () => {
cy.openTableTab("Country", 25);
cy.get(".nc-pagination").should("exist");
// verify > pagination option
mainPage.getPagination(">").click();
mainPage
.getPagination(2)
.should("have.class", "ant-pagination-item-active");
// verify < pagination option
mainPage.getPagination("<").click();
mainPage
.getPagination(1)
.should("have.class", "ant-pagination-item-active");
});
// create new row using + button in header
//
it("Add row using tool header button", () => {
// http://localhost:8080/api/v1/db/meta/audits/comments/count?ids[]=101&ids[]=102&ids[]=103&ids[]=104&ids[]=105&ids[]=106&ids[]=107&ids[]=108&ids[]=109&fk_model_id=md_zfkb9v3mzky958
cy.intercept("/api/v1/db/meta/audits/comments/count*").as(
"waitForPageLoad"
);
// add a row to end of Country table
mainPage.addNewRowExpand("Country");
cy.get(".nc-expand-col-Country")
.find(".nc-cell > input")
.first()
.type("Test Country");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find(".ant-btn-primary")
.contains("Save row")
.should("exist")
.click();
// cy.get("#data-table-form-Country > input")
// .first()
// .type("Test Country");
// cy.contains("Save row").filter("button").click();
cy.toastWait("updated successfully");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find(".ant-btn")
.contains("Cancel")
.should("exist")
.click();
// verify
mainPage.getPagination(5).click();
cy.wait("@waitForPageLoad");
mainPage.getCell("Country", 10).contains("Test Country").should("exist");
});
// delete single row
//
it("Delete Row", () => {
// delete row added in previous step
mainPage.getCell("Country", 10).rightclick();
cy.getActiveMenu(".nc-dropdown-grid-context-menu")
.contains("Delete Row")
.click();
// cy.toastWait('Deleted row successfully')
// verify
cy.get(`:nth-child(10) > [data-title="Country"]`).should("not.exist");
mainPage.getPagination(1).click();
});
// create new row using right click menu option
//
it.skip("Add row using rightclick menu option", () => {
// Temporary
mainPage.getPagination(5).click();
mainPage.getCell("Country", 9).rightclick({ force: true });
cy.getActiveMenu(".nc-dropdown-grid-context-menu")
.contains("Insert New Row")
.click({ force: true });
mainPage
.getCell("Country", 10)
.dblclick()
.find("input")
.type("Test Country-1{enter}");
mainPage.getCell("Country", 10).rightclick({ force: true });
cy.getActiveMenu(".nc-dropdown-grid-context-menu")
.contains("Insert New Row")
.click({ force: true });
mainPage
.getCell("Country", 11)
.dblclick()
.find("input")
.type("Test Country-2{enter}");
// GUI-v2 Kludge:
// to move cursor away from input field; enter key is not recognized
// mainPage.getCell("Country", 10).click()
// verify
mainPage
.getCell("Country", 10)
.contains("Test Country-1")
.should("exist");
mainPage
.getCell("Country", 11)
.contains("Test Country-2")
.should("exist");
});
// delete selected rows (multiple)
//
it.skip("Delete Selected", () => {
cy.get(".ant-checkbox").should("exist").eq(10).click({ force: true });
cy.get(".ant-checkbox").should("exist").eq(11).click({ force: true });
mainPage.getCell("Country", 10).rightclick({ force: true });
cy.getActiveMenu(".nc-dropdown-grid-context-menu")
.contains("Delete Selected Rows")
.click({ force: true });
// verify
// mainPage.getCell("Country", 10).should("not.exist");
// mainPage.getCell("Country", 11).should("not.exist");
cy.get(`:nth-child(10) > [data-title="Country"]`).should("not.exist");
cy.get(`:nth-child(11) > [data-title="Country"]`).should("not.exist");
mainPage.getPagination(1).click();
});
it("Enable sort", () => {
mainPage.sortField("Country", "Z → A");
cy.contains("Zambia").should("exist");
});
it("Disable sort", () => {
mainPage.clearSort();
cy.contains("Zambia").should("not.exist");
});
it("Hide field", () => {
mainPage.hideField("LastUpdate");
});
it("Show field", () => {
mainPage.unhideField("LastUpdate");
});
it("Create Filter", () => {
mainPage.filterField("Country", "is equal", "India");
// cy.get("td:contains(India)").should("exist");
mainPage.getCell("Country", 1).contains("India").should("exist");
});
it("Delete Filter", () => {
// remove sort and check
mainPage.filterReset();
mainPage.getCell("Country", 1).contains("India").should("not.exist");
// cy.contains("td:contains(India)").should("not.exist");
cy.closeTableTab("Country");
});
});
};

369
scripts/cypress/integration/common/3b_formula_column.js

@ -1,369 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { loginPage } from "../../support/page_objects/navigation";
import {
isTestSuiteActive,
isXcdb,
} from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - FORMULA`, () => {
// Run once before test- create project (rest/graphql)
//
// before(() => {
// // loginPage.loginAndOpenProject(apiType, dbType)
// cy.openTableTab("City", 25);
// });
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
// after(() => {
// cy.closeTableTab("City");
// });
// Given rowname & expected result for first 10 entries, validate
// NOTE: Scroll issue with Cypress automation, to fix
// validating partial data, row number 5 to 9
//
const rowValidation = (rowName, result) => {
// scroll back
// cy.get(
// `tbody > :nth-child(1) > [data-col="City"]`
// ).scrollIntoView();
// for (let i = 0; i < 10; i++)
for (let i = 3; i < 5; i++)
mainPage
.getCell(rowName, i + 1)
.contains(result[i].toString())
.should("exist");
// cy.get(`tbody > :nth-child(${i + 1}) > [data-col="${rowName}"]`)
// .contains(result[i].toString())
// .should("exist");
};
// Routine to create a new look up column
//
const addFormulaBasedColumn = (columnName, formula) => {
cy.get(".nc-grid tr > th:last .nc-icon").click({
force: true,
});
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find("input.nc-column-name-input", { timeout: 3000 })
.should("exist")
.clear()
.type(columnName);
// cy.get(".nc-column-type-input").last().click().type("Formula");
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".nc-column-type-input")
.last()
.click()
.type("Formula");
cy.getActiveSelection(".nc-dropdown-column-type")
.find(".ant-select-item-option")
.contains("Formula")
.click();
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find("textarea.nc-formula-input")
.click()
.type(formula, { parseSpecialCharSequences: false });
// cy.get(".ant-btn-primary").contains("Save").should('exist').click();
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".ant-btn-primary:visible")
.contains("Save")
.click();
// cy.toastWait(`Column created`);
cy.closeTableTab("City");
cy.openTableTab("City", 25);
cy.get(`th[data-title="${columnName}"]`).should("exist");
};
// routine to delete column
//
const deleteColumnByName = (columnName) => {
mainPage.deleteColumn(columnName);
};
// routine to edit column
//
const editColumnByName = (oldName, newName, newFormula) => {
cy.get(`th:contains(${oldName}) .nc-icon.ant-dropdown-trigger`)
.trigger("mouseover", { force: true })
.click({ force: true });
// cy.get(".nc-column-edit").click();
// cy.get(".nc-column-edit").should("not.be.visible");
cy.getActiveMenu(".nc-dropdown-column-operations")
.find(".nc-column-edit")
.click();
if (newName !== oldName) {
cy.getActiveMenu(".nc-dropdown-edit-column")
.find("input.nc-column-name-input", { timeout: 3000 })
.should("exist")
.clear()
.type(newName);
}
cy.get("textarea.nc-formula-input")
.click()
.clear()
.type(newFormula, { parseSpecialCharSequences: false });
cy.get(".ant-form-item-explain-error").should('not.exist');
cy.get(".ant-btn-primary").contains("Save").should("exist").click();
// cy.toastWait(`Column created`);
if (newName !== oldName) {
cy.get(`th[data-title="${oldName}"]`).should("not.exist");
}
cy.get(`th[data-title="${newName}"]`).should("exist");
};
// routine to edit a column with Circular Reference
//
const editCircularColumnByName = (columnName, newFormula) => {
cy.get(`th:contains(${columnName}) .nc-icon.ant-dropdown-trigger`)
.trigger("mouseover", { force: true })
.click({ force: true });
cy.getActiveMenu(".nc-dropdown-column-operations")
.find(".nc-column-edit")
.click();
cy.getActiveMenu(".nc-dropdown-edit-column")
.find("input.nc-column-name-input", { timeout: 3000 })
.should("exist");
cy.get("textarea.nc-formula-input")
.click()
.clear()
.type(newFormula, { parseSpecialCharSequences: false });
// clicking the Save button, should NOT submit the form
cy.get(".ant-btn-primary").contains("Save").click();
// therefore we can see the error
cy.get(".ant-form-item-explain-error").contains("Can’t save field because it causes a circular reference");
// then close the form without saving
cy.get(".ant-btn").contains("Cancel").click();
};
///////////////////////////////////////////////////
// Test case
// On City table (from Sakila DB), first 10 entries recorded here for verification
let cityId = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let countryId = [87, 82, 101, 60, 97, 31, 107, 44, 44, 50];
let city = [
"A corua (La Corua)",
"Abha",
"Abu Dhabi",
"Acua",
"Adana",
"Addis Abeba",
"Aden",
"Adoni",
"Ahmadnagar",
"Akishima",
];
// Temporary locally computed expected results
let RESULT_STRING = [];
let RESULT_MATH_0 = [];
let RESULT_MATH_1 = [];
let RESULT_MATH_2 = [];
let RESULT_MATH_3 = [];
let RESULT_WEEKDAY_0 = [];
let RESULT_WEEKDAY_1 = [];
let RESULT_CIRC_REF_0 = [];
let RESULT_CIRC_REF_1 = [];
let RESULT_CIRC_REF_2 = [];
let RESULT_CIRC_REF_0_FINAL = [];
let RESULT_CIRC_REF_2_FINAL = [];
for (let i = 0; i < 10; i++) {
// CONCAT, LOWER, UPPER, TRIM
RESULT_STRING[i] = `${city[i].toUpperCase()}${city[
i
].toLowerCase()}trimmed`;
// ADD, AVG, LEN
RESULT_MATH_0[i] =
cityId[i] +
countryId[i] +
(cityId[i] + countryId[i]) / 2 +
city[i].length;
// CEILING, FLOOR, ROUND, MOD, MIN, MAX
RESULT_MATH_1[i] =
Math.ceil(1.4) +
Math.floor(1.6) +
Math.round(2.5) +
(cityId[i] % 3) +
Math.min(cityId[i], countryId[i]) +
Math.max(cityId[i], countryId[i]);
RESULT_MATH_2[i] =
1.23 + Math.min(2.34, 3.45) + Math.max(2.34, 3.45);
// LOG, EXP, POWER, SQRT
// only integer verification being computed, hence trunc
RESULT_MATH_3[i] = Math.trunc(
Math.log(cityId[i]) +
Math.exp(cityId[i]) +
Math.pow(cityId[i], 3) +
Math.sqrt(countryId[i])
);
// WEEKDAY: starts from Monday
RESULT_WEEKDAY_0[i] = 1;
// WEEKDAY: starts from Sunday
RESULT_WEEKDAY_1[i] = 2;
RESULT_CIRC_REF_0[i] = city[i]
RESULT_CIRC_REF_1[i] = city[i]
RESULT_CIRC_REF_2[i] = city[i] + city[i]
RESULT_CIRC_REF_0_FINAL[i] = city[i] + city[i]
RESULT_CIRC_REF_2_FINAL[i] = city[i] + city[i] + city[i] + city[i]
}
it("Formula: ADD, AVG, LEN", () => {
cy.openTableTab("City", 25);
addFormulaBasedColumn(
"NC_MATH_0",
"ADD({CityId}, {CountryId}) + AVG({CityId}, {CountryId}) + LEN({City})"
);
rowValidation("NC_MATH_0", RESULT_MATH_0);
});
it.skip("Formula: WEEKDAY", () => {
editColumnByName("NC_MATH_0", "NC_WEEKDAY_0", `WEEKDAY("2022-07-19")`);
rowValidation("NC_WEEKDAY_0", RESULT_WEEKDAY_0);
editColumnByName(
"NC_WEEKDAY_0",
"NC_WEEKDAY_1",
`WEEKDAY("2022-07-19", "sunday")`
);
rowValidation("NC_WEEKDAY_1", RESULT_WEEKDAY_1);
});
it("Formula: CONCAT, LOWER, UPPER, TRIM", () => {
editColumnByName(
// "NC_WEEKDAY_1",
"NC_MATH_0",
"NC_STR_1",
`CONCAT(UPPER({City}), LOWER({City}), TRIM(' trimmed '))`
);
rowValidation("NC_STR_1", RESULT_STRING);
});
it("Formula: CEILING, FLOOR, ROUND, MOD, MIN, MAX", () => {
editColumnByName(
"NC_STR_1",
"NC_MATH_1",
`CEILING(1.4) + FLOOR(1.6) + ROUND(2.5) + MOD({CityId}, 3) + MIN({CityId}, {CountryId}) + MAX({CityId}, {CountryId})`
);
rowValidation("NC_MATH_1", RESULT_MATH_1);
});
it("Formula: ROUND with decimals, MIN, MAX", () => {
editColumnByName(
"NC_MATH_1",
"NC_MATH_2",
`ROUND(1.2345, 2) + MIN(2.34, 3.45) + MAX(2.34, 3.45)`
);
rowValidation("NC_MATH_2", RESULT_MATH_2)
})
it("Formula: LOG, EXP, POWER, SQRT", () => {
// if (!isXcdb()) {
if (dbType === "mysql") {
// SQLITE doesnt support LOG, EXP, POWER SQRT construct
editColumnByName(
"NC_MATH_2",
"NC_MATH_3",
`LOG({CityId}) + EXP({CityId}) + POWER({CityId}, 3) + SQRT({CountryId})`
);
rowValidation("NC_MATH_3", RESULT_MATH_3);
}
});
it("Formula: NOW, EDIT & Delete column", () => {
// if (!isXcdb()) editColumnByName("NC_MATH_2", "NC_NOW", `NOW()`);
if (dbType === "mysql") editColumnByName("NC_MATH_3", "NC_NOW", `NOW()`);
else editColumnByName("NC_MATH_2", "NC_NOW", `NOW()`);
deleteColumnByName("NC_NOW");
cy.closeTableTab("City");
});
it("Formula: Circular references", () => {
cy.openTableTab("City", 25);
addFormulaBasedColumn(
"NC_CIRC_REF_0",
"{City}"
);
addFormulaBasedColumn(
"NC_CIRC_REF_1",
"{NC_CIRC_REF_0}"
);
editCircularColumnByName(
"NC_CIRC_REF_0",
"{NC_CIRC_REF_1}"
);
deleteColumnByName("NC_CIRC_REF_1");
deleteColumnByName("NC_CIRC_REF_0");
cy.closeTableTab("City");
});
it("Formula: Duplicated dependencies (neighbours)", () => {
cy.openTableTab("City", 25);
addFormulaBasedColumn(
"NC_CIRC_REF_0",
"{City}"
);
addFormulaBasedColumn(
"NC_CIRC_REF_1",
"{NC_CIRC_REF_0}"
);
addFormulaBasedColumn(
"NC_CIRC_REF_2",
"CONCAT({NC_CIRC_REF_1},{NC_CIRC_REF_1})"
);
rowValidation("NC_CIRC_REF_0", RESULT_CIRC_REF_0);
rowValidation("NC_CIRC_REF_1", RESULT_CIRC_REF_1);
rowValidation("NC_CIRC_REF_2", RESULT_CIRC_REF_2);
editColumnByName(
"NC_CIRC_REF_0",
"NC_CIRC_REF_0",
"CONCAT({City},{City})"
);
rowValidation("NC_CIRC_REF_0", RESULT_CIRC_REF_0_FINAL);
rowValidation("NC_CIRC_REF_2", RESULT_CIRC_REF_2_FINAL);
deleteColumnByName("NC_CIRC_REF_2");
deleteColumnByName("NC_CIRC_REF_1");
deleteColumnByName("NC_CIRC_REF_0");
cy.closeTableTab("City");
});
});
};

110
scripts/cypress/integration/common/3c_lookup_column.js

@ -1,110 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - LookUp column`, () => {
// to retrieve few v-input nodes from their label
//
const fetchParentFromLabel = (label) => {
cy.get("label").contains(label).parents(".ant-row").click();
};
// before(() => {
// });
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
// after(() => {
// cy.closeTableTab("City");
// });
// Routine to create a new look up column
//
const addLookUpColumn = (childTable, childCol) => {
cy.get(".nc-grid tr > th:last .nc-icon").click({
force: true,
});
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find("input.nc-column-name-input")
.should("exist")
.clear()
.type(childCol);
// cy.get(".nc-column-type-input").last().click().type("Lookup");
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".nc-column-type-input")
.last()
.click()
.type("Lookup");
cy.getActiveSelection(".nc-dropdown-column-type")
.find(".ant-select-item-option")
.contains("Lookup")
.click();
cy.inputHighlightRenderWait();
// Configure Child table & column names
fetchParentFromLabel("Child table");
cy.getActiveSelection(".nc-dropdown-relation-table")
.find(".ant-select-item-option")
.contains(childTable)
.click();
fetchParentFromLabel("Child column");
cy.getActiveSelection(".nc-dropdown-relation-column")
.find(".ant-select-item-option")
.contains(childCol)
.click();
// cy.get(".ant-btn-primary").contains("Save").should('exist').click();
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".ant-btn-primary:visible")
.contains("Save")
.click();
cy.toastWait(`Column created`);
cy.get(`th[data-title="${childCol}"]`).should("exist");
};
// routine to delete column
//
const deleteColumnByName = (childCol) => {
mainPage.deleteColumn(childCol);
};
///////////////////////////////////////////////////
// Test case
it("Add Lookup column (Address, PostalCode) & Delete", () => {
cy.openTableTab("City", 25);
addLookUpColumn("Address", "PostalCode");
// Verify first entry, will be displayed as alias here 'childColumn (from childTable)'
mainPage.getCell("PostalCode", 1).contains("4166").should("exist");
deleteColumnByName("PostalCode");
cy.closeTableTab("City");
});
it.skip("Add Lookup column (Country, CountryId) & Delete", () => {
addLookUpColumn("Country", "CountryId");
// Verify first entry, will be displayed as alias here 'childColumn (from childTable)'
cy.get(`tbody > :nth-child(1) > [data-col="CountryId"]`)
.contains("87")
.should("exist");
deleteColumnByName("CountryId");
});
});
};

147
scripts/cypress/integration/common/3d_rollup_column.js

@ -1,147 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - RollUp column`, () => {
// to retrieve few v-input nodes from their label
//
const fetchParentFromLabel = (label) => {
cy.get("label").contains(label).parents(".ant-row").click();
};
// before(() => {
// });
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
// after(() => {
// cy.closeTableTab("Country");
// });
// Routine to create a new look up column
//
const addRollUpColumn = (
columnName,
childTable,
childCol,
aggregateFunc
) => {
cy.get(".nc-grid tr > th:last .nc-icon").click({
force: true,
});
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find("input.nc-column-name-input")
.should("exist")
.clear()
.type(columnName);
// cy.get(".nc-column-type-input").last().click().type("RollUp");
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".nc-column-type-input")
.last()
.click()
.type("RollUp");
cy.getActiveSelection(".nc-dropdown-column-type")
.find(".ant-select-item-option")
.contains("Rollup")
.click();
cy.inputHighlightRenderWait();
// Configure Child table & column names
fetchParentFromLabel("Child table");
cy.getActiveSelection(".nc-dropdown-relation-table")
.find(".ant-select-item-option")
.contains(childTable)
.click();
fetchParentFromLabel("Child column");
cy.getActiveSelection(".nc-dropdown-relation-column")
.find(".ant-select-item-option")
.contains(childCol)
.click();
fetchParentFromLabel("Aggregate function");
cy.getActiveSelection(".nc-dropdown-rollup-function")
.find(".ant-select-item-option")
.contains(aggregateFunc)
.click();
// cy.get(".ant-btn-primary").contains("Save").should('exist').click();
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".ant-btn-primary:visible")
.contains("Save")
.click();
cy.toastWait(`Column created`);
cy.get(`th[data-title="${columnName}"]`).should("exist");
};
// routine to delete column
//
const deleteColumnByName = (columnName) => {
mainPage.deleteColumn(columnName);
};
// routine to edit column
//
// const editColumnByName = (oldName, newName) => {
// // verify if column exists before delete
// cy.get(`th:contains(${oldName})`).should("exist");
//
// // delete opiton visible on mouse-over
// cy.get(`th:contains(${oldName}) .mdi-menu-down`)
// .trigger("mouseover")
// .click();
//
// // edit/ save on pop-up
// cy.get(".nc-column-edit").click();
// cy.get(".nc-column-name-input input").clear().type(newName);
// cy.get(".nc-col-create-or-edit-card").contains("Save").click();
//
// cy.toastWait("Successfully updated alias");
//
// // validate if deleted (column shouldnt exist)
// cy.get(`th:contains(${oldName})`).should("not.exist");
// cy.get(`th:contains(${newName})`).should("exist");
// };
///////////////////////////////////////////////////
// Test case
it("Add Rollup column (City, City, count) & Delete", () => {
cy.openTableTab("Country", 25);
addRollUpColumn("RollUpCol", "City", "City", "count");
// Verify first entry, will be displayed as alias here 'childColumn (from childTable)'
// intentionally verifying 4th item, as initial items are being masked out by list scroll down
mainPage.getCell("RollUpCol", 4).contains("2").should("exist");
// editColumnByName("RollUpCol_2", "RollUpCol_New");
deleteColumnByName("RollUpCol");
cy.closeTableTab("Country");
});
it.skip("Add Rollup column (City, CountryId, count) & Delete", () => {
addRollUpColumn("RollUpCol_1", "City", "CountryId", "count");
// Verify first entry, will be displayed as alias here 'childColumn (from childTable)'
cy.get(`tbody > :nth-child(4) > [data-col="RollUpCol_1"]`)
.contains("2")
.should("exist");
// editColumnByName("RollUpCol_1", "RollUpCol_New");
deleteColumnByName("RollUpCol_New");
});
});
};

296
scripts/cypress/integration/common/3e_duration_column.js

@ -1,296 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - DURATION`, () => {
const tableName = "DurationTable";
// to retrieve few v-input nodes from their label
//
const fetchParentFromLabel = (label) => {
cy.get("label").contains(label).parents(".ant-row").first().click();
};
before(() => {
cy.restoreLocalStorage();
cy.createTable(tableName);
cy.saveLocalStorage();
});
// Run once before test- create table
//
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
cy.restoreLocalStorage();
cy.deleteTable(tableName);
cy.saveLocalStorage();
});
// Routine to create a new look up column
//
const addDurationColumn = (columnName, durationFormat) => {
cy.get(".nc-grid tr > th:last .nc-icon").click({
force: true,
});
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find("input.nc-column-name-input", { timeout: 3000 })
.should("exist")
.clear()
.type(columnName);
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".nc-column-type-input")
.last()
.click()
.type("Duration");
cy.getActiveSelection(".nc-dropdown-column-type")
.find(".ant-select-item-option")
.contains("Duration")
.click();
cy.inputHighlightRenderWait();
// Configure Duration format
fetchParentFromLabel("Duration Format");
cy.getActiveSelection(".nc-dropdown-duration-option")
.find(".ant-select-item-option")
.contains(durationFormat)
.click();
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".ant-btn-primary:visible")
.contains("Save")
.click();
cy.toastWait(`Column created`);
cy.get(`th[data-title="${columnName}"]`).should("exist");
};
// routine to delete column
//
const deleteColumnByName = (columnName) => {
mainPage.deleteColumn(columnName);
};
// routine to edit column
//
const editColumnByName = (oldName, newName, newDurationFormat) => {
cy.get(`th:contains(${oldName}) .nc-icon.ant-dropdown-trigger`)
.trigger("mouseover", { force: true })
.click({ force: true });
cy.getActiveMenu(".nc-dropdown-column-operations")
.find(".nc-column-edit")
.click();
// rename column and verify
cy.getActiveMenu(".nc-dropdown-edit-column")
.find("input.nc-column-name-input", { timeout: 3000 })
.should("exist")
.clear()
.type(newName);
cy.inputHighlightRenderWait();
// Configure Duration format
fetchParentFromLabel("Duration Format");
cy.getActiveSelection(".nc-dropdown-duration-option")
.find(".ant-select-item-option")
.contains(newDurationFormat)
.click();
cy.getActiveMenu(".nc-dropdown-edit-column")
.find(".ant-btn-primary:visible")
.contains("Save")
.click();
cy.toastWait("Column updated");
cy.get(`th:contains(${oldName})`).should("not.exist");
cy.get(`th:contains(${newName})`).should("exist");
};
const addDurationData = (
colName,
index,
cellValue,
expectedValue,
isNewRow = false
) => {
if (isNewRow) {
mainPage.addNewRowExpand("DurationTable");
} else {
// mainPage.getRow(index).find(".nc-row-expand-icon").click({ force: true });
cy.get(".nc-row-expand")
.eq(index - 1)
.click({ force: true });
}
cy.get(".duration-cell-wrapper > input")
.first()
.should("exist")
.type(cellValue);
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Save row")
.should("exist")
.click();
cy.toastWait("Row updated successfully");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Cancel")
.should("exist")
.click();
mainPage.getCell(colName, index).contains(expectedValue).should("exist");
};
///////////////////////////////////////////////////
// Test case
{
// Duration: h:mm
it("Duration: h:mm", () => {
addDurationColumn("NC_DURATION_0", "h:mm (e.g. 1:23)");
addDurationData("NC_DURATION_0", 1, "1:30", "01:30", true);
addDurationData("NC_DURATION_0", 2, "30", "00:30", true);
addDurationData("NC_DURATION_0", 3, "60", "01:00", true);
addDurationData("NC_DURATION_0", 4, "80", "01:20", true);
addDurationData("NC_DURATION_0", 5, "12:34", "12:34", true);
addDurationData("NC_DURATION_0", 6, "15:130", "17:10", true);
addDurationData("NC_DURATION_0", 7, "123123", "2052:03", true);
});
it("Duration: Edit Column NC_DURATION_0", () => {
editColumnByName(
"NC_DURATION_0",
"NC_DURATION_EDITED_0",
"h:mm:ss (e.g. 3:45, 1:23:40)"
);
});
it("Duration: Delete column", () => {
deleteColumnByName("NC_DURATION_EDITED_0");
});
}
{
// Duration: h:mm:ss
it("Duration: h:mm:ss", () => {
addDurationColumn("NC_DURATION_1", "h:mm:ss (e.g. 3:45, 1:23:40)");
addDurationData("NC_DURATION_1", 1, "11:22:33", "11:22:33");
addDurationData("NC_DURATION_1", 2, "1234", "00:20:34");
addDurationData("NC_DURATION_1", 3, "50", "00:00:50");
addDurationData("NC_DURATION_1", 4, "1:1111", "00:19:31");
addDurationData("NC_DURATION_1", 5, "1:11:1111", "01:29:31");
addDurationData("NC_DURATION_1", 6, "15:130", "00:17:10");
addDurationData("NC_DURATION_1", 7, "123123", "34:12:03");
});
it("Duration: Edit Column NC_DURATION_1", () => {
editColumnByName(
"NC_DURATION_1",
"NC_DURATION_EDITED_1",
"h:mm:ss.s (e.g. 3:34.6, 1:23:40.0)"
);
});
it("Duration: Delete column", () => {
deleteColumnByName("NC_DURATION_EDITED_1");
});
}
{
// h:mm:ss.s
it("Duration: h:mm:ss.s", () => {
addDurationColumn(
"NC_DURATION_2",
"h:mm:ss.s (e.g. 3:34.6, 1:23:40.0)"
);
addDurationData("NC_DURATION_2", 1, "1234", "00:20:34.0");
addDurationData("NC_DURATION_2", 2, "12:34", "00:12:34.0");
addDurationData("NC_DURATION_2", 3, "12:34:56", "12:34:56.0");
addDurationData("NC_DURATION_2", 4, "12:34:999", "12:50:39.0");
addDurationData("NC_DURATION_2", 5, "12:999:56", "28:39:56.0");
addDurationData("NC_DURATION_2", 6, "12:34:56.12", "12:34:56.1");
addDurationData("NC_DURATION_2", 7, "12:34:56.199", "12:34:56.2");
});
it("Duration: Edit Column NC_DURATION_2", () => {
editColumnByName(
"NC_DURATION_2",
"NC_DURATION_EDITED_2",
"h:mm:ss (e.g. 3:45, 1:23:40)"
);
});
it("Duration: Delete column", () => {
deleteColumnByName("NC_DURATION_EDITED_2");
});
}
{
// h:mm:ss.ss
it("Duration: h:mm:ss.ss", () => {
addDurationColumn(
"NC_DURATION_3",
"h:mm:ss.ss (e.g. 3.45.67, 1:23:40.00)"
);
addDurationData("NC_DURATION_3", 1, "1234", "00:20:34.00");
addDurationData("NC_DURATION_3", 2, "12:34", "00:12:34.00");
addDurationData("NC_DURATION_3", 3, "12:34:56", "12:34:56.00");
addDurationData("NC_DURATION_3", 4, "12:34:999", "12:50:39.00");
addDurationData("NC_DURATION_3", 5, "12:999:56", "28:39:56.00");
addDurationData("NC_DURATION_3", 6, "12:34:56.12", "12:34:56.12");
addDurationData("NC_DURATION_3", 7, "12:34:56.199", "12:34:56.20");
});
it("Duration: Edit Column NC_DURATION_3", () => {
editColumnByName(
"NC_DURATION_3",
"NC_DURATION_EDITED_3",
"h:mm:ss.ss (e.g. 3.45.67, 1:23:40.00)"
);
});
it("Duration: Delete column", () => {
deleteColumnByName("NC_DURATION_EDITED_3");
});
}
{
// h:mm:ss.sss
it("Duration: h:mm:ss.sss", () => {
addDurationColumn(
"NC_DURATION_4",
"h:mm:ss.sss (e.g. 3.45.678, 1:23:40.000)"
);
addDurationData("NC_DURATION_4", 1, "1234", "00:20:34.000");
addDurationData("NC_DURATION_4", 2, "12:34", "00:12:34.000");
addDurationData("NC_DURATION_4", 3, "12:34:56", "12:34:56.000");
addDurationData("NC_DURATION_4", 4, "12:34:999", "12:50:39.000");
addDurationData("NC_DURATION_4", 5, "12:999:56", "28:39:56.000");
addDurationData("NC_DURATION_4", 6, "12:34:56.12", "12:34:56.012");
addDurationData("NC_DURATION_4", 7, "12:34:56.199", "12:34:56.199");
});
it("Duration: Edit Column NC_DURATION_4", () => {
editColumnByName(
"NC_DURATION_4",
"NC_DURATION_EDITED_4",
"h:mm (e.g. 1:23)"
);
});
it("Duration: Delete column", () => {
deleteColumnByName("NC_DURATION_EDITED_4");
});
}
});
};

380
scripts/cypress/integration/common/3f_link_to_another_record.js

@ -1,380 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { loginPage } from "../../support/page_objects/navigation";
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
// tbd: this needs a proper fix
let waitTime = 0;
let clear;
describe(`${apiType.toUpperCase()} api - Link to another record`, () => {
function fetchParentFromLabel(label) {
cy.get("label").contains(label).parents(".ant-row").click();
}
// Insert new row
function addRow(index, cellValue) {
cy.get(".nc-grid-add-new-cell").should("exist").click();
mainPage
.getCell("Title", index)
.dblclick()
.then(($el) => {
cy.wrap($el).find("input").clear().type(`${cellValue}{enter}`);
});
mainPage.getCell("Title", index).contains(cellValue).should("exist");
}
// Insert LTAR column
//
function addLtarColumn(columnName, foreignTable, relationType) {
// + icon
cy.get(".nc-grid tr > th:last .nc-icon").click();
// Column name
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find("input.nc-column-name-input", { timeout: 3000 })
.should("exist")
.clear()
.type(columnName);
// Column type
// cy.get(".nc-column-type-input").last()
// .click()
// .type("Link");
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".nc-column-type-input")
.last()
.click()
.type("Link");
cy.getActiveSelection(".nc-dropdown-column-type")
.find(".ant-select-item-option")
.contains("LinkToAnotherRecord")
.click();
// relation type (hm/ mm)
cy.get(".nc-ltar-relation-type")
.find(".ant-radio")
.eq(relationType === "hm" ? 0 : 1)
.click();
// Foreign table
fetchParentFromLabel("Child table");
cy.get(".nc-ltar-child-table").last().click().type(foreignTable);
cy.getActiveSelection(".nc-dropdown-ltar-child-table")
.find(".ant-select-item-option")
.contains(foreignTable)
.click();
// Save
// cy.get(".ant-btn-primary")
// .contains("Save")
// .should('exist')
// .click();
cy.getActiveMenu(".nc-dropdown-grid-add-column")
.find(".ant-btn-primary:visible")
.contains("Save")
.click();
// Toast
cy.toastWait(`Column created`);
// Verify
cy.get(`th[data-title="${columnName}"]`).should("exist");
}
// Content verification for LTAR cell
// Validates only 1st chip contents
//
function verifyLtarCell(columnName, index, cellValue) {
cy.get(`:nth-child(${index}) > [data-title="${columnName}"]`)
.find(".chip")
.eq(0)
.contains(cellValue)
.should("exist");
}
// Unlink LTAR cell
//
function ltarUnlink(columnName, index) {
// http://localhost:8080/api/v1/db/meta/audits/comments/count?ids[]=1&fk_model_id=md_f4y7jp89pe8vkt
cy.intercept("GET", `/api/v1/db/meta/audits/comments/count?**`).as(
"unlinkCount"
);
// Click on cell to enable unlink icon
cy.get(`:nth-child(${index}) > [data-title="${columnName}"]`)
.last()
.click();
// Click on unlink icon
cy.get(`:nth-child(${index}) > [data-title="${columnName}"]`)
.last()
.find(".unlink-icon")
.should("exist")
.click();
// Glitch; hence wait
cy.wait("@unlinkCount");
}
before(() => {
cy.restoreLocalStorage();
clear = Cypress.LocalStorage.clear;
Cypress.LocalStorage.clear = () => {};
});
afterEach(() => {
cy.saveLocalStorage();
});
beforeEach(() => {
cy.restoreLocalStorage();
});
after(() => {
// Cleanup
//
cy.restoreLocalStorage();
cy.openTableTab("Sheet1", 0);
mainPage.deleteColumn("Link1-2hm");
mainPage.deleteColumn("Link1-2mm");
mainPage.deleteColumn("Sheet2");
cy.deleteTable("Sheet1");
cy.deleteTable("Sheet2");
cy.saveLocalStorage();
Cypress.LocalStorage.clear = clear;
});
///////////////////////////////////////////////////
// Test case
it("Create Link columns", () => {
cy.createTable("Sheet1");
cy.createTable("Sheet2");
cy.openTableTab("Sheet1", 0);
addRow(1, "1a");
addRow(2, "1b");
addRow(3, "1c");
addLtarColumn("Link1-2hm", "Sheet2", "hm");
addLtarColumn("Link1-2mm", "Sheet2", "mm");
cy.closeTableTab("Sheet1");
cy.openTableTab("Sheet2", 0);
addLtarColumn("Link2-1hm", "Sheet1", "hm");
cy.closeTableTab("Sheet2");
// Sheet2 now has all 3 column categories : HM, BT, MM
//
});
// Expand form [Add new row]
//
it("Add HM, BT, MM Link, Expand form", () => {
// http://localhost:8080/api/v1/db/data/noco/p_0l53e1xgsxlecb/md_mn4xgb2jnq16a7?limit=10&offset=0&where=&fields[]=Title&fields[]=Id
cy.intercept("GET", `/api/v1/db/data/noco/**`).as("waitForCardLoad");
cy.openTableTab("Sheet2", 0);
// Click on `Add new row` button
mainPage.addNewRowExpand("Sheet2");
// Title
cy.get(".nc-expand-col-Title")
.find(".nc-cell > input")
.should("exist")
.first()
.clear()
.type("2a");
// trigger("mouseover") is required to show the + icon
// didn't seem to work. As a kludge, used click with {force:true}
// additional delay ensures card contents are available before clicking
//
// BT
cy.get(".nc-expand-col-Sheet1")
.find(".nc-action-icon")
.should("exist")
.click({ force: true });
cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(0)
.click();
// MM
cy.get(".nc-expand-col-Sheet1.List").find(".ant-btn-primary").click();
cy.wait("@waitForCardLoad");
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(0)
.click();
// HM
cy.get(".nc-expand-col-Link2-1hm").find(".ant-btn-primary").click();
cy.wait("@waitForCardLoad");
cy.getActiveModal().find(".ant-card").should("exist").eq(0).click();
// Save row
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Save row")
.should("exist")
.click();
// Toast
cy.toastWait("updated successfully");
// Close modal
cy.get("body").type("{esc}");
});
// In cell insert
it("Add HM, BT, MM Link, In cell form", () => {
// Insert row with `Title` field, rest of links are empty
addRow(2, "2b");
// BT
mainPage
.getCell("Sheet1", 2)
.find(".nc-action-icon")
.click({ force: true });
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(1)
.click();
// MM
mainPage
.getCell("Sheet1 List", 2)
.find(".nc-action-icon")
.last()
.click({ force: true });
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(1)
.click();
// HM
mainPage
.getCell("Link2-1hm", 2)
.find(".nc-action-icon")
.last()
.click({ force: true });
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(1)
.click();
});
// Existing row, expand record
it("Add HM, BT, MM Link, expand record", () => {
// http://localhost:8080/api/v1/db/data/noco/p_0l53e1xgsxlecb/md_mn4xgb2jnq16a7?limit=10&offset=0&where=&fields[]=Title&fields[]=Id
cy.intercept("GET", `/api/v1/db/data/noco/**`).as("waitForCardLoad");
addRow(3, "2c");
// kludge; remove empty record in the end
mainPage.getCell("Title", 3).click();
mainPage.getCell("Title", 4).rightclick();
// delete row
cy.getActiveMenu(".nc-dropdown-grid-context-menu")
.find('.ant-dropdown-menu-item:contains("Delete Row")')
.first()
.click();
cy.get(".nc-row-expand").eq(2).click({ force: true });
// wait for page render to complete
cy.get('button:contains("Save row"):visible').should("exist");
// BT
cy.get(".nc-expand-col-Sheet1")
.find(".nc-action-icon")
.should("exist")
.click({ force: true });
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(2)
.click();
// MM
cy.get(".nc-expand-col-Sheet1.List").find(".ant-btn-primary").click();
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(2)
.click();
// HM
cy.get(".nc-expand-col-Link2-1hm").find(".ant-btn-primary").click();
cy.getActiveModal(".nc-modal-link-record")
.find(".ant-card")
.should("exist")
.eq(2)
.click();
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Save row")
.should("exist")
.click();
// cy.toastWait("updated successfully");
cy.toastWait("No columns to update");
cy.get("body").type("{esc}");
verifyLtarCell("Sheet1", 1, "1a");
verifyLtarCell("Sheet1", 2, "1b");
verifyLtarCell("Sheet1", 3, "1c");
verifyLtarCell("Sheet1 List", 1, "1a");
verifyLtarCell("Sheet1 List", 2, "1b");
verifyLtarCell("Sheet1 List", 3, "1c");
verifyLtarCell("Link2-1hm", 1, "1a");
verifyLtarCell("Link2-1hm", 2, "1b");
verifyLtarCell("Link2-1hm", 3, "1c");
cy.closeTableTab("Sheet2");
});
it("Verification", () => {
cy.openTableTab("Sheet1", 3);
verifyLtarCell("Link1-2hm", 1, "2a");
verifyLtarCell("Link1-2hm", 2, "2b");
verifyLtarCell("Link1-2hm", 3, "2c");
verifyLtarCell("Link1-2mm", 1, "2a");
verifyLtarCell("Link1-2mm", 2, "2b");
verifyLtarCell("Link1-2mm", 3, "2c");
verifyLtarCell("Sheet2", 1, "2a");
verifyLtarCell("Sheet2", 2, "2b");
verifyLtarCell("Sheet2", 3, "2c");
cy.closeTableTab("Sheet1");
});
it("Unlink", () => {
cy.openTableTab("Sheet1", 3);
ltarUnlink("Link1-2hm", 1);
ltarUnlink("Link1-2hm", 2);
ltarUnlink("Link1-2hm", 3);
ltarUnlink("Link1-2mm", 1);
ltarUnlink("Link1-2mm", 2);
ltarUnlink("Link1-2mm", 3);
ltarUnlink("Sheet2", 1);
ltarUnlink("Sheet2", 2);
ltarUnlink("Sheet2", 3);
cy.closeTableTab("Sheet1");
});
});
};

95
scripts/cypress/integration/common/4a_table_view_grid_gallery_form.js

@ -1,95 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - Table views: Create/Edit/Delete`, () => {
const name = "Test" + Date.now();
// Run once before test- create project (rest/graphql)
//
before(() => {
cy.restoreLocalStorage();
// open a table to work on views
//
cy.openTableTab("Country", 25);
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
cy.restoreLocalStorage();
cy.closeTableTab("Country");
cy.saveLocalStorage();
});
// Common routine to create/edit/delete GRID & GALLERY view
// Input: viewType - 'grid'/'gallery'
//
const viewTest = (viewType) => {
it(`Create ${viewType} view`, () => {
// click on 'Grid/Gallery' button on Views bar
cy.get(`.nc-create-${viewType}-view`).click();
// Pop up window, click Submit (accepting default name for view)
cy.getActiveModal(".nc-modal-view-create")
.find(".ant-btn-primary")
.click();
cy.toastWait("View created successfully");
// validate if view was created && contains default name 'Country1'
cy.get(`.nc-${viewType}-view-item`)
.contains(`${capitalizeFirstLetter(viewType)}-1`)
.should("exist");
});
it(`Edit ${viewType} view name`, () => {
// click on edit-icon (becomes visible on hovering mouse)
cy.get(`.nc-${viewType}-view-item`).last().dblclick();
// feed new name
cy.get(`.nc-${viewType}-view-item input`)
.clear()
.type(`${viewType}View-1{enter}`);
cy.toastWait("View renamed successfully");
// validate
cy.get(`.nc-${viewType}-view-item`)
.contains(`${viewType}View-1`)
.should("exist");
});
it(`Delete ${viewType} view`, () => {
// number of view entries should be 2 before we delete
cy.get(".nc-view-item").its("length").should("eq", 2);
// click on delete icon (becomes visible on hovering mouse)
cy.get(".nc-view-delete-icon").click({ force: true });
cy.getActiveModal(".nc-modal-view-delete")
.find(".ant-btn-dangerous")
.click();
cy.toastWait("View deleted successfully");
// confirm if the number of veiw entries is reduced by 1
cy.get(".nc-view-item").its("length").should("eq", 1);
});
};
// below four scenario's will be invoked twice, once for rest & then for graphql
viewTest("grid"); // grid view
viewTest("gallery"); // gallery view
viewTest("form"); // form view
});
};

120
scripts/cypress/integration/common/4b_table_view_share.js

@ -1,120 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
import { loginPage } from "../../support/page_objects/navigation";
let storedURL = "";
let linkText = "";
const generateLinkWithPwd = () => {
mainPage.shareView().click();
cy.getActiveModal(".nc-modal-share-view")
.find(".ant-modal-title")
.contains("This view is shared via a private link")
.should("be.visible");
// enable checkbox & feed pwd, save
cy.get('[data-cy="nc-modal-share-view__with-password"]').click();
cy.get('[data-cy="nc-modal-share-view__password"]').clear().type('1')
cy.get('[data-cy="nc-modal-share-view__save-password"]').click();
cy.toastWait("Successfully updated");
// copy link text, visit URL
cy.get('[data-cy="nc-modal-share-view__link"]').then(($el) => {
linkText = $el.text();
// todo: visit url?
cy.log(linkText);
})
};
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - Shared VIEWs (GRID)`, () => {
// Run once before test- create project (rest/graphql)
//
before(() => {
cy.restoreLocalStorage();
cy.openTableTab("City", 25);
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
cy.restoreLocalStorage();
cy.closeTableTab("City");
cy.saveLocalStorage();
});
it("Generate link with password", () => {
// store base URL- to re-visit and delete form view later
cy.url().then((url) => {
storedURL = url;
});
generateLinkWithPwd();
cy.signOut();
});
it("Share view with incorrect password", () => {
cy.visit(linkText, {
baseUrl: null,
});
cy.getActiveModal(".nc-modal-shared-view-password-dlg").should("exist");
// feed password
cy.getActiveModal(".nc-modal-shared-view-password-dlg")
.find('input[type="password"]')
.clear()
.type("a");
cy.getActiveModal(".nc-modal-shared-view-password-dlg")
.find('button:contains("Unlock")')
.click();
// if pwd is incorrect, active modal requesting to feed in password again will persist
cy.getActiveModal(".nc-modal-shared-view-password-dlg")
.find('button:contains("Unlock")')
.should("exist");
});
// fallover test- use previously opened view & continue verification instead of opening again
it("Share view with correct password", () => {
// feed password
cy.getActiveModal(".nc-modal-shared-view-password-dlg")
.find('input[type="password"]')
.clear()
.type("1");
cy.getActiveModal(".nc-modal-shared-view-password-dlg")
.find('button:contains("Unlock")')
.click();
// if pwd is incorrect, active modal requesting to feed in password again will persist
// cy.getActiveModal().find('button:contains("Unlock")').should('not.exist');
// cy.get(".ant-modal-content:visible").should("not.exist")
cy.wait(1000);
// Verify Download as CSV is here
mainPage.downloadCsv().should("exist");
cy.get(".nc-actions-menu-btn").should("exist").click();
mainPage.downloadExcel().should("exist");
cy.get(".nc-actions-menu-btn").should("exist").click();
});
it("Delete view", () => {
loginPage.loginAndOpenProject(apiType, dbType);
cy.openTableTab("City", 25);
// wait for page load to complete
cy.get(".nc-grid-row").should("have.length", 25);
mainPage.deleteCreatedViews();
});
});
};

406
scripts/cypress/integration/common/4c_form_view_detailed.js

@ -1,406 +0,0 @@
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
import { mainPage, settingsPage } from "../../support/page_objects/mainPage";
import { loginPage } from "../../support/page_objects/navigation";
let formViewURL;
function verifyFormDrawerFieldLocation(fieldName, position) {
cy.get(".nc-editable.item").eq(position).contains(fieldName).should("exist");
}
function verifyFormDrawerHideObjectCount(count) {
if (count) {
cy.get(".nc-form")
.find(".nc-field-remove-icon")
.its("length")
.should("eq", count);
} else {
cy.get(".nc-form").find(".nc-field-remove-icon").should("not.exist");
}
}
function verifyFormMenuDrawerCardCount(cardCount) {
if (cardCount) {
cy.get(".nc-form-left-drawer")
.find(".ant-card")
.should("have.length", cardCount);
} else {
cy.get(".nc-form-left-drawer").find(".ant-card").should("not.exist");
}
}
function validateFormHeader() {
cy.get(".nc-form").should("exist");
cy.get(".nc-form")
.find('[placeholder="Form Title"]')
.should("exist")
.then(($el) => {
cy.log($el);
expect($el.val()).to.equal("A B C D");
});
cy.get(".nc-form")
.find('[placeholder="Add form description"]')
.should("exist")
.then(($el) => {
cy.log($el);
expect($el.val()).to.equal("Some description about form comes here");
});
}
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - FORM view`, () => {
const name = "Test" + Date.now();
// Run once before test- create project (rest/graphql)
//
before(() => {
// standalone test
// loginPage.loginAndOpenProject(apiType, dbType);
// open a table to work on views
//
cy.restoreLocalStorage();
cy.openTableTab("Country", 25);
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
cy.restoreLocalStorage();
cy.closeTableTab("Country");
cy.saveLocalStorage();
});
// Common routine to create/edit/delete GRID & GALLERY view
// Input: viewType - 'grid'/'gallery'
//
const viewTest = (viewType) => {
it(`Create ${viewType} view`, () => {
// click on 'Grid/Gallery' button on Views bar
cy.get(`.nc-create-${viewType}-view`).click();
// Pop up window, click Submit (accepting default name for view)
cy.getActiveModal(".nc-modal-view-create")
.find("button:contains(Submit)")
.click();
cy.toastWait("View created successfully");
// validate if view was creted && contains default name 'Form-1'
cy.get(`.nc-view-item.nc-${viewType}-view-item`)
.contains("Form-1")
.should("exist");
});
it(`Validate ${viewType} view: Drag & drop for re-order items`, () => {
// default order: Country, LastUpdate, Country => City
verifyFormDrawerFieldLocation("Country", 0);
verifyFormDrawerFieldLocation("LastUpdate", 1);
// move Country field down (drag, drop)
cy.get(".nc-form-drag-LastUpdate").drag(".nc-form-drag-Country");
cy.wait(1000);
// Verify if order is: LastUpdate, Country, Country => City
verifyFormDrawerFieldLocation("LastUpdate", 0);
verifyFormDrawerFieldLocation("Country", 1);
});
it(`Validate ${viewType} view: Drag & drop for add/remove items`, () => {
// default, only one item in menu-bar; ensure LastUpdate field was present in form view
verifyFormMenuDrawerCardCount(0);
verifyFormDrawerFieldLocation("LastUpdate", 0);
// drag 'LastUpdate' & drop into menu bar drag-drop box
cy.get(".nc-form-drag-LastUpdate").drag(".nc-drag-n-drop-to-hide");
// validate- fields count in menu bar to be increased by 1 &&
// first member in 'formView' is Country
verifyFormDrawerFieldLocation("Country", 0);
verifyFormMenuDrawerCardCount(1);
});
it(`Validate ${viewType} view: Inverted order field member addition from menu`, () => {
cy.get(".nc-form-remove-all").click();
verifyFormMenuDrawerCardCount(2);
// click fields in inverted order: LastUpdate, Country => City
cy.get(".nc-form-left-drawer").find(".ant-card").eq(1).click();
verifyFormMenuDrawerCardCount(1);
cy.get(".nc-form-left-drawer").find(".ant-card").eq(0).click();
// verify if order of appearance in form is right
// Country was never removed as its required field. Other two will appear in inverted order
verifyFormMenuDrawerCardCount(0);
verifyFormDrawerFieldLocation("Country", 0);
verifyFormDrawerFieldLocation("City List", 1);
verifyFormDrawerFieldLocation("LastUpdate", 2);
});
it(`Validate ${viewType}: Form header & description validation`, () => {
// Header & description should exist
cy.get(".nc-form").find('[placeholder="Form Title"]').should("exist");
cy.get(".nc-form")
.find('[placeholder="Add form description"]')
.should("exist");
// Update header & add some description, verify
cy.get(".nc-form")
.find('[placeholder="Form Title"]')
.clear()
.type("A B C D");
cy.get(".nc-form")
.find('[placeholder="Add form description"]')
.type("Some description about form comes here");
cy.get(".nc-form").click();
// validate new contents
validateFormHeader();
});
it(`Validate ${viewType}: Add all, Remove all validation`, () => {
// ensure buttons exist on left hand menu
cy.get(".nc-form-left-drawer")
.find(".nc-form-add-all")
.should("not.exist");
cy.get(".nc-form-left-drawer")
.find(".nc-form-remove-all")
.should("be.visible");
// click: remove-all
cy.get(".nc-form-left-drawer").find(".nc-form-remove-all").click();
cy.wait(1000);
// form should not contain any "field remove icons"
verifyFormDrawerHideObjectCount(0);
// menu bar should contain 2 .pointer.item (LastUpdate, County->City)
verifyFormMenuDrawerCardCount(2);
// click: Add all
cy.get(".nc-form-left-drawer")
.find(".nc-form-add-all")
.should("be.visible")
.click();
cy.get(".nc-form-left-drawer")
.find(".nc-form-remove-all")
.should("be.visible");
// form should contain "field remove icons"
verifyFormDrawerHideObjectCount(2);
// menu bar should not contain .pointer.item (column name/ field name add options)
verifyFormMenuDrawerCardCount(0);
});
it(`Validate ${viewType}: Submit default, empty show this message textbox`, () => {
// fill up mandatory fields
cy.get(".nc-form-input-Country").type("_abc");
cy.get(".nc-form-input-LastUpdate").click();
cy.get(".ant-picker-now-btn:visible").contains("Now").click();
cy.get(".ant-btn-primary:visible").contains("Ok").click();
// default message, no update
// submit button & validate
cy.get(".nc-form").find("button").contains("Submit").click();
cy.get(
".ant-alert-message :contains('Successfully submitted form data')"
).should("exist");
// end of test removes newly added rows from table. that step validates if row was successfully added.
});
it(`Validate ${viewType}: Submit default, with valid Show message entry`, () => {
// clicking again on view name shows blank still. work around- toggling between two views
cy.get(`.nc-view-item.nc-${viewType}-view-item`)
.contains("Form-1")
.click();
// fill up mandatory fields
cy.get(".nc-form-input-Country").should("exist").type("_abc");
cy.get(".nc-form-input-LastUpdate").click();
cy.get(".ant-picker-now-btn:visible").contains("Now").click();
cy.get(".ant-btn-primary:visible").contains("Ok").click();
// add message
cy.get("textarea.nc-form-after-submit-msg").type("Congratulations!");
// submit button & validate
cy.get(".nc-form").find("button").contains("Submit").click();
cy.get(".ant-alert-message :contains('Congratulations!')").should(
"exist"
);
// end of test removes newly added rows from table. that step validates if row was successfully added.
});
it(`Validate ${viewType}: Submit default, Enable checkbox "Submit another form`, () => {
// clicking again on view name shows blank still. work around- toggling between two views
cy.get(`.nc-view-item.nc-${viewType}-view-item`)
.contains("Form-1")
.click();
// fill up mandatory fields
cy.get(".nc-form-input-Country").type("_abc");
cy.get(".nc-form-input-LastUpdate").click();
cy.get(".ant-picker-now-btn:visible").contains("Now").click();
cy.get(".ant-btn-primary:visible").contains("Ok").click();
// enable "Submit another form" check box
cy.get("button.nc-form-checkbox-submit-another-form").click();
// submit button & validate
cy.get(".nc-form").find("button").contains("Submit").click();
cy.get(".ant-alert-message :contains('Congratulations!')").should(
"exist"
);
cy.get("button")
.contains("Submit Another Form")
.should("exist")
.click();
// New form appeared? Header & description should exist
validateFormHeader();
// end of test removes newly added rows from table. that step validates if row was successfully added.
});
it(`Validate ${viewType}: Submit default, Enable checkbox "blank form after 5 seconds"`, () => {
cy.get(".nc-form-input-Country").type("_abc");
cy.get(".nc-form-input-LastUpdate").click();
cy.get(".ant-picker-now-btn:visible").contains("Now").click();
cy.get(".ant-btn-primary:visible").contains("Ok").click();
// enable "New form after 5 seconds" button
cy.get("button.nc-form-checkbox-submit-another-form").click();
cy.get("button.nc-form-checkbox-show-blank-form").click();
// submit button & validate
cy.get(".nc-form").find("button").contains("Submit").click();
cy.get(".ant-alert-message :contains('Congratulations!')")
.should("exist")
.then(() => {
// validate if form has appeared again
validateFormHeader();
});
// end of test removes newly added rows from table. that step validates if row was successfully added.
});
it(`Validate ${viewType}: Email me verification, without SMTP configuration`, () => {
// open formview & enable "email me" option
cy.get(`.nc-view-item.nc-${viewType}-view-item`)
.contains("Form-1")
.click();
// validate if form has appeared again
cy.wait(1000);
validateFormHeader();
cy.get(".nc-form-remove-all").click();
cy.get(".nc-form-checkbox-send-email").click();
// validate if toaster pops up requesting to activate SMTP
cy.toastWait(
"Please activate SMTP plugin in App store for enabling email notification"
);
});
it(`Validate ${viewType}: Email me verification, with SMTP configuration`, () => {
// activate SMTP, dummy profile
settingsPage.openMenu(settingsPage.APPSTORE);
mainPage.configureSMTP("admin@ex.com", "smtp.ex.com", "8080", "TLS");
cy.get(`.nc-view-item.nc-${viewType}-view-item`)
.contains("Form-1")
.click();
// validate if form has appeared again
validateFormHeader();
cy.get(".nc-form-checkbox-send-email").click();
settingsPage.openMenu(settingsPage.APPSTORE);
mainPage.resetSMTP();
});
it(`Validate ${viewType}: Add/ remove field verification"`, () => {
cy.get(`.nc-view-item.nc-${viewType}-view-item`)
.contains("Form-1")
.click();
cy.get(".nc-form-add-all").click();
cy.wait(300);
// validate if form has appeared again
validateFormHeader();
cy.get(".nc-form-input-LastUpdate").should("exist");
// remove "LastUpdate field"
cy.get(".nc-form").find(".nc-field-remove-icon").eq(1).click();
cy.get(".nc-form-input-LastUpdate").should("not.exist");
cy.get(".nc-form-left-drawer")
.find(".ant-card")
.contains("LastUpdate")
.should("exist")
.click();
cy.get(".nc-form-input-LastUpdate").should("exist");
cy.wait(300);
});
it(`Validate ${viewType}: URL verification`, () => {
cy.get(`.nc-view-item.nc-${viewType}-view-item`)
.contains("Form-1")
.click();
// validate if form has appeared again
validateFormHeader();
});
it(`Delete ${viewType} view`, () => {
// number of view entries should be 2 before we delete
cy.get(".nc-view-item").its("length").should("eq", 2);
// click on delete icon (becomes visible on hovering mouse)
cy.get(".nc-view-delete-icon").click({ force: true });
cy.getActiveModal(".nc-modal-view-delete")
.find(".ant-btn-dangerous")
.click();
cy.toastWait("View deleted successfully");
// confirm if the number of veiw entries is reduced by 1
cy.get(".nc-view-item").its("length").should("eq", 1);
// clean up newly added rows into Country table operations
// this auto verifies successfull addition of rows to table as well
mainPage.getPagination(5).click();
cy.get(".nc-grid-row").should("have.length", 13);
cy.get(".ant-checkbox").should("exist").eq(10).click({ force: true });
cy.get(".ant-checkbox").should("exist").eq(11).click({ force: true });
cy.get(".ant-checkbox").should("exist").eq(12).click({ force: true });
cy.get(".ant-checkbox").should("exist").eq(13).click({ force: true });
mainPage.getCell("Country", 10).rightclick({ force: true });
cy.getActiveMenu(".nc-dropdown-grid-context-menu")
.contains("Delete Selected Rows")
.click({ force: true });
});
};
// below scenario's will be invoked twice, once for rest & then for graphql
viewTest("form");
});
};

107
scripts/cypress/integration/common/4d_table_view_grid_locked.js

@ -1,107 +0,0 @@
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
import { mainPage } from "../../support/page_objects/mainPage";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - Lock view`, () => {
// Run once before test- create project (rest/graphql)
//
before(() => {
cy.restoreLocalStorage();
cy.openTableTab("Country", 25);
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
cy.restoreLocalStorage();
cy.closeTableTab("Country");
cy.saveLocalStorage();
});
const lockViewTest = (enabled) => {
it(`Grid: lock view set to ${enabled}: validation`, () => {
let vString = enabled ? "not." : "";
let menuOption = enabled ? "Locked View" : "Collaborative View";
// on menu, collaboration view appears first (at index 0)
// followed by Locked view (at index 1)
cy.get(".nc-actions-menu-btn").click();
cy.getActiveMenu(".nc-dropdown-actions-menu")
.find(".ant-dropdown-menu-submenu")
.eq(0)
.click();
cy.wait(1000);
cy.get(".nc-locked-menu-item")
.contains(menuOption)
.should("exist")
.click();
// cy.get(".nc-sidebar-lock-menu")
// .click();
// cy.getActiveMenu()
// .find('.nc-menu-item:visible')
// .eq(menuOption)
// .click();
if (enabled) {
cy.toastWait("Successfully Switched to locked view");
cy.get(".nc-icon-locked").should("exist");
} else {
cy.toastWait("Successfully Switched to collaborative view");
cy.get(".nc-icon-collaborative").should("exist");
}
// expected toolbar for Lock view: Only lock-view menu, reload, toggle-nav-drawer to be enabled
//
// cy.get(".nc-sidebar-lock-menu:enabled")
// .should("exist");
cy.get(".nc-toolbar-reload-btn").should("exist");
cy.get(".nc-add-new-row-btn > .cursor-pointer").should(
`${vString}exist`
);
cy.get(".nc-fields-menu-btn:enabled").should(`${vString}exist`);
cy.get(".nc-sort-menu-btn:enabled").should(`${vString}exist`);
cy.get(".nc-filter-menu-btn:enabled").should(`${vString}exist`);
// dblClick on a cell & see if we can edit
mainPage.getCell("Country", 1).dblclick();
mainPage.getCell("Country", 1).find("input").should(`${vString}exist`);
// the expand button should be always enabled
cy.get(".nc-row-expand").should("exist");
// check if add/ expand options available for 'has many' column type
// GUI-v2: TBD
mainPage
.getCell("City List", 1)
.click()
.find(".nc-action-icon.nc-plus")
.should(`${vString}exist`);
mainPage
.getCell("City List", 1)
.click()
.find(".nc-action-icon.nc-arrow-expand")
.should(`${vString}exist`);
// update row option (right click) - should not be available for Lock view
mainPage.getCell("City List", 1).rightclick();
cy.get(".ant-dropdown-content").should(`${vString}be.visible`);
});
};
// Locked view
lockViewTest(true);
// collaboration view
lockViewTest(false);
});
};

226
scripts/cypress/integration/common/4e_form_view_share.js

@ -1,226 +0,0 @@
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
import { mainPage } from "../../support/page_objects/mainPage";
import { loginPage } from "../../support/page_objects/navigation";
let storedURL = "";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} api - FORM view (Share)`, () => {
const name = "Test" + Date.now();
// Run once before test- create project (rest/graphql)
//
before(() => {
// loginPage.loginAndOpenProject(apiType, dbType);
cy.restoreLocalStorage();
cy.openTableTab("City", 25);
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
cy.restoreLocalStorage();
cy.closeTableTab("City");
cy.saveLocalStorage();
});
// Common routine to create/edit/delete GRID & GALLERY view
// Input: viewType - 'grid'/'gallery'
//
const viewTest = (viewType) => {
it(`Create ${viewType} view`, () => {
0;
// click on create grid view button
cy.get(`.nc-create-${viewType}-view`).click();
// Pop up window, click Submit (accepting default name for view)
cy.getActiveModal(".nc-modal-view-create")
.find("button:contains(Submit)")
.click();
cy.toastWait("View created successfully");
// validate if view was creted && contains default name 'Country1'
cy.get(`.nc-view-item.nc-${viewType}-view-item`)
.contains("Form-1")
.should("exist");
// Prepare form
// add header, description
// add post submission message
// swap position for City, LastUpdate fields
// remove City=>Address field
// enable "Submit another form" check box
cy.get("button.nc-form-checkbox-show-blank-form").click();
// [kludge] CI-CD: title is being rendered initially in disabled state
cy.wait(2000);
// Update header & add some description, verify
cy.get(".nc-form")
.find('[placeholder="Form Title"]')
.clear()
.type("A B C D");
cy.get(".nc-form")
.find('[placeholder="Add form description"]')
.type("Some description about form comes here");
// add message
cy.get("textarea.nc-form-after-submit-msg").type("Congratulations!");
// move Country field down (drag, drop)
cy.get(".nc-form-drag-LastUpdate").drag(".nc-form-drag-City");
cy.get('[title="Address List"]').drag(".nc-drag-n-drop-to-hide");
// store base URL- to re-visit and delete form view later
cy.url().then((url) => {
storedURL = url;
});
});
it(`Share form view`, () => {
cy.get(`.nc-view-item.nc-${viewType}-view-item`)
.contains("Form-1")
.click();
cy.wait(2000);
mainPage.shareView().click();
// copy link text, visit URL
cy.getActiveModal(".nc-modal-share-view")
.should("exist")
.find(".share-link-box")
.contains("/nc/form/", { timeout: 10000 })
.should("exist")
.then(($obj) => {
// http://localhost:8080/api/v1/db/public/shared-view/761f0200-e72c-487a-85bf-615d0d277054/rows?offset=0&filterArrJson=[]&sortArrJson=[]
cy.intercept("/api/v1/db/public/shared-view/**").as(
"waitForPageLoad"
);
let linkText = $obj.text().trim();
cy.log(linkText);
cy.signOut();
cy.visit(linkText, {
baseUrl: null,
});
cy.wait(["@waitForPageLoad"], { times: 2 });
// wait for share view page to load!
cy.get(".nc-form").should("exist");
// New form appeared? Header & description should exist
cy.get(".nc-form-view", { timeout: 10000 })
.find("h1")
.contains("A B C D")
.should("exist");
cy.get(".nc-form-view", { timeout: 10000 })
.find("h2")
.contains("Some description about form comes here")
.should("exist");
// all fields, barring removed field should exist
cy.get('[title="City"]').should("exist");
cy.get('[title="LastUpdate"]').should("exist");
cy.get('[title="Country"]').should("exist");
cy.get('[title="Address List"]').should("not.exist");
// order of LastUpdate & City field is retained
cy.get(".nc-form-column-label")
.eq(0)
.contains("LastUpdate")
.should("exist");
cy.get(".nc-form-column-label")
.eq(1)
.contains("City")
.should("exist");
// submit form, to read message
cy.get(".nc-form-input-City").type("_abc");
cy.get(".nc-form-input-LastUpdate").click();
cy.get(".ant-picker-now-btn:visible").contains("Now").click();
cy.get(".ant-btn-primary:visible").contains("Ok").click();
// cy.get('.nc-form-field-Country')
// .trigger('mouseover')
// .click()
// .find('.nc-action-icon')
// .click();
// // cy.get("button").contains("Link to 'Country'").click();
// cy.getActiveModal()
// .find(".ant-card")
// .contains("Afghanistan")
// .click();
//
// // submit button & validate
// cy.get(".nc-form")
// .find("button")
// .contains("Submit")
// .click();
//
// cy.get(".ant-alert-message")
// .contains("Congratulations")
// .should("exist")
// .then(() => {
// cy.get(".nc-form").should("exist");
//
// // validate if form has appeared again
// cy.get(".nc-share-form-title")
// .contains("A B C D")
// .should("exist");
// cy.get(".nc-share-form-desc")
// .contains("Some description about form comes here")
// .should("exist");
// });
});
});
it(`Delete ${viewType} view`, () => {
// go back to base page
loginPage.loginAndOpenProject(apiType, dbType);
cy.openTableTab("City", 25);
// number of view entries should be 2 before we delete
cy.get(".nc-view-item").its("length").should("eq", 2);
// click on delete icon (becomes visible on hovering mouse)
cy.get(".nc-view-delete-icon").click({ force: true });
cy.wait(1000);
cy.getActiveModal(".nc-modal-view-delete")
.find(".ant-btn-dangerous")
.should("exist")
.click();
cy.toastWait("View deleted successfully");
// confirm if the number of veiw entries is reduced by 1
cy.get(".nc-view-item").its("length").should("eq", 1);
// // clean up newly added rows into Country table operations
// // this auto verifies successfull addition of rows to table as well
// mainPage.getPagination(25).click();
//
// cy.get(".nc-grid-row").should("have.length", 1);
// cy.get(".ant-checkbox").should('exist').eq(1).click({ force: true });
// mainPage.getCell("Country", 1).rightclick({ force: true });
// cy.getActiveMenu()
// .contains("Delete Selected Rows")
// .click({ force: true });
});
};
// below scenario's will be invoked twice, once for rest & then for graphql
viewTest("form");
});
};

477
scripts/cypress/integration/common/4f_grid_view_share.js

@ -1,477 +0,0 @@
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
import { mainPage } from "../../support/page_objects/mainPage";
import { loginPage } from "../../support/page_objects/navigation";
let storedURL = "";
// 0: all enabled
// 1: field hide
// 2: field sort
// 3: field filter
// 4: default (address table): for view operation validation
// 5: default (country table): for update row/column validation
let viewURL = {};
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
const generateViewLink = (viewName) => {
mainPage.shareView().click();
cy.wait(1000);
// wait, as URL initially will be /undefined
cy.getActiveModal(".nc-modal-share-view")
.find(".share-link-box")
.contains("/nc/view/", { timeout: 10000 })
.should("exist");
// copy link text, visit URL
cy.getActiveModal(".nc-modal-share-view")
.find(".share-link-box")
.contains("/nc/view/", { timeout: 10000 })
.then(($obj) => {
// cy.get("body").type("{esc}");
cy.closeActiveModal(".nc-modal-share-view");
// viewURL.push($obj.text())
viewURL[viewName] = $obj.text().trim();
});
cy.getActiveModal(".nc-modal-share-view").should("not.be.visible");
};
let clear;
describe(`${apiType.toUpperCase()} api - GRID view (Share)`, () => {
// Run once before test- create project (rest/graphql)
//
before(() => {
cy.restoreLocalStorage();
cy.openTableTab("Address", 25);
clear = Cypress.LocalStorage.clear;
Cypress.LocalStorage.clear = () => {};
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
// close table
// mainPage.deleteCreatedViews()
cy.restoreLocalStorage();
cy.closeTableTab("Address");
cy.saveLocalStorage();
Cypress.LocalStorage.clear = clear;
});
// Common routine to create/edit/delete GRID & GALLERY view
// Input: viewType - 'grid'/'gallery'
//
const viewTest = (viewType) => {
it(`Create ${viewType.toUpperCase()} view`, () => {
// create a normal public view
cy.get(`.nc-create-${viewType}-view`).click();
cy.getActiveModal(".nc-modal-view-create")
.find("button:contains(Submit)")
.click();
cy.toastWait("View created successfully");
// store base URL- to re-visit and delete form view later
cy.url().then((url) => {
storedURL = url;
});
});
it(`Share ${viewType.toUpperCase()} hide, sort, filter & verify`, () => {
cy.intercept("/api/v1/db/meta/audits/comments/**").as(
"waitForPageLoad"
);
cy.get(`.nc-view-item.nc-${viewType}-view-item`)
.contains("Grid-1")
.click();
mainPage.hideField("Address2");
mainPage.sortField("District", "Z → A");
mainPage.filterField("Address", "is like", "Ab");
generateViewLink("combined");
cy.log(viewURL["combined"]);
cy.wait(["@waitForPageLoad"]);
// kludge: additional wait to ensure page load is completed
cy.wait(2000);
});
it(`Share GRID view : ensure we have only one link even if shared multiple times`, () => {
// generate view link multiple times
generateViewLink("combined");
generateViewLink("combined");
// verify if only one link exists in table
mainPage.shareViewList().click();
cy.get('th:contains("View Link")').should("exist");
cy.get('th:contains("View Link")')
.parent()
.parent()
.next()
.find("tr")
.its("length")
.should("eq", 1)
.then(() => {
cy.get("button.ant-modal-close:visible").click();
});
cy.signOut();
});
it(`Share ${viewType.toUpperCase()} view : Visit URL, Verify title`, () => {
// visit public view
cy.visit(viewURL["combined"], {
baseUrl: null,
});
cy.wait(5000);
// wait for page rendering to complete
cy.get(".nc-grid-row").should("have.length", 18);
// verify title
cy.get(".nc-shared-view-title").contains("Grid-1").should("exist");
});
it(`Share ${viewType.toUpperCase()} view : verify fields hidden/open`, () => {
// verify column headers
cy.get('[data-title="Address"]').should("exist");
cy.get('[data-title="Address2"]').should("not.exist");
cy.get('[data-title="District"]').should("exist");
});
it(`Share ${viewType.toUpperCase()} view : verify fields sort/ filter`, () => {
// country column content verification before sort
mainPage
.getCell("District", 1)
.contains("West Bengali")
.should("exist");
mainPage.getCell("District", 2).contains("Tutuila").should("exist");
mainPage.getCell("District", 3).contains("Tamil Nadu").should("exist");
});
it(`Share ${viewType.toUpperCase()} view : verify download CSV`, () => {
mainPage.hideField("LastUpdate");
const verifyCsv = (retrievedRecords) => {
// expected output, statically configured
let storedRecords = [
`Address,District,PostalCode,Phone,Location,Customer List,Staff List,City,Staff List`,
`1013 Tabuk Boulevard,West Bengali,96203,158399646978,[object Object],2,,Kanchrapara,`,
`1892 Nabereznyje Telny Lane,Tutuila,28396,478229987054,[object Object],2,,Tafuna,`,
`1993 Tabuk Lane,Tamil Nadu,64221,648482415405,[object Object],2,,Tambaram,`,
`1661 Abha Drive,Tamil Nadu,14400,270456873752,[object Object],1,,Pudukkottai,`,
];
for (let i = 0; i < storedRecords.length; i++) {
let strCol = storedRecords[i].split(",");
let retCol = retrievedRecords[i].split(",");
for (let j = 0; j < 4; j++) {
expect(strCol[j]).to.be.equal(retCol[j]);
}
}
};
// download & verify
mainPage.downloadAndVerifyCsvFromSharedView(
`Address_exported_1.csv`,
verifyCsv
);
mainPage.unhideField("LastUpdate");
});
it(`Share ${viewType.toUpperCase()} view : Disable sort`, () => {
// remove sort and validate
mainPage.clearSort();
mainPage
.getCell("District", 1)
.contains("West Bengali")
.should("exist");
});
it(`Share ${viewType.toUpperCase()} view : Enable sort`, () => {
// Sort menu operations (Country Column, Z → A)
mainPage.sortField("District", "Z → A");
mainPage
.getCell("District", 1)
.contains("West Bengali")
.should("exist");
});
it(`Share ${viewType.toUpperCase()} view : Create Filter`, () => {
// add filter & validate
mainPage.filterField("District", "is like", "Tamil");
// wait for page rendering to complete
cy.get(".nc-grid-row").should("have.length", 2);
mainPage.getCell("District", 1).contains("Tamil").should("exist");
});
it(`Share ${viewType.toUpperCase()} view : verify download CSV after local filter`, () => {
mainPage.hideField("LastUpdate");
const verifyCsv = (retrievedRecords) => {
// expected output, statically configured
let storedRecords = [
`Address,District,PostalCode,Phone,Location,Customer List,Staff List,City,Staff List`,
`1993 Tabuk Lane,Tamil Nadu,64221,648482415405,[object Object],2,,Tambaram,`,
`1661 Abha Drive,Tamil Nadu,14400,270456873752,[object Object],1,,Pudukkottai,`,
];
// for (let i = 0; i < storedRecords.length; i++) {
// expect(retrievedRecords[i]).to.be.equal(storedRecords[i])
// }
// ncv2@fixme
// for (let i = 0; i < storedRecords.length; i++) {
// let strCol = storedRecords[i].split(",");
// let retCol = retrievedRecords[i].split(",");
// for (let j = 0; j < 4; j++) {
// expect(strCol[j]).to.be.equal(retCol[j]);
// }
// }
};
mainPage.downloadAndVerifyCsvFromSharedView(
`Address_exported_1.csv`,
verifyCsv
);
mainPage.unhideField("LastUpdate");
});
it(`Share ${viewType.toUpperCase()} view : Delete Filter`, () => {
// Remove sort and Validate
mainPage.filterReset();
mainPage
.getCell("District", 1)
.contains("West Bengali")
.should("exist");
});
it(`Share GRID view : Virtual column validation > has many`, () => {
// verify column headers
cy.get('[data-title="Customer List"]').should("exist");
cy.get('[data-title="Staff List"]').should("exist");
cy.get('[data-title="City"]').should("exist");
cy.get('[data-title="Staff List"]').should("exist");
// has many field validation
mainPage
.getCell("Customer List", 3)
.click()
.find(".nc-icon.nc-unlink-icon")
.should("not.exist");
mainPage
.getCell("Customer List", 3)
.click()
.find(".nc-icon.nc-action-icon.nc-plus")
.should("not.exist");
// mainPage
// .getCell("Customer List", 3)
// .click()
// .find(".nc-icon.nc-action-icon.nc-arrow-expand")
// .click({ force: true });
//
// // reload button
// cy.getActiveModal().find(".nc-icon").should("exist");
// cy.getActiveModal()
// .find("button")
// .contains("Link to")
// .should("not.exist");
// cy.getActiveModal()
// .find(".ant-card")
// .contains("2")
// .should("exist");
// cy.getActiveModal()
// .find(".ant-card")
// .find("button")
// .should("not.exist");
// cy.get('button.ant-modal-close').click();
});
it(`Share GRID view : Virtual column validation > belongs to`, () => {
// belongs to field validation
mainPage
.getCell("City", 1)
.click()
.find(".nc-icon.nc-unlink-icon")
.should("not.exist");
mainPage
.getCell("City", 1)
.click()
.find(".nc-icon.nc-action-icon.nc-arrow-expand")
.should("not.exist");
mainPage
.getCell("City", 1)
.find(".chips")
.contains("Kanchrapara")
.should("exist");
});
it(`Share GRID view : Virtual column validation > many to many`, () => {
// many-to-many field validation
mainPage
.getCell("Staff List", 1)
.click()
.find(".nc-icon.nc-unlink-icon")
.should("not.exist");
mainPage
.getCell("Staff List", 1)
.click()
.find(".nc-icon.nc-action-icon.nc-plus")
.should("not.exist");
mainPage
.getCell("Staff List", 1)
.click()
.find(".nc-icon.nc-action-icon.nc-arrow-expand")
.click({ force: true });
// // reload button
// Fix me : ncv2@fixme
// cy.getActiveModal().find(".nc-icon").should("exist");
// cy.getActiveModal()
// .find("button")
// .contains("Link to")
// .should("not.exist");
// cy.get('button.ant-modal-close:visible').last().click();
});
it(`Delete ${viewType.toUpperCase()} view`, () => {
// go back to base page
loginPage.loginAndOpenProject(apiType, dbType);
cy.openTableTab("Address", 25);
// number of view entries should be 2 before we delete
cy.get(".nc-view-item").its("length").should("eq", 2);
cy.get(".nc-view-delete-icon").eq(0).click({ force: true });
cy.getActiveModal(".nc-modal-view-delete")
.find(".ant-btn-dangerous")
.click();
cy.toastWait("View deleted successfully");
// confirm if the number of veiw entries is reduced by 1
cy.get(".nc-view-item").its("length").should("eq", 1);
});
};
// below scenario's will be invoked twice, once for rest & then for graphql
viewTest("grid");
});
describe(`${apiType.toUpperCase()} api - Grid view/ row-column update verification`, () => {
before(() => {
cy.restoreLocalStorage();
// Address table has belongs to, has many & many-to-many
cy.openTableTab("Country", 25);
// store base URL- to re-visit and delete form view later
cy.url().then((url) => {
storedURL = url;
generateViewLink("rowColUpdate");
});
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
cy.restoreLocalStorage();
cy.closeTableTab("Country");
cy.saveLocalStorage();
});
it(`Generate default Shared GRID view URL`, () => {
// add row
mainPage.addNewRowExpand("Country");
cy.get(".nc-expand-col-Country")
.find(".nc-cell > input")
.should("exist")
.first()
.clear({ force: true })
.type("a");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Save row")
.should("exist")
.click();
cy.toastWait("updated successfully");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Cancel")
.should("exist")
.click();
// add column
mainPage.addColumn("dummy", "Country");
cy.signOut();
// visit public view
cy.log(viewURL["rowColUpdate"]);
cy.visit(viewURL["rowColUpdate"], {
baseUrl: null,
});
cy.wait(5000);
// wait for public view page to load!
// wait for page rendering to complete
cy.get(".nc-grid-row").should("have.length", 25);
});
it(`Share GRID view : new row visible`, () => {
// verify row
// cy.get(`.v-pagination > li:contains('5') button`).click();
cy.get(
`.nc-pagination > .ant-pagination-item.ant-pagination-item-5`
).click();
// wait for page rendering to complete
cy.get(".nc-grid-row").should("have.length", 10);
mainPage.getCell("Country", 10).contains("a").should("exist");
});
it(`Share GRID view : new column visible`, () => {
// verify column headers
cy.get('[data-title="dummy"]').should("exist");
});
it(`Clean up`, () => {
loginPage.loginAndOpenProject(apiType, dbType);
cy.openTableTab("Country", 25);
// delete row
mainPage.getPagination(5).click();
// kludge: flicker on load
cy.wait(3000);
// wait for page rendering to complete
cy.get(".nc-grid-row").should("have.length", 10);
mainPage.getCell("Country", 10).rightclick();
cy.getActiveMenu(".nc-dropdown-grid-context-menu")
.find('.ant-dropdown-menu-item:contains("Delete Row")')
.first()
.click();
// delete column
mainPage.deleteColumn("dummy");
mainPage.deleteCreatedViews();
});
});
};

504
scripts/cypress/integration/common/4f_pg_grid_view_share.js

@ -1,504 +0,0 @@
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
import { mainPage } from "../../support/page_objects/mainPage";
import { loginPage } from "../../support/page_objects/navigation";
let storedURL = "";
// 0: all enabled
// 1: field hide
// 2: field sort
// 3: field filter
// 4: default (address table): for view operation validation
// 5: default (country table): for update row/column validation
let viewURL = {};
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
function verifyLtarCell(columnName, index, cellValue, options) {
if (cellValue !== "")
cy.get(`:nth-child(${index}) > [data-title="${columnName}"]`)
.find(".chip")
.eq(0)
.contains(cellValue)
.should("exist");
mainPage
.getCell(columnName, index)
.click()
.find(".nc-icon.nc-unlink-icon")
.should(`${options.unlink ? "exist" : "not.exist"}`);
mainPage
.getCell(columnName, index)
.click()
.find(".nc-icon.nc-plus")
.should(`${options.plus ? "exist" : "not.exist"}`);
mainPage
.getCell(columnName, index)
.click()
.find(".nc-icon.nc-arrow-expand")
.should(`${options.expand ? "exist" : "not.exist"}`);
}
function actionLtarCell(columnName, index, button) {
mainPage
.getCell(columnName, index)
.click()
.find(`.nc-icon${button}`)
.click({ force: true });
}
function verifyChildListCard(cardValue, options) {
// reload button
cy.getActiveModal(".nc-modal-child-list")
.find(`[data-cy="nc-child-list-reload"]`)
.should(`${options.reload ? "exist" : "not.exist"}`);
// link-to button
cy.getActiveModal(".nc-modal-child-list")
.find(`[data-cy="nc-child-list-button-link-to"]`)
.should(`${options.linkTo ? "exist" : "not.exist"}`);
// child card
// - contents : should exist
// - link-to button : should not exist
// - delete button : should not exist
if (cardValue !== "") {
cy.getActiveModal(".nc-modal-child-list")
.find(".ant-card")
.contains(cardValue)
.should("exist");
cy.getActiveModal(".nc-modal-child-list")
.find(".ant-card")
.find(`[data-cy="nc-child-list-icon-unlink"]`)
.should(`${options.unlink ? "exist" : "not.exist"}`);
cy.getActiveModal(".nc-modal-child-list")
.find(".ant-card")
.find(`[data-cy="nc-child-list-icon-delete"]`)
.should(`${options.delete ? "exist" : "not.exist"}`);
}
}
const generateViewLink = (viewName) => {
mainPage.shareView().click();
cy.wait(1000);
// wait, as URL initially will be /undefined
cy.getActiveModal(".nc-modal-share-view")
.find(".share-link-box")
.contains("/nc/view/", { timeout: 10000 })
.should("exist");
// copy link text, visit URL
cy.getActiveModal(".nc-modal-share-view")
.find(".share-link-box")
.contains("/nc/view/", { timeout: 10000 })
.then(($obj) => {
// cy.get("body").type("{esc}");
cy.closeActiveModal(".nc-modal-share-view");
viewURL[viewName] = $obj.text().trim();
});
};
let clear;
describe(`${apiType.toUpperCase()} api - GRID view (Share)`, () => {
// Run once before test- create project (rest/graphql)
//
before(() => {
cy.restoreLocalStorage();
cy.openTableTab("Address", 25);
clear = Cypress.LocalStorage.clear;
Cypress.LocalStorage.clear = () => {};
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
// close table
// mainPage.deleteCreatedViews()
cy.restoreLocalStorage();
cy.closeTableTab("Address");
cy.saveLocalStorage();
Cypress.LocalStorage.clear = clear;
});
// Common routine to create/edit/delete GRID & GALLERY view
// Input: viewType - 'grid'/'gallery'
//
const viewTest = (viewType) => {
it(`Create ${viewType.toUpperCase()} view`, () => {
// create a normal public view
cy.get(`.nc-create-${viewType}-view`).click();
cy.getActiveModal(".nc-modal-view-create")
.find("button:contains(Submit)")
.click();
cy.toastWait("View created successfully");
// store base URL- to re-visit and delete form view later
cy.url().then((url) => {
storedURL = url;
});
});
it(`Share ${viewType.toUpperCase()} hide, sort, filter & verify`, () => {
cy.intercept("/api/v1/db/meta/audits/comments/**").as(
"waitForPageLoad"
);
cy.get(`.nc-view-item.nc-${viewType}-view-item`)
.contains("Grid-1")
.click();
mainPage.hideField("Address2");
mainPage.sortField("Address", "Z → A");
mainPage.filterField("Address", "is like", "Ab");
generateViewLink("combined");
cy.log(viewURL["combined"]);
cy.wait(["@waitForPageLoad"]);
// kludge: additional wait to ensure page load is completed
cy.wait(2000);
});
it(`Share GRID view : ensure we have only one link even if shared multiple times`, () => {
// generate view link multiple times
generateViewLink("combined");
generateViewLink("combined");
// verify if only one link exists in table
mainPage.shareViewList().click();
cy.get('th:contains("View Link")').should("exist");
cy.get('th:contains("View Link")')
.parent()
.parent()
.next()
.find("tr")
.its("length")
.should("eq", 1)
.then(() => {
cy.get("button.ant-modal-close:visible").click();
});
cy.signOut();
});
it(`Share ${viewType.toUpperCase()} view : Visit URL, Verify title`, () => {
// visit public view
cy.visit(viewURL["combined"], {
baseUrl: null,
});
cy.wait(5000);
// wait for page rendering to complete
cy.get(".nc-grid-row").should("have.length", 18);
// verify title
cy.get(".nc-shared-view-title").contains("Grid-1").should("exist");
});
it(`Share ${viewType.toUpperCase()} view : verify fields hidden/open`, () => {
// verify column headers
cy.get('[data-title="Address"]').should("exist");
cy.get('[data-title="Address2"]').should("not.exist");
cy.get('[data-title="District"]').should("exist");
});
it(`Share ${viewType.toUpperCase()} view : verify fields sort/ filter`, () => {
// country column content verification before sort
mainPage
.getCell("Address", 1)
.contains("669 Firozabad Loop")
.should("exist");
mainPage
.getCell("Address", 2)
.contains("48 Maracabo Place")
.should("exist");
mainPage
.getCell("Address", 3)
.contains("44 Najafabad Way")
.should("exist");
});
it(`Share ${viewType.toUpperCase()} view : verify download CSV`, () => {
mainPage.hideField("LastUpdate");
const verifyCsv = (retrievedRecords) => {
// expected output, statically configured
let storedRecords = [
`Address,District,PostalCode,Phone`,
`669 Firozabad Loop,,92265,,[object Object],2,,Kanchrapara,`,
`48 Maracabo Place,,1570,,[object Object],2,,Tafuna,`,
`44 Najafabad Way,,61391,,[object Object],2,,Tambaram,`,
`381 Kabul Way,,87272,,[object Object],1,,Pudukkottai,`,
];
for (let i = 0; i < storedRecords.length; i++) {
let strCol = storedRecords[i].split(",");
let retCol = retrievedRecords[i].split(",");
expect(strCol[0]).to.be.equal(retCol[0]);
expect(strCol[2]).to.be.equal(retCol[2]);
// expect(retrievedRecords[i]).to.be.equal(storedRecords[i])
}
};
// download & verify
mainPage.downloadAndVerifyCsvFromSharedView(
`Address_exported_1.csv`,
verifyCsv
);
mainPage.unhideField("LastUpdate");
});
it(`Share ${viewType.toUpperCase()} view : Disable sort`, () => {
// remove sort and validate
mainPage.clearSort();
mainPage
.getCell("Address", 1)
.contains("669 Firozabad Loop")
.should("exist");
});
it(`Share ${viewType.toUpperCase()} view : Enable sort`, () => {
// Sort menu operations (Country Column, Z->A)
mainPage.sortField("Address", "A → Z");
mainPage
.getCell("Address", 1)
.contains("1013 Tabuk Boulevard")
.should("exist");
});
it(`Share ${viewType.toUpperCase()} view : Create Filter`, () => {
// add filter & validate
mainPage.filterField("Address", "is like", "drive");
// wait for page rendering to complete
cy.get(".nc-grid-row").should("have.length", 3);
mainPage
.getCell("Address", 1)
.contains("1294 Firozabad Drive")
.should("exist");
});
it(`Share ${viewType.toUpperCase()} view : verify download CSV after local filter`, () => {
mainPage.hideField("LastUpdate");
const verifyCsv = (retrievedRecords) => {
// expected output, statically configured
let storedRecords = [
`Address,District,PostalCode,Phone,Location,Customer List,Staff List,City,Staff List`,
`1294 Firozabad Drive,,70618,,2,,Pingxiang,,`,
`1661 Abha Drive,,14400,,1,,Pudukkottai,,`,
];
// for (let i = 0; i < storedRecords.length; i++) {
// expect(retrievedRecords[i]).to.be.equal(storedRecords[i])
// }
for (let i = 0; i < storedRecords.length; i++) {
let strCol = storedRecords[i].split(",");
let retCol = retrievedRecords[i].split(",");
expect(strCol[0]).to.be.equal(retCol[0]);
expect(strCol[2]).to.be.equal(retCol[2]);
}
};
mainPage.downloadAndVerifyCsvFromSharedView(
`Address_exported_1.csv`,
verifyCsv
);
mainPage.unhideField("LastUpdate");
});
it(`Share ${viewType.toUpperCase()} view : Delete Filter`, () => {
// Remove sort and Validate
mainPage.filterReset();
mainPage.clearSort();
mainPage
.getCell("Address", 1)
.contains("669 Firozabad Loop")
.should("exist");
});
it(`Share GRID view : Virtual column validation > has many`, () => {
// verify column headers
cy.get('[data-title="Customer List"]').should("exist");
cy.get('[data-title="Staff List"]').should("exist");
cy.get('[data-title="City"]').should("exist");
cy.get('[data-title="Staff List1"]').should("exist");
// has many field validation
verifyLtarCell("Customer List", 1, "1", {
unlink: false,
plus: false,
expand: true,
});
actionLtarCell("Customer List", 1, ".nc-arrow-expand");
verifyChildListCard("1", {
reload: true,
linkTo: false,
unlink: false,
delete: false,
});
cy.closeActiveModal(".nc-modal-child-list");
});
it(`Share GRID view : Virtual column validation > belongs to`, () => {
// belongs to field validation
verifyLtarCell("City", 1, "al-Ayn", {
unlink: false,
plus: false,
expand: false,
});
});
it(`Share GRID view : Virtual column validation > many to many`, () => {
// many to many field verification
verifyLtarCell("Staff List1", 1, "", {
unlink: false,
plus: false,
expand: true,
});
actionLtarCell("Staff List1", 1, ".nc-arrow-expand");
verifyChildListCard("", {
reload: true,
linkTo: false,
unlink: false,
delete: false,
});
});
it(`Delete ${viewType.toUpperCase()} view`, () => {
// go back to base page
loginPage.loginAndOpenProject(apiType, dbType);
cy.openTableTab("Address", 25);
// number of view entries should be 2 before we delete
cy.get(".nc-view-item").its("length").should("eq", 2);
cy.get(".nc-view-delete-icon").eq(0).click({ force: true });
cy.getActiveModal(".nc-modal-view-delete")
.find(".ant-btn-dangerous")
.click();
cy.toastWait("View deleted successfully");
// confirm if the number of veiw entries is reduced by 1
cy.get(".nc-view-item").its("length").should("eq", 1);
});
};
// below scenario's will be invoked twice, once for rest & then for graphql
viewTest("grid");
});
describe(`${apiType.toUpperCase()} api - Grid view/ row-column update verification`, () => {
before(() => {
cy.restoreLocalStorage();
// Address table has belongs to, has many & many-to-many
cy.openTableTab("Country", 25);
// store base URL- to re-visit and delete form view later
cy.url().then((url) => {
storedURL = url;
generateViewLink("rowColUpdate");
});
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
cy.restoreLocalStorage();
cy.closeTableTab("Country");
cy.saveLocalStorage();
});
it(`Generate default Shared GRID view URL`, () => {
// add row
mainPage.addNewRowExpand("Country");
cy.get(".nc-expand-col-Country")
.find(".nc-cell > input")
.should("exist")
.first()
.clear({ force: true })
.type("a");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Save row")
.should("exist")
.click();
cy.toastWait("updated successfully");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Cancel")
.should("exist")
.click();
// add column
mainPage.addColumn("dummy", "Country");
cy.signOut();
// visit public view
cy.log(viewURL["rowColUpdate"]);
cy.visit(viewURL["rowColUpdate"], {
baseUrl: null,
});
cy.wait(5000);
// wait for public view page to load!
// wait for page rendering to complete
cy.get(".nc-grid-row").should("have.length", 25);
});
it(`Share GRID view : new row visible`, () => {
// verify row
// cy.get(`.v-pagination > li:contains('5') button`).click();
cy.get(
`.nc-pagination > .ant-pagination-item.ant-pagination-item-5`
).click();
// wait for page rendering to complete
cy.get(".nc-grid-row").should("have.length", 10);
mainPage.getCell("Country", 10).contains("a").should("exist");
});
it(`Share GRID view : new column visible`, () => {
// verify column headers
cy.get('[data-title="dummy"]').should("exist");
});
it(`Clean up`, () => {
loginPage.loginAndOpenProject(apiType, dbType);
cy.openTableTab("Country", 25);
// delete row
mainPage.getPagination(5).click();
// kludge: flicker on load
cy.wait(3000);
// wait for page rendering to complete
cy.get(".nc-grid-row").should("have.length", 10);
mainPage.getCell("Country", 10).rightclick();
cy.getActiveMenu(".nc-dropdown-grid-context-menu")
.find('.ant-dropdown-menu-item:contains("Delete Row")')
.first()
.click();
// delete column
mainPage.deleteColumn("dummy");
mainPage.deleteCreatedViews();
});
});
};

210
scripts/cypress/integration/common/4g_table_view_expanded_form.js

@ -1,210 +0,0 @@
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
import { loginPage } from "../../support/page_objects/navigation";
import { mainPage } from "../../support/page_objects/mainPage";
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
// function verifyExpandFormHeader(title) {
// cy.getActiveDrawer(".nc-drawer-expanded-form")
// .should("exist")
// .find(".nc-expanded-form-header")
// .contains(title)
// .should("exist");
// }
function verifyExpandFormHeader(title) {
cy.get(
`.nc-drawer-expanded-form .nc-expanded-form-header :contains("${title}")`
).should("exist");
}
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
let clear;
describe(`${apiType.toUpperCase()} api - Table views: Expanded form`, () => {
before(() => {
cy.restoreLocalStorage();
// open a table to work on views
//
cy.openTableTab("Country", 25);
clear = Cypress.LocalStorage.clear;
Cypress.LocalStorage.clear = () => {};
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
cy.restoreLocalStorage();
cy.closeTableTab("Country");
cy.saveLocalStorage();
Cypress.LocalStorage.clear = clear;
});
// Common routine to create/edit/delete GRID & GALLERY view
// Input: viewType - 'grid'/'gallery'
//
const viewTest = (viewType) => {
it(`Create ${viewType} view`, () => {
// click on 'Grid/Gallery' button on Views bar
cy.get(`.nc-create-${viewType}-view`).click();
// Pop up window, click Submit (accepting default name for view)
cy.getActiveModal(".nc-modal-view-create")
.find(".ant-btn-primary")
.click();
cy.toastWait("View created successfully");
// validate if view was created && contains default name 'Country1'
cy.get(`.nc-${viewType}-view-item`)
.contains(`${capitalizeFirstLetter(viewType)}-1`)
.should("exist");
if (viewType === "gallery") {
// http://localhost:8080/api/v1/db/data/noco/p_4ufoizgrorwyey/md_g0zc9d40w8zpmy/views/vw_xauikhkm8r49fy?offset=0&limit=25
cy.intercept("/api/v1/db/data/noco/**").as("getGalleryViewData");
// mainPage.unhideField("City List");
cy.get(".nc-fields-menu-btn").click();
cy.getActiveMenu(".nc-dropdown-fields-menu")
.find(`.nc-fields-list label:contains("City List"):visible`)
.click();
cy.get(".nc-fields-menu-btn").click();
cy.wait(["@getGalleryViewData"]);
cy.get('.ant-card-body [title="City List"]').should("exist");
}
});
it(`Expand a row in ${viewType} and verify url`, () => {
// click on first row-expand if grid & first card if its gallery
if (viewType === "grid") {
cy.get(".nc-row-expand").first().click({ force: true });
// wait for page render to complete
cy.get('button:contains("Save row"):visible').should("exist");
} else if (viewType === "gallery") {
cy.get(".nc-gallery-container .ant-card").first().click();
}
// ensure expand draw is open
verifyExpandFormHeader("Afghanistan");
cy.url().should("include", "rowId=1");
// spy on clipboard to verify copied text
// creating alias for clipboard
cy.window().then((win) => {
cy.spy(win.navigator.clipboard, "writeText").as("copy");
});
// copy url
cy.getActiveDrawer(".nc-drawer-expanded-form")
.should("exist")
.find(".nc-copy-row-url")
.click();
// use alias; verify if clipboard was called with correct text
cy.get("@copy").should("be.calledWithMatch", `?rowId=1`);
// close expanded form
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find(".nc-expand-form-close-btn")
.click();
});
it(`Visit a ${viewType} row url and verify expanded form`, () => {
cy.url().then((url) => {
cy.visit(
"/" + url.split("/").slice(3).join("/").split("?")[0] + "?rowId=2"
);
verifyExpandFormHeader("Algeria");
});
});
it(`Visit an invalid ${viewType} row url and verify expanded form`, () => {
cy.url().then((url) => {
cy.visit(
"/" +
url.split("/").slice(3).join("/").split("?")[0] +
"?rowId=99999999"
);
cy.toastWait("Record not found");
cy.get(`.nc-drawer-expanded-form .ant-drawer-content:visible`).should(
"not.exist"
);
// defaults to corresponding grid / gallery view
cy.get(viewType === "grid" ? ".nc-grid" : ".nc-gallery").should(
"exist"
);
});
});
it(`Visit a ${viewType} row url and verify nested expanded form`, () => {
cy.url().then((url) => {
cy.visit(
"/" + url.split("/").slice(3).join("/").split("?")[0] + "?rowId=1"
);
verifyExpandFormHeader("Afghanistan");
cy.get(".nc-drawer-expanded-form .ant-drawer-content").should(
"exist"
);
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find(".ant-card-body")
.first()
.click();
cy.get(".nc-drawer-expanded-form .ant-drawer-content").should(
"have.length",
2
);
cy.wait(1000);
verifyExpandFormHeader("Kabul");
// close expanded forms
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find(".ant-btn")
.contains("Cancel")
.click();
verifyExpandFormHeader("Afghanistan");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find(".ant-btn")
.contains("Cancel")
.click();
});
});
it("Delete view", () => {
cy.get(".nc-view-delete-icon").click({ force: true });
cy.getActiveModal(".nc-modal-view-delete")
.find(".ant-btn-dangerous")
.click();
cy.toastWait("View deleted successfully");
});
};
// viewTest("grid"); // grid view
viewTest("gallery"); // gallery view
});
};

541
scripts/cypress/integration/common/4h_kanban.js

@ -1,541 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import {
isTestSuiteActive,
isXcdb,
} from "../../support/page_objects/projectConstants";
import { loginPage } from "../../support/page_objects/navigation";
// kanban grouping field configuration
//
function configureGroupingField(field, closeMenu = true) {
cy.get(".nc-kanban-stacked-by-menu-btn").click();
cy.getActiveMenu(".nc-dropdown-kanban-stacked-by-menu")
.should("exist")
.find(".nc-kanban-grouping-field-select")
.click();
cy.get(".ant-select-dropdown:visible")
.should("exist")
.find(`.ant-select-item`)
.contains(new RegExp("^" + field + "$", "g"))
.should("exist")
.click();
if (closeMenu) {
cy.get(".nc-kanban-stacked-by-menu-btn").click();
}
cy.get(".nc-kanban-stacked-by-menu-btn")
.contains(`Stacked By ${field}`)
.should("exist");
}
// number of kanban stacks altogether
//
function verifyKanbanStackCount(count) {
cy.get(".nc-kanban-stack").should("have.length", count);
}
// order of kanban stacks
//
function verifyKanbanStackOrder(order) {
cy.get(".nc-kanban-stack").each(($el, index) => {
cy.wrap($el).should("contain", order[index]);
});
}
// kanban stack footer numbers
//
function verifyKanbanStackFooterCount(count) {
cy.get(".nc-kanban-stack").each(($el, index) => {
cy.wrap($el)
.scrollIntoView()
.find(".nc-kanban-data-count")
.should(
"contain",
`${count[index]} record${count[index] !== 1 ? "s" : ""}`
);
});
}
// kanban card count in a stack
//
function verifyKanbanStackCardCount(count) {
cy.get(".nc-kanban-stack").each(($el, index) => {
if (count[index] > 0) {
cy.wrap($el)
.find(".nc-kanban-item")
.should("exist")
.should("have.length", count[index]);
}
});
}
// order of cards within a stack
//
function verifyKanbanStackCardOrder(order, stackIndex, cardIndex) {
cy.get(".nc-kanban-stack")
.eq(stackIndex)
.find(".nc-kanban-item")
.eq(cardIndex)
.should("contain", order);
}
// drag drop kanban card
//
function dragAndDropKanbanCard(srcCard, dstCard) {
cy.get(`.nc-kanban-item .ant-card :visible:contains("${srcCard}")`).drag(
`.nc-kanban-item :visible:contains("${dstCard}")`
);
}
// drag drop kanban stack
//
function dragAndDropKanbanStack(srcStack, dstStack) {
cy.get(`.nc-kanban-stack-head :contains("${srcStack}")`).drag(
`.nc-kanban-stack-head :contains("${dstStack}")`
);
}
let localDebug = false;
function addOption(index, value) {
cy.getActiveMenu(".nc-dropdown-edit-column")
.find(".ant-btn-dashed")
.should("exist")
.click();
cy.get(".nc-dropdown-edit-column .nc-select-option").should(
"have.length",
index
);
cy.get(".nc-dropdown-edit-column .nc-select-option")
.last()
.find("input")
.click()
.type(value);
}
function editColumn() {
cy.get(`[data-title="Rating"]`).first().scrollIntoView();
cy.get(`th:contains("Rating") .nc-icon.ant-dropdown-trigger`)
.trigger("mouseover", { force: true })
.click({ force: true });
cy.getActiveMenu(".nc-dropdown-column-operations")
.find(".nc-column-edit")
.click();
cy.inputHighlightRenderWait();
// change column type and verify
cy.getActiveMenu(".nc-dropdown-edit-column")
.find(".nc-column-type-input")
.last()
.click()
.type("SingleSelect");
cy.getActiveSelection(".nc-dropdown-column-type")
.find(".ant-select-item-option")
.contains("SingleSelect")
.click();
cy.inputHighlightRenderWait();
addOption(1, "G");
addOption(2, "PG");
addOption(3, "PG-13");
addOption(4, "R");
addOption(5, "NC-17");
cy.getActiveMenu(".nc-dropdown-edit-column")
.find(".ant-btn-primary:visible")
.contains("Save")
.click();
cy.toastWait("Column updated");
}
// test suite
//
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
let clear;
describe(`${apiType.toUpperCase()} api - Kanban`, () => {
before(() => {
cy.restoreLocalStorage();
if (dbType === "postgres" || dbType === "xcdb") {
cy.openTableTab("Film", 25);
if (dbType === "postgres") {
// delete SQL views
cy.deleteTable("NicerButSlowerFilmList");
cy.deleteTable("FilmList");
}
// edit `rating` column: from custom DB type to single select
editColumn();
cy.closeTableTab("Film");
}
clear = Cypress.LocalStorage.clear;
Cypress.LocalStorage.clear = () => {};
});
// beforeEach(() => {
// cy.restoreLocalStorage();
// });
//
// afterEach(() => {
// cy.saveLocalStorage();
// });
after(() => {
Cypress.LocalStorage.clear = clear;
cy.saveLocalStorage();
});
/**
class name specific to kanban view
.nc-kanban-stacked-by-menu-btn
.nc-dropdown-kanban-stacked-by-menu
.nc-kanban-add-edit-stack-menu-btn
.nc-dropdown-kanban-add-edit-stack-menu
.nc-kanban-grouping-field-select
.nc-dropdown-kanban-stack-context-menu
**/
it("Create Kanban view", () => {
if (localDebug === false) {
cy.openTableTab("Film", 25);
cy.viewCreate("kanban");
}
});
it("Rename Kanban view", () => {
cy.viewRename("kanban", 0, "Film Kanban");
});
it("Configure grouping field", () => {
configureGroupingField("Rating", true);
});
it("Verify kanban stacks", () => {
verifyKanbanStackCount(6);
verifyKanbanStackOrder([
"uncategorized",
"G",
"PG",
"PG-13",
"R",
"NC-17",
]);
verifyKanbanStackFooterCount([0, 178, 194, 223, 195, 210]);
verifyKanbanStackCardCount([0, 25, 25, 25, 25, 25]);
});
it("Hide fields", () => {
mainPage.hideAllColumns();
mainPage.unhideField("Title", "kanban");
verifyKanbanStackCardCount([0, 25, 25, 25, 25, 25]);
});
it("Verify card order", () => {
// verify 3 cards from each stack
verifyKanbanStackCardOrder("ACE GOLDFINGER", 1, 0);
verifyKanbanStackCardOrder("AFFAIR PREJUDICE", 1, 1);
verifyKanbanStackCardOrder("AFRICAN EGG", 1, 2);
verifyKanbanStackCardOrder("ACADEMY DINOSAUR", 2, 0);
verifyKanbanStackCardOrder("AGENT TRUMAN", 2, 1);
verifyKanbanStackCardOrder("ALASKA PHANTOM", 2, 2);
verifyKanbanStackCardOrder("AIRPLANE SIERRA", 3, 0);
verifyKanbanStackCardOrder("ALABAMA DEVIL", 3, 1);
verifyKanbanStackCardOrder("ALTER VICTORY", 3, 2);
verifyKanbanStackCardOrder("AIRPORT POLLOCK", 4, 0);
verifyKanbanStackCardOrder("ALONE TRIP", 4, 1);
verifyKanbanStackCardOrder("AMELIE HELLFIGHTERS", 4, 2);
verifyKanbanStackCardOrder("ADAPTATION HOLES", 5, 0);
verifyKanbanStackCardOrder("ALADDIN CALENDAR", 5, 1);
verifyKanbanStackCardOrder("ALICE FANTASIA", 5, 2);
});
it.skip("Verify inter-stack drag and drop", () => {
dragAndDropKanbanCard("ACE GOLDFINGER", "ACADEMY DINOSAUR");
verifyKanbanStackCardOrder("AFFAIR PREJUDICE", 1, 0);
verifyKanbanStackCardOrder("ACE GOLDFINGER", 2, 0);
verifyKanbanStackCardOrder("ACADEMY DINOSAUR", 2, 1);
dragAndDropKanbanCard("ACE GOLDFINGER", "AFFAIR PREJUDICE");
verifyKanbanStackCardOrder("ACE GOLDFINGER", 1, 0);
verifyKanbanStackCardOrder("AFFAIR PREJUDICE", 1, 1);
verifyKanbanStackCardOrder("ACADEMY DINOSAUR", 2, 0);
});
it.skip("Verify intra-stack drag and drop", () => {
dragAndDropKanbanCard("ACE GOLDFINGER", "AFFAIR PREJUDICE");
verifyKanbanStackCardOrder("AFFAIR PREJUDICE", 1, 0);
verifyKanbanStackCardOrder("ACE GOLDFINGER", 1, 1);
dragAndDropKanbanCard("ACE GOLDFINGER", "AFFAIR PREJUDICE");
verifyKanbanStackCardOrder("ACE GOLDFINGER", 1, 0);
verifyKanbanStackCardOrder("AFFAIR PREJUDICE", 1, 1);
});
it("Verify stack drag drop", () => {
verifyKanbanStackOrder([
"uncategorized",
"G",
"PG",
"PG-13",
"R",
"NC-17",
]);
dragAndDropKanbanStack("PG-13", "R");
verifyKanbanStackOrder([
"uncategorized",
"G",
"PG",
"R",
"PG-13",
"NC-17",
]);
dragAndDropKanbanStack("PG-13", "R");
verifyKanbanStackOrder([
"uncategorized",
"G",
"PG",
"PG-13",
"R",
"NC-17",
]);
});
it("Verify Sort", () => {
mainPage.sortField("Title", "Z → A");
verifyKanbanStackCardOrder("YOUNG LANGUAGE", 1, 0);
verifyKanbanStackCardOrder("WEST LION", 1, 1);
verifyKanbanStackCardOrder("WORST BANGER", 2, 0);
verifyKanbanStackCardOrder("WORDS HUNTER", 2, 1);
mainPage.clearSort();
verifyKanbanStackCardOrder("ACE GOLDFINGER", 1, 0);
verifyKanbanStackCardOrder("AFFAIR PREJUDICE", 1, 1);
verifyKanbanStackCardOrder("ACADEMY DINOSAUR", 2, 0);
verifyKanbanStackCardOrder("AGENT TRUMAN", 2, 1);
});
it("Verify Filter", () => {
mainPage.filterField("Title", "is like", "BA");
verifyKanbanStackCardOrder("BAKED CLEOPATRA", 1, 0);
verifyKanbanStackCardOrder("BALLROOM MOCKINGBIRD", 1, 1);
verifyKanbanStackCardOrder("ARIZONA BANG", 2, 0);
verifyKanbanStackCardOrder("EGYPT TENENBAUMS", 2, 1);
mainPage.filterReset();
verifyKanbanStackCardOrder("ACE GOLDFINGER", 1, 0);
verifyKanbanStackCardOrder("AFFAIR PREJUDICE", 1, 1);
verifyKanbanStackCardOrder("ACADEMY DINOSAUR", 2, 0);
verifyKanbanStackCardOrder("AGENT TRUMAN", 2, 1);
});
// it("Stack context menu- rename stack", () => {
// verifyKanbanStackCount(6);
// cy.get('.nc-kanban-stack-head').eq(1).find('.ant-dropdown-trigger').click();
// cy.getActiveMenu('.nc-dropdown-kanban-stack-context-menu').should('be.visible');
// cy.getActiveMenu('.nc-dropdown-kanban-stack-context-menu')
// .find('.ant-dropdown-menu-item')
// .contains('Rename Stack')
// .click();
// })
it("Stack context menu- delete stack", () => {});
it("Stack context menu- collapse stack", () => {});
it("Copy view", () => {
mainPage.sortField("Title", "Z → A");
mainPage.filterField("Title", "is like", "BA");
cy.viewCopy(1);
// verify copied view
cy.get(".nc-kanban-stacked-by-menu-btn")
.contains(`Stacked By Rating`)
.should("exist");
verifyKanbanStackCount(6);
verifyKanbanStackOrder([
"uncategorized",
"G",
"PG",
"PG-13",
"R",
"NC-17",
]);
verifyKanbanStackFooterCount([0, 4, 5, 8, 6, 6]);
verifyKanbanStackCardOrder("BAREFOOT MANCHURIAN", 1, 0);
verifyKanbanStackCardOrder("WORST BANGER", 2, 0);
cy.viewDelete(1);
});
it("Add stack", () => {
cy.viewOpen("kanban", 0);
cy.get(".nc-kanban-add-edit-stack-menu-btn").should("exist").click();
cy.getActiveMenu(".nc-dropdown-kanban-add-edit-stack-menu").should(
"be.visible"
);
cy.getActiveMenu(".nc-dropdown-kanban-add-edit-stack-menu")
.find(".ant-btn-dashed")
.click();
cy.getActiveMenu(".nc-dropdown-kanban-add-edit-stack-menu")
.find(".nc-select-option")
.last()
.click()
.type("Test{enter}");
verifyKanbanStackCount(7);
verifyKanbanStackOrder([
"uncategorized",
"G",
"PG",
"PG-13",
"R",
"NC-17",
"Test",
]);
});
it("Collapse stack", () => {
cy.get(".nc-kanban-stack-head").last().scrollIntoView();
cy.get(".nc-kanban-stack-head").last().click();
cy.getActiveMenu(".nc-dropdown-kanban-stack-context-menu").should(
"be.visible"
);
// collapse stack
cy.getActiveMenu(".nc-dropdown-kanban-stack-context-menu")
.find(".ant-dropdown-menu-item")
.contains("Collapse Stack")
.click();
cy.get(".nc-kanban-collapsed-stack")
.should("exist")
.should("have.length", 1);
// expand back
cy.get(".nc-kanban-collapsed-stack").click();
cy.get(".nc-kanban-collapsed-stack").should("not.exist");
});
it("Add record to stack", () => {
mainPage.hideAllColumns();
mainPage.toggleShowSystemFields();
mainPage.unhideField("LanguageId", "kanban");
mainPage.unhideField("Title", "kanban");
mainPage.filterReset();
mainPage.clearSort();
// skip for xcdb: many mandatory fields
if (!isXcdb()) {
cy.get(".nc-kanban-stack-head").last().scrollIntoView();
cy.get(".nc-kanban-stack-head").last().click();
cy.getActiveMenu(".nc-dropdown-kanban-stack-context-menu").should(
"be.visible"
);
// add record
cy.getActiveMenu(".nc-dropdown-kanban-stack-context-menu")
.find(".ant-dropdown-menu-item")
.contains("Add new record")
.click();
cy.getActiveDrawer(".nc-drawer-expanded-form").should("be.visible");
cy.get(".nc-expand-col-Title")
.find(".nc-cell > input")
.should("exist")
.first()
.clear()
.type("New record");
cy.get(".nc-expand-col-LanguageId")
.find(".nc-cell > input")
.should("exist")
.first()
.clear()
.type("1");
cy.getActiveDrawer(".nc-drawer-expanded-form")
.find("button")
.contains("Save row")
.click();
cy.toastWait("updated successfully");
cy.get("body").type("{esc}");
// verify if the new record is in the stack
verifyKanbanStackCount(7);
verifyKanbanStackOrder([
"uncategorized",
"G",
"PG",
"PG-13",
"R",
"NC-17",
"Test",
]);
verifyKanbanStackCardCount([0, 25, 25, 25, 25, 25, 1]);
}
mainPage.toggleShowSystemFields();
});
it("Expand record", () => {
// mainPage.toggleShowSystemFields();
// mainPage.showAllColumns();
cy.get(".nc-kanban-stack").eq(1).find(".nc-kanban-item").eq(0).click();
cy.get(".nc-expand-col-Title")
.find(".nc-cell > input")
.then(($el) => {
expect($el[0].value).to.have.string("ACE GOLDFINGER");
});
cy.get("body").type("{esc}");
});
it("Stack context menu- delete stack", () => {
if (!isXcdb()) {
cy.get(".nc-kanban-stack-head").last().scrollIntoView();
cy.get(".nc-kanban-stack-head").last().click();
cy.getActiveMenu(".nc-dropdown-kanban-stack-context-menu").should(
"be.visible"
);
cy.getActiveMenu(".nc-dropdown-kanban-stack-context-menu")
.find(".ant-dropdown-menu-item")
.contains("Delete Stack")
.click();
cy.getActiveModal(".nc-modal-kanban-delete-stack").should("be.visible");
cy.getActiveModal(".nc-modal-kanban-delete-stack")
.find(".ant-btn-primary")
.click();
verifyKanbanStackCount(6);
verifyKanbanStackOrder([
"uncategorized",
"G",
"PG",
"PG-13",
"R",
"NC-17",
]);
verifyKanbanStackCardCount([1, 25, 25, 25, 25, 25]);
}
});
it("Delete Kanban view", () => {
cy.viewDelete(0);
cy.closeTableTab("Film");
});
});
};

191
scripts/cypress/integration/common/4i_survey_form.js

@ -1,191 +0,0 @@
// test suite
//
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
import { mainPage } from "../../support/page_objects/mainPage";
import { loginPage } from "../../support/page_objects/navigation";
let linkText = "";
const generateShareLink_surveyMode = () => {
mainPage.shareView().click();
// ensure modal is rendered and visible
cy.getActiveModal(".nc-modal-share-view")
.find(".ant-modal-title")
.contains("This view is shared via a private link")
.should("be.visible");
// enable survey mode
cy.get('[data-cy="nc-modal-share-view__survey-mode"]').click();
// copy link text, save URL
cy.get('[data-cy="nc-modal-share-view__link"]').then(($el) => {
linkText = $el.text();
cy.log(linkText);
});
};
// Options:
// footer
// btnSubmit
// fieldLabel
//
function validateFormPage(options) {
// header & description
cy.get('[data-cy="nc-survey-form__heading"]')
.contains("A B C D")
.should("be.visible");
cy.get('[data-cy="nc-survey-form__sub-heading"]')
.contains("Survey form for testing")
.should("be.visible");
// footer (page index)
cy.get('[data-cy="nc-survey-form__footer"]')
.contains(options.footer)
.should("be.visible");
// submit button: will be either OK or Submit
cy.get(`[data-cy="nc-survey-form__${options.btnSubmit}"]`).should(
"be.visible"
);
// field label
cy.get(
`[data-cy="nc-survey-form__input-${options.fieldLabel.replaceAll(
" ",
""
)}"]`
).should("be.visible");
}
// test suite
//
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
let clear;
/**
* class names specific to survey mode
* data-cy="nc-survey-form__heading"
* data-cy="nc-survey-form__sub-heading"
* data-cy="nc-survey-form__input-${fieldTitle}"
* data-cy="nc-survey-form__field-description"
* data-cy="nc-survey-form__btn-submit"
* data-cy="nc-survey-form__btn-next"
* data-cy="nc-survey-form__success-msg"
* data-cy="nc-survey-form__btn-submit-another-form"
* data-cy="nc-survey-form__footer"
* data-cy="nc-survey-form__icon-next"
* data-cy="nc-survey-form__icon-prev"
*/
describe(`${apiType.toUpperCase()} api - Kanban`, () => {
before(() => {
cy.restoreLocalStorage();
// disable CY storage handling
clear = Cypress.LocalStorage.clear;
Cypress.LocalStorage.clear = () => {};
});
after(() => {
// re-enable CY storage handling
Cypress.LocalStorage.clear = clear;
cy.saveLocalStorage();
});
it("Create form view", () => {
cy.openTableTab("Country", 25);
cy.viewCreate("form");
// prepare form
// wait for input fields to be rendered as enabled
cy.wait(2000);
// Update header & add some description
cy.get(".nc-form")
.find('[placeholder="Form Title"]')
.clear()
.type("A B C D");
cy.get(".nc-form")
.find('[placeholder="Add form description"]')
.type("Survey form for testing");
cy.get(".nc-form").click();
// add success message
cy.get("textarea.nc-form-after-submit-msg").type("Congratulations!");
// enable "Submit another form" check box
cy.get("button.nc-form-checkbox-submit-another-form").click();
// show another form after 5 seconds
cy.get("button.nc-form-checkbox-show-blank-form").click();
});
it("Share form, enable survey mode", () => {
generateShareLink_surveyMode();
cy.signOut();
});
it("Visit link, validate form", () => {
cy.visit(linkText);
// validate form
validateFormPage({
footer: "1 / 3",
btnSubmit: "btn-next",
fieldLabel: "Country",
});
cy.get('[data-cy="nc-survey-form__input-Country"]').type("x{enter}");
validateFormPage({
footer: "2 / 3",
btnSubmit: "btn-next",
fieldLabel: "LastUpdate",
});
cy.get('[data-cy="nc-survey-form__input-LastUpdate"]').click();
cy.get(".ant-picker-now-btn:visible").contains("Now").click();
cy.get(".ant-btn-primary:visible").contains("Ok").click();
cy.get('[data-cy="nc-survey-form__btn-next"]').click();
// takes time for the link field to be rendered
cy.wait(2000);
validateFormPage({
footer: "3 / 3",
btnSubmit: "btn-submit",
fieldLabel: "City List",
});
cy.get('[data-cy="nc-survey-form__btn-submit"]').click();
// validate success message
cy.get('[data-cy="nc-survey-form__success-msg"]')
.should("be.visible")
.contains("Congratulations!")
.should("be.visible");
// validate "Submit another form" button
cy.get('[data-cy="nc-survey-form__btn-submit-another-form"]').should(
"be.visible"
);
});
it("Delete form view", () => {
loginPage.loginAndOpenProject(apiType, dbType);
cy.openTableTab("Country", 25);
// clean up newly added rows into Country table operations
// this auto verifies successfull addition of rows to table as well
mainPage.getPagination(5).click();
// wait for page rendering to complete
cy.get(".nc-grid-row").should("have.length", 10);
mainPage.getCell("Country", 10).rightclick();
cy.getActiveMenu(".nc-dropdown-grid-context-menu")
.contains("Delete Row")
.click();
cy.viewDelete(0);
});
});
};

285
scripts/cypress/integration/common/5a_user_role.js

@ -1,285 +0,0 @@
import { loginPage, projectsPage } from "../../support/page_objects/navigation";
import { mainPage, settingsPage } from "../../support/page_objects/mainPage";
import {
isPostgres,
isXcdb,
roles,
staticProjects,
} from "../../support/page_objects/projectConstants";
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
import {
_advSettings,
_editSchema,
_editData,
_editComment,
_viewMenu,
_topRightMenu,
disableTableAccess,
_accessControl,
} from "../spec/roleValidation.spec";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe("Static user creations (different roles)", () => {
before(() => {
// standalone test
// loginPage.loginAndOpenProject(apiType, dbType);
// open a table to work on views
//
cy.restoreLocalStorage();
settingsPage.openMenu(settingsPage.TEAM_N_AUTH);
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
cy.restoreLocalStorage();
cy.signOut();
cy.saveLocalStorage();
});
const addUser = (user) => {
it(`RoleType: ${user.name}`, () => {
// for first project, users need to be added explicitly using "New User" button
// for subsequent projects, they will be required to just add to this project
// using ROW count to identify if its former or latter scenario
// 5 users (owner, creator, editor, viewer, commenter) = 5
// cy.get(`.nc-user-row`).then((obj) => {
// cy.log(obj.length);
// if (obj.length == 5) {
// mainPage.addExistingUserToProject(
// user.credentials.username,
// user.name
// );
// } else {
// mainPage.addNewUserToProject(
// user.credentials,
// user.name
// );
// }
// });
cy.get(`.nc-user-row`).should("exist");
mainPage.addNewUserToProject(user.credentials, user.name);
});
};
addUser(roles.creator);
addUser(roles.editor);
addUser(roles.commenter);
addUser(roles.viewer);
// Access control list- configuration
//
it(`Access control list- configuration`, () => {
mainPage.closeMetaTab();
// open Project metadata tab
//
settingsPage.openMenu(settingsPage.PROJ_METADATA);
settingsPage.openTab(settingsPage.UI_ACCESS_CONTROL);
// validate if it has 19 entries representing tables & views
if (isPostgres()) cy.get(".nc-acl-table-row").should("have.length", 24);
else if (isXcdb()) cy.get(".nc-acl-table-row").should("have.length", 19);
else cy.get(".nc-acl-table-row").should("have.length", 19);
// disable table & view access
//
disableTableAccess("Language", "editor");
disableTableAccess("Language", "commenter");
disableTableAccess("Language", "viewer");
disableTableAccess("CustomerList", "editor");
disableTableAccess("CustomerList", "commenter");
disableTableAccess("CustomerList", "viewer");
cy.get("button.nc-acl-save").click();
cy.toastWait("Updated UI ACL for tables successfully");
mainPage.closeMetaTab();
});
});
const roleValidation = (roleType) => {
let clear;
describe(`User role validation`, () => {
before(() => {
cy.restoreLocalStorage();
cy.visit(mainPage.roleURL[roleType]);
cy.get('button:contains("SIGN UP"):visible').should("exist");
cy.get('input[type="text"]', { timeout: 20000 }).type(
roles[roleType].credentials.username
);
cy.get('input[type="password"]').type(
roles[roleType].credentials.password
);
cy.get('button:contains("SIGN UP")').click();
cy.get(`.nc-project-page-title:contains("My Projects"):visible`).should(
"exist"
);
if (dbType === "xcdb") {
if ("rest" == apiType)
projectsPage.openProject(staticProjects.sampleREST.basic.name);
else projectsPage.openProject(staticProjects.sampleGQL.basic.name);
} else if (dbType === "mysql") {
if ("rest" == apiType)
projectsPage.openProject(staticProjects.externalREST.basic.name);
else projectsPage.openProject(staticProjects.externalGQL.basic.name);
} else if (dbType === "postgres") {
if ("rest" == apiType)
projectsPage.openProject(staticProjects.pgExternalREST.basic.name);
else
projectsPage.openProject(staticProjects.pgExternalGQL.basic.name);
}
if (roleType === "creator") {
cy.closeTableTab();
}
cy.saveLocalStorage();
clear = Cypress.LocalStorage.clear;
Cypress.LocalStorage.clear = () => {};
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
cy.restoreLocalStorage();
cy.signOut();
cy.saveLocalStorage();
Cypress.LocalStorage.clear = clear;
});
///////////////////////////////////////////////////////
// Test suite
it(`[${roles[roleType].name}] Left navigation menu, New User add`, () => {
// project configuration settings
//
if (roleType !== "owner") {
_advSettings(roleType, "userRole");
}
});
it(`[${roles[roleType].name}] Access control`, () => {
// Access control validation
//
if (roleType !== "owner") {
_accessControl(roleType, "userRole");
}
});
it(`[${roles[roleType].name}] Schema: create table, add/modify/delete column`, () => {
// Schema related validations
// - Add/delete table
// - Add/Update/delete column
//
if (roleType !== "owner") {
_editSchema(roleType, "userRole");
}
});
it(`[${roles[roleType].name}] Data: add/modify/delete row, update cell contents`, () => {
// Table data related validations
// - Add/delete/modify row
//
if (roleType !== "owner") {
_editData(roleType, "userRole");
}
});
it(`[${roles[roleType].name}] Comments: view/add`, () => {
// read &/ update comment
// Viewer: only allowed to read
// Everyone else: read &/ update
//
if (roleType !== "owner") {
_editComment(roleType, "userRole");
}
});
it(`[${roles[roleType].name}] Right navigation menu, share view`, () => {
// right navigation menu bar
// Editor/Viewer/Commenter : can only view 'existing' views
// Rest: can create/edit
if (roleType !== "owner") {
_viewMenu(roleType, "userRole");
}
});
it(`[${roles[roleType].name}] Download files`, () => {
// to be fixed
if (
roleType === "commenter" ||
roleType === "viewer" ||
roleType === "owner"
) {
} else {
// viewer & commenter doesn't contain hideField option in ncv2
// #ID, City, LastUpdate, City => Address, Country <= City, +
mainPage.hideField("LastUpdate");
const verifyCsv = (retrievedRecords) => {
// expected output, statically configured
let storedRecords = [
`City,Address List,Country`,
`A Corua (La Corua),939 Probolinggo Loop,Spain`,
`Abha,733 Mandaluyong Place,Saudi Arabia`,
`Abu Dhabi,535 Ahmadnagar Manor,United Arab Emirates`,
`Acua,1789 Saint-Denis Parkway,Mexico`,
];
// skip if xcdb
if (!isXcdb()) {
for (let i = 0; i < storedRecords.length; i++) {
// cy.log(retrievedRecords[i])
expect(retrievedRecords[i]).to.be.equal(storedRecords[i]);
}
}
};
// download & verify
mainPage.downloadAndVerifyCsv(
`City_exported_1.csv`,
verifyCsv,
roleType
);
mainPage.unhideField("LastUpdate");
}
});
it(`[${roles[roleType].name}] App store accessibility`, () => {
cy.visit("/#/apps").then((r) => {
cy.toastWait("You don't have enough permission to access the page.");
});
});
});
};
// skip owner validation as rest of the cases pretty much cover the same
// roleValidation("owner");
roleValidation("creator");
roleValidation("editor");
roleValidation("commenter");
roleValidation("viewer");
};

172
scripts/cypress/integration/common/5b_preview_role.js

@ -1,172 +0,0 @@
// pre-requisite:
// user@nocodb.com signed up as admin
// sakilaDb database created already
import { loginPage, projectsPage } from "../../support/page_objects/navigation";
import { mainPage, settingsPage } from "../../support/page_objects/mainPage";
import {
isPostgres,
isTestSuiteActive,
isXcdb,
roles,
} from "../../support/page_objects/projectConstants";
import {
_advSettings,
_editSchema,
_editData,
_editComment,
_viewMenu,
_topRightMenu,
enableTableAccess,
_accessControl,
disableTableAccess,
} from "../spec/roleValidation.spec";
export const genTest = (apiType, dbType, roleType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
///////////////////////////////////////////////////////////
//// Test Suite
let clear;
function configureAcl() {
// open Project metadata tab
//
settingsPage.openMenu(settingsPage.PROJ_METADATA);
settingsPage.openTab(settingsPage.UI_ACCESS_CONTROL);
// validate if it has 19 entries representing tables & views
if (isPostgres()) cy.get(".nc-acl-table-row").should("have.length", 24);
else if (isXcdb()) cy.get(".nc-acl-table-row").should("have.length", 19);
else cy.get(".nc-acl-table-row").should("have.length", 19);
// disable table & view access
//
disableTableAccess("Language", "editor");
disableTableAccess("Language", "commenter");
disableTableAccess("Language", "viewer");
disableTableAccess("CustomerList", "editor");
disableTableAccess("CustomerList", "commenter");
disableTableAccess("CustomerList", "viewer");
cy.get("button.nc-acl-save").click();
cy.toastWait("Updated UI ACL for tables successfully");
mainPage.closeMetaTab();
}
describe("Role preview validations", () => {
// Sign in/ open project
before(() => {
// cy.restoreLocalStorage();
loginPage.loginAndOpenProject(apiType, dbType);
clear = Cypress.LocalStorage.clear;
Cypress.LocalStorage.clear = () => {};
// configureAcl();
cy.openTableTab("City", 25);
settingsPage.openProjectMenu();
cy.getActiveMenu(".nc-dropdown-project-menu")
.find(`[data-submenu-id="preview-as"]`)
.should("exist")
.click();
cy.get(".nc-role-preview-menu").should("have.length", 3);
cy.get(`.nc-role-preview-menu:contains("Editor")`)
.should("exist")
.click();
});
after(() => {
cy.get(".nc-preview-btn-exit-to-app").click();
// wait for page rendering to complete
cy.get(".nc-grid-row", { timeout: 25000 }).should("have.length", 25);
cy.closeTableTab("City");
// open Project metadata tab
//
settingsPage.openMenu(settingsPage.PROJ_METADATA);
settingsPage.openTab(settingsPage.UI_ACCESS_CONTROL);
// validate if it has 19 entries representing tables & views
if (isPostgres()) cy.get(".nc-acl-table-row").should("have.length", 24);
else if (isXcdb()) cy.get(".nc-acl-table-row").should("have.length", 19);
else cy.get(".nc-acl-table-row").should("have.length", 19);
// restore access
//
enableTableAccess("Language", "editor");
enableTableAccess("Language", "commenter");
enableTableAccess("Language", "viewer");
enableTableAccess("CustomerList", "editor");
enableTableAccess("CustomerList", "commenter");
enableTableAccess("CustomerList", "viewer");
cy.saveLocalStorage();
Cypress.LocalStorage.clear = clear;
});
const genTestSub = (roleType) => {
it(`Role preview: ${roleType}: Enable preview`, () => {
cy.get(".nc-floating-preview-btn", { timeout: 30000 }).should("exist");
cy.get(".nc-floating-preview-btn")
.find(`[type="radio"][value="${roleType}"]`)
.should("exist")
.click();
});
it(`Role preview: ${roleType}: Advance settings`, () => {
// project configuration settings
//
_advSettings(roleType, "preview");
});
it(`Role preview: ${roleType}: Access control`, () => {
// Access control validation
//
_accessControl(roleType, "preview");
});
it(`Role preview: ${roleType}: Edit data`, () => {
// Table data related validations
// - Add/delete/modify row
//
_editData(roleType, "preview");
});
it(`Role preview: ${roleType}: Edit comment`, () => {
// read &/ update comment
// Viewer: not allowed to read
// Everyone else: read &/ update
//
_editComment(roleType, "preview");
});
it(`Role preview: ${roleType}: Preview menu`, () => {
// right navigation menu bar
// Editor/Viewer/Commenter : can only view 'existing' views
// Rest: can create/edit
_viewMenu(roleType, "preview");
});
it(`Role preview: ${roleType}: Edit Schema`, () => {
// Schema related validations
// - Add/delete table
// - Add/Update/delete column
//
_editSchema(roleType, "preview");
});
};
genTestSub("editor");
genTestSub("commenter");
genTestSub("viewer");
});
};

165
scripts/cypress/integration/common/5c_super_user_role.js

@ -1,165 +0,0 @@
export const genTest = (apiType, dbType) => {
describe(`${apiType.toUpperCase()} api - Super user test`, () => {
before(() => {
});
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
after(() => {
});
it(`Open App store page and check slack app`, () => {
cy.visit('/#/apps').then((win) => {
cy.get('.nc-app-store-title').should('exist');
cy.get('.nc-app-store-card-Slack').should('exist');
// install slack app
cy.get('.nc-app-store-card-Slack .install-btn').invoke(
'attr',
'style',
'right: 10px'
);
cy.get(
'.nc-app-store-card-Slack .install-btn .nc-app-store-card-install'
).click();
cy.getActiveModal('.nc-modal-plugin-install')
.find('[placeholder="Channel Name"]')
.type('Test channel');
cy.getActiveModal('.nc-modal-plugin-install')
.find('[placeholder="Webhook URL"]')
.type('http://test.com');
cy.getActiveModal('.nc-modal-plugin-install')
.find('button:contains("Save")')
.click();
cy.toastWait('Successfully installed');
cy.get(
'.nc-app-store-card-Slack .install-btn .nc-app-store-card-install'
).should('not.exist');
// update slack app config
cy.get('.nc-app-store-card-Slack .install-btn .nc-app-store-card-edit')
.should('exist')
.click();
cy.getActiveModal('.nc-modal-plugin-install')
.should('exist')
.find('[placeholder="Channel Name"]')
.should('have.value', 'Test channel')
.clear()
.type('Test channel 2');
cy.getActiveModal('.nc-modal-plugin-install')
.get('button:contains("Save")')
.click();
cy.toastWait('Successfully installed');
// reset slack app
cy.get('.nc-app-store-card-Slack .install-btn .nc-app-store-card-reset')
.should('exist')
.click();
cy.getActiveModal('.nc-modal-plugin-uninstall')
.should('exist')
.find('button:contains("Confirm")')
.click();
cy.toastWait('Plugin uninstalled successfully');
});
});
it(`Open super user management page and add/delete user`, () => {
// delay for avoiding error related to vue router change
cy.wait(500);
cy.visit('/#/account/users').then((win) => {
cy.get('[data-cy="nc-super-user-list"]').should('exist')
.find('tbody tr').then((rows) => {
const initialUserCount = rows.length;
cy.get('[data-cy=\'nc-super-user-invite\'')
.click();
// additional wait to ensure the modal is fully loaded
cy.getActiveModal('.nc-modal-invite-user').should('exist');
cy.getActiveModal('.nc-modal-invite-user')
.find('input[placeholder="E-mail"]')
.should('exist');
cy.getActiveModal('.nc-modal-invite-user')
.find('input[placeholder="E-mail"]')
.type('test@nocodb.com');
cy.getActiveModal('.nc-modal-invite-user')
.find('.ant-select.nc-user-roles')
.click();
cy.getActiveModal('.nc-modal-invite-user')
.find('button.ant-btn-primary')
.click();
cy.toastWait('Successfully added user');
cy.getActiveModal().find('[data-cy="nc-root-user-invite-modal-close"]').click();
cy.get('[data-cy="nc-super-user-list"]').should('exist')
.find('tbody tr').should('have.length', initialUserCount +1)
.last().find('[data-cy="nc-super-user-delete"]').click();
cy.getActiveModal().find('.ant-modal-confirm-btns .ant-btn-primary').click();
cy.toastWait('User deleted successfully');
cy.get('[data-cy="nc-super-user-list"]').should('exist')
.find('tbody tr').should('have.length', initialUserCount);
});
});
});
it('User management settings', () => {
});
it(`Token management`, () => {
// delay for avoiding error related to vue router change
cy.wait(500);
cy.visit('/#/account/tokens').then((win) => {
cy.get('[data-cy="nc-token-list"]').should('exist').find(':contains("No Data")').should('exist');
cy.get('[data-cy="nc-token-create"]').click();
cy.get('[data-cy="nc-token-modal-description"]').type('Descriptqion');
cy.get('[data-cy="nc-token-modal-save"]').click();
cy.toastWait('Token generated successfully');
cy.get('[data-cy="nc-token-list"]').should('exist')
.find('tbody tr').should('have.length', 1);
cy.get('.nc-token-menu').click();
cy.getActiveMenu('.nc-dropdown-api-token-mgmt').find('.ant-dropdown-menu-item:contains("Remove")').click();
cy.getActiveModal().find('.ant-modal-confirm-btns .ant-btn-primary').click();
cy.toastWait('Token deleted successfully');
});
});
});
};

76
scripts/cypress/integration/common/6b_downloadCsv.js

@ -1,76 +0,0 @@
import { mainPage } from "../../support/page_objects/mainPage";
import { loginPage } from "../../support/page_objects/navigation";
import {
isPostgres,
isTestSuiteActive,
} from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} Upload/ Download CSV`, () => {
// before(() => {
// // standalone test
// // loginPage.loginAndOpenProject(apiType, dbType);
// });
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
it("Download verification- base view, default columns", () => {
cy.openTableTab("Country", 25);
mainPage.hideField("LastUpdate");
const verifyCsv = (retrievedRecords) => {
// expected output, statically configured
// let storedRecords = [
// `Country,CityList`,
// `Afghanistan,Kabul`,
// `Algeria,"Batna, Bchar, Skikda"`,
// `American Samoa,Tafuna`,
// `Angola,"Benguela, Namibe"`,
// ];
let storedRecords = [
["Country", "City List"],
["Afghanistan", "Kabul"],
["Algeria", "Skikda", "Bchar", "Batna"],
["American Samoa", "Tafuna"],
["Angola", "Benguela", "Namibe"],
];
// if (isPostgres()) {
// // order of second entry is different
// storedRecords = [
// `Country,City List`,
// `Afghanistan,Kabul`,
// `Algeria,"Skikda, Bchar, Batna"`,
// `American Samoa,Tafuna`,
// `Angola,"Benguela, Namibe"`,
// ];
// }
for (let i = 0; i < storedRecords.length - 1; i++) {
for (let j = 0; j < storedRecords[i].length; j++)
expect(retrievedRecords[i]).to.have.string(storedRecords[i][j]);
// often, the order in which records "Skikda, Bchar, Batna" appear, used to toggle
// hence verifying record contents separately
// expect(retrievedRecords[i]).to.be.equal(storedRecords[i]);
}
};
// download & verify
mainPage.downloadAndVerifyCsv(`Country_exported_1.csv`, verifyCsv);
mainPage.unhideField("LastUpdate");
cy.closeTableTab("Country");
});
});
};

236
scripts/cypress/integration/common/6c_swagger_api.js

@ -1,236 +0,0 @@
import { loginPage } from "../../support/page_objects/navigation";
import { mainPage } from "../../support/page_objects/mainPage";
import { roles } from "../../support/page_objects/projectConstants";
import { isTestSuiteActive } from "../../support/page_objects/projectConstants";
export const genTest = (apiType, dbType) => {
if (!isTestSuiteActive(apiType, dbType)) return;
describe(`${apiType.toUpperCase()} : API List - Test preparation`, () => {
before(() => {
cy.fileHook();
loginPage.loginAndOpenProject(apiType, dbType);
});
it("Open project & record swagger URL, AuthToken", () => {
let authToken = mainPage.getAuthToken();
cy.url().then((url) => {
// retrieve project name from URL & use it to construct Swagger URL
// URL on homepage: http://localhost:3000/#/nc/externalrest_weUO?type=roles&dbalias=&name=Team%20%26%20Auth%20
// [REST] Swagger URL: http://localhost:8080/nc/externalrest_weUO/db/swagger
// [GQL] http://localhost:8080/nc/externalgql_dgwx/v1/graphql
const projectName = url.split("/")[5].split("?")[0];
let swaggerURL = ``;
if ("rest" == apiType) {
swaggerURL = `http://localhost:8080/nc/${projectName}/db/swagger`;
} else {
swaggerURL = `http://localhost:8080/nc/${projectName}/v1/graphql`;
}
// exchange information between two tests using a file
// https://stackoverflow.com/questions/52050657/what-is-the-best-practice-of-pass-states-between-tests-in-cypress
//
cy.writeFile("shared.json", {
SWAGGER_URL: swaggerURL,
AUTH_TOKEN: authToken,
});
});
});
});
if ("rest" == apiType) {
describe(`Swagger page, base verification`, () => {
before(() => {
cy.fileHook();
});
// returns swagger button intended for
//
const getSwaggerButton = (tag, idx, desc) => {
return cy
.get(`#operations-tag-${tag}`)
.next()
.find(".opblock")
.eq(idx)
.find(`button:contains(${desc})`);
};
let Token;
// basic authentication tag verification
//
it("Swagger URL access & basic validation", () => {
// retrieve information stored in previous IT block
//
cy.readFile("shared.json").then((jsonPayload) => {
let URL = jsonPayload.SWAGGER_URL;
Token = jsonPayload.AUTH_TOKEN;
cy.visit(URL, {
baseUrl: null,
}).then(() => {
// wait to allow time for SWAGGER Library loading to finish
cy.log(Token);
// cy.snip("Swagger");
// validate; API order assumed
cy.get("#operations-tag-Authentication", {
timeout: 20000,
})
.should("exist")
.next()
.find(".opblock")
.should("has.length", 9);
getSwaggerButton("Authentication", 0, "User login").should("exist");
getSwaggerButton("Authentication", 1, "User signup").should(
"exist"
);
getSwaggerButton("Authentication", 2, "Password Forgot").should(
"exist"
);
getSwaggerButton("Authentication", 3, "Email validate link").should(
"exist"
);
getSwaggerButton(
"Authentication",
4,
"Validate password reset token"
).should("exist");
getSwaggerButton("Authentication", 5, "Password reset").should(
"exist"
);
getSwaggerButton("Authentication", 6, "User details").should(
"exist"
);
getSwaggerButton("Authentication", 7, "Update user details").should(
"exist"
);
getSwaggerButton("Authentication", 8, "Update user details").should(
"exist"
);
});
});
});
it("Authorize success: Valid token", () => {
// authorize button, feed token, click authorize
cy.get('[class="btn authorize unlocked"]').click();
cy.get("input").type(Token);
cy.get(".auth-btn-wrapper > .authorize").click();
// Response: "Authorized" should exist on DOM
cy.get(".auth-container").contains("Authorized").should("exist");
cy.get(".btn-done").click();
// Authorize button is LOCKED now
cy.get('[class="btn authorize locked"]').should("exist");
});
it("Execute Authentication (valid token case) > GET: User details API", () => {
// Auth> User details API
getSwaggerButton("Authentication", 6, "User details").click();
// "Try it out" button, followed by "Execute"
cy.get(".try-out > .btn").click();
cy.get(".execute-wrapper > .btn").click();
// check response: validate email credentials
cy.get(".highlight-code > .microlight")
.contains("email")
.should("exist");
cy.get(".highlight-code > .microlight")
.contains(roles.owner.credentials.username)
.should("exist");
// reset operations (clear, cancel, windback User details tab)
cy.get(".btn-clear").click();
cy.get(".try-out > .btn").click();
getSwaggerButton("Authentication", 6, "User details").click();
});
it("Logout post authorization", () => {
// authorize button, logout
cy.get('[class="btn authorize locked"]').click();
cy.get('.auth-btn-wrapper > button:contains("Logout")').click();
cy.get(".btn-done").click();
// Authorize button is UNLOCKED now
cy.get('[class="btn authorize unlocked"]').should("exist");
});
it("Execute Authentication (logout case) > GET: User details API", () => {
// Auth> User details API
getSwaggerButton("Authentication", 6, "User details").click();
// "Try it out" button, followed by "Execute"
cy.get(".try-out > .btn").click();
cy.get(".execute-wrapper > .btn").click();
// check response: email credentials shouldnt exist. should display 'guest:true'
cy.get(".highlight-code > .microlight")
.contains("guest")
.should("exist");
cy.get(".highlight-code > .microlight")
.contains("email")
.should("not.exist");
cy.get(".highlight-code > .microlight")
.contains(roles.owner.credentials.username)
.should("not.exist");
// reset operations (clear, cancel, windback User details tab)
cy.get(".btn-clear").click();
cy.get(".try-out > .btn").click();
getSwaggerButton("Authentication", 6, "User details").click();
});
it("Authorize failure: invalid token", () => {
// authorize button, feed *invalid* token, click authorize
cy.get('[class="btn authorize unlocked"]').click();
cy.get("input").type("xyz");
cy.get(".auth-btn-wrapper > .authorize").click();
// Response: "Authorized" should *not* exist on DOM
// TBD: cy.get('.auth-container').contains('Authorized').should('not.exist')
cy.get(".btn-done").click();
// Authorize button should be UNLOCKED now
// TBD: cy.get('[class="btn authorize unlocked"]').should('exist')
});
it("Execute Authentication (invalid token case) > GET: User details API", () => {
// Auth> User details API
getSwaggerButton("Authentication", 6, "User details").click();
// "Try it out" button, followed by "Execute"
cy.get(".try-out > .btn").click();
cy.get(".execute-wrapper > .btn").click();
// check response: email credentials shouldnt exist. should display 'guest:true'
cy.get(".highlight-code > .microlight")
.contains("guest")
.should("exist");
cy.get(".highlight-code > .microlight")
.contains("email")
.should("not.exist");
cy.get(".highlight-code > .microlight")
.contains(roles.owner.credentials.username)
.should("not.exist");
// reset operations (clear, cancel, windback User details tab)
cy.get(".btn-clear").click();
cy.get(".try-out > .btn").click();
getSwaggerButton("Authentication", 6, "User details").click();
});
// clean-up created file (shared.json)
// after(() => {
// cy.exec("del shared.json").then(()=> {
// cy.log("file cleaned up!")
// })
// })
});
}
};

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save