Browse Source

Merge branch 'develop' into feat/webhook-body-edit

pull/5530/head
Wing-Kam Wong 2 years ago
parent
commit
3a72c31ccb
  1. 4
      charts/nocodb/Chart.yaml
  2. 11
      charts/nocodb/values.yaml
  3. 6
      packages/nc-gui/components/cell/Currency.vue
  4. 6
      packages/nc-gui/components/cell/Decimal.vue
  5. 5
      packages/nc-gui/components/cell/Duration.vue
  6. 6
      packages/nc-gui/components/cell/Email.vue
  7. 6
      packages/nc-gui/components/cell/Float.vue
  8. 6
      packages/nc-gui/components/cell/Integer.vue
  9. 8
      packages/nc-gui/components/cell/Percent.vue
  10. 8
      packages/nc-gui/components/cell/Text.vue
  11. 6
      packages/nc-gui/components/cell/TextArea.vue
  12. 5
      packages/nc-gui/components/cell/Url.vue
  13. 19
      packages/nc-gui/components/smartsheet/Grid.vue
  14. 10
      packages/nc-gui/components/smartsheet/expanded-form/Comments.vue
  15. 9
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  16. 1
      packages/nc-gui/context/index.ts
  17. 28
      packages/nc-gui/lang/cs.json
  18. 54
      packages/nc-gui/lang/ru.json
  19. 606
      packages/nc-plugin/package-lock.json
  20. 12
      packages/nocodb-nest/package-lock.json
  21. 4
      packages/nocodb-nest/src/app.module.ts
  22. 14
      packages/nocodb-nest/src/db/BaseModelSqlv2.ts
  23. 4
      packages/nocodb-nest/src/meta/migrations/XcMigrationSourcev2.ts
  24. 34
      packages/nocodb-nest/src/meta/migrations/v2/nc_030_add_description_field.ts
  25. 6
      packages/nocodb-nest/src/services/public-datas.service.ts
  26. 12
      packages/nocodb/package-lock.json
  27. 6
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts
  28. 4
      packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts
  29. 34
      packages/nocodb/src/lib/migrations/v2/nc_030_add_description_field.ts
  30. 14
      tests/playwright/package-lock.json
  31. 12
      tests/playwright/pages/Account/AppStore.ts
  32. 8
      tests/playwright/pages/Account/ChangePassword.ts
  33. 11
      tests/playwright/pages/Account/Settings.ts
  34. 2
      tests/playwright/pages/Dashboard/Grid/Column/index.ts
  35. 5
      tests/playwright/pages/Dashboard/Grid/index.ts
  36. 2
      tests/playwright/pages/Dashboard/TreeView.ts
  37. 1
      tests/playwright/pages/Dashboard/ViewSidebar/index.ts
  38. 9
      tests/playwright/pages/Dashboard/WebhookForm/index.ts
  39. 20
      tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts
  40. 11
      tests/playwright/pages/Dashboard/index.ts
  41. 2
      tests/playwright/playwright.config.ts
  42. 3
      tests/playwright/quickTests/quickTests.spec.ts
  43. 2
      tests/playwright/scripts/docker-compose-playwright-pg.yml
  44. 23
      tests/playwright/tests/db/01-webhook.spec.ts
  45. 10
      tests/playwright/tests/db/accountLicense.spec.ts
  46. 8
      tests/playwright/tests/db/accountTokenManagement.spec.ts
  47. 15
      tests/playwright/tests/db/accountUserManagement.spec.ts
  48. 19
      tests/playwright/tests/db/accountUserSettings.spec.ts
  49. 23
      tests/playwright/tests/db/authChangePassword.spec.ts
  50. 15
      tests/playwright/tests/db/baseShare.spec.ts
  51. 55
      tests/playwright/tests/db/cellSelection.spec.ts
  52. 12
      tests/playwright/tests/db/columnAttachments.spec.ts
  53. 8
      tests/playwright/tests/db/columnBarcode.spec.ts
  54. 65
      tests/playwright/tests/db/columnCheckbox.spec.ts
  55. 6
      tests/playwright/tests/db/columnDateTime.spec.ts
  56. 6
      tests/playwright/tests/db/columnDuration.spec.ts
  57. 8
      tests/playwright/tests/db/columnFormula.spec.ts
  58. 8
      tests/playwright/tests/db/columnGeoData.spec.ts
  59. 6
      tests/playwright/tests/db/columnLinkToAnotherRecord.spec.ts
  60. 6
      tests/playwright/tests/db/columnLookupRollup.spec.ts
  61. 6
      tests/playwright/tests/db/columnMenuOperations.spec.ts
  62. 10
      tests/playwright/tests/db/columnMultiSelect.spec.ts
  63. 8
      tests/playwright/tests/db/columnQrCode.spec.ts
  64. 8
      tests/playwright/tests/db/columnRating.spec.ts
  65. 8
      tests/playwright/tests/db/columnRelationalExtendedTests.spec.ts
  66. 10
      tests/playwright/tests/db/columnSingleSelect.spec.ts
  67. 16
      tests/playwright/tests/db/erd.spec.ts
  68. 12
      tests/playwright/tests/db/expandedFormUrl.spec.ts
  69. 56
      tests/playwright/tests/db/filters.spec.ts
  70. 8
      tests/playwright/tests/db/findRowByScanner.spec.ts
  71. 10
      tests/playwright/tests/db/import.spec.ts
  72. 8
      tests/playwright/tests/db/keyboardShortcuts.spec.ts
  73. 10
      tests/playwright/tests/db/language.spec.ts
  74. 2
      tests/playwright/tests/db/megaTable.spec.ts
  75. 10
      tests/playwright/tests/db/metaSync.spec.ts
  76. 10
      tests/playwright/tests/db/mobileMode.spec.ts
  77. 6
      tests/playwright/tests/db/pagination.spec.ts
  78. 8
      tests/playwright/tests/db/projectOperations.spec.ts
  79. 15
      tests/playwright/tests/db/rolesCreate.spec.ts
  80. 10
      tests/playwright/tests/db/rolesPreview.spec.ts
  81. 6
      tests/playwright/tests/db/rolesSuperUser.spec.ts
  82. 6
      tests/playwright/tests/db/swagger.spec.ts
  83. 8
      tests/playwright/tests/db/tableColumnOperation.spec.ts
  84. 8
      tests/playwright/tests/db/tableOperations.spec.ts
  85. 8
      tests/playwright/tests/db/toolbarOperations.spec.ts
  86. 10
      tests/playwright/tests/db/undo-redo.spec.ts
  87. 18
      tests/playwright/tests/db/viewForm.spec.ts
  88. 8
      tests/playwright/tests/db/viewFormShareSurvey.spec.ts
  89. 8
      tests/playwright/tests/db/viewGridShare.spec.ts
  90. 10
      tests/playwright/tests/db/viewKanban.spec.ts
  91. 8
      tests/playwright/tests/db/viewMap.spec.ts
  92. 8
      tests/playwright/tests/db/viewMenu.spec.ts
  93. 8
      tests/playwright/tests/db/views.spec.ts
  94. 14
      tests/playwright/tests/utils/general.ts

4
charts/nocodb/Chart.yaml

@ -5,6 +5,10 @@ dependencies:
name: postgresql
repository: https://charts.bitnami.com/bitnami
version: ~11.6.6
- condition: mysql.enabled
name: mysql
repository: https://charts.bitnami.com/bitnami
version: ~9.4.6
description: A Helm chart for Kubernetes
maintainers: []
name: nocodb

11
charts/nocodb/values.yaml

@ -83,6 +83,7 @@ extraEnvs:
extraSecretEnvs:
NC_AUTH_JWT_SECRET: secretString
NC_DB: "mysql2://mysql:3306?u=nocodb&p=secretPass&d=nocodb"
storage:
size: 3Gi
@ -96,3 +97,13 @@ postgresql:
password: secretPass
persistence:
size: 8Gi
mysql:
enabled: false
auth:
database: nocodb
username: nocodb
password: secretPass
persistence:
enabled: false
size: 8Gi

6
packages/nc-gui/components/cell/Currency.vue

@ -1,6 +1,6 @@
<script setup lang="ts">
import type { VNodeRef } from '@vue/runtime-core'
import { ColumnInj, EditModeInj, computed, inject, parseProp, useVModel } from '#imports'
import { ColumnInj, EditModeInj, IsExpandedFormOpenInj, computed, inject, parseProp, useVModel } from '#imports'
interface Props {
modelValue: number | null | undefined
@ -52,7 +52,9 @@ const currency = computed(() => {
}
})
const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
const isExpandedFormOpen = inject(IsExpandedFormOpenInj)!
const focus: VNodeRef = (el) => !isExpandedFormOpen && (el as HTMLInputElement)?.focus()
const submitCurrency = () => {
if (lastSaved.value !== vModel.value) {

6
packages/nc-gui/components/cell/Decimal.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import type { VNodeRef } from '@vue/runtime-core'
import { EditModeInj, inject, useVModel } from '#imports'
import { EditModeInj, IsExpandedFormOpenInj, inject, useVModel } from '#imports'
interface Props {
// when we set a number, then it is number type
@ -36,7 +36,9 @@ const vModel = computed({
},
})
const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
const isExpandedFormOpen = inject(IsExpandedFormOpenInj)!
const focus: VNodeRef = (el) => !isExpandedFormOpen && (el as HTMLInputElement)?.focus()
</script>
<template>

5
packages/nc-gui/components/cell/Duration.vue

@ -3,6 +3,7 @@ import type { VNodeRef } from '@vue/runtime-core'
import {
ColumnInj,
EditModeInj,
IsExpandedFormOpenInj,
computed,
convertDurationToSeconds,
convertMS2Duration,
@ -73,7 +74,9 @@ const submitDuration = () => {
isEdited.value = false
}
const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
const isExpandedFormOpen = inject(IsExpandedFormOpenInj)!
const focus: VNodeRef = (el) => !isExpandedFormOpen && (el as HTMLInputElement)?.focus()
</script>
<template>

6
packages/nc-gui/components/cell/Email.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import type { VNodeRef } from '@vue/runtime-core'
import { EditModeInj, IsSurveyFormInj, computed, inject, useI18n, validateEmail } from '#imports'
import { EditModeInj, IsExpandedFormOpenInj, IsSurveyFormInj, computed, inject, useI18n, validateEmail } from '#imports'
interface Props {
modelValue: string | null | undefined
@ -35,7 +35,9 @@ const vModel = computed({
const validEmail = computed(() => vModel.value && validateEmail(vModel.value))
const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
const isExpandedFormOpen = inject(IsExpandedFormOpenInj)!
const focus: VNodeRef = (el) => !isExpandedFormOpen && (el as HTMLInputElement)?.focus()
watch(
() => editEnabled.value,

6
packages/nc-gui/components/cell/Float.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import type { VNodeRef } from '@vue/runtime-core'
import { EditModeInj, inject, useVModel } from '#imports'
import { EditModeInj, IsExpandedFormOpenInj, inject, useVModel } from '#imports'
interface Props {
// when we set a number, then it is number type
@ -36,7 +36,9 @@ const vModel = computed({
},
})
const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
const isExpandedFormOpen = inject(IsExpandedFormOpenInj)!
const focus: VNodeRef = (el) => !isExpandedFormOpen && (el as HTMLInputElement)?.focus()
</script>
<template>

6
packages/nc-gui/components/cell/Integer.vue

@ -1,6 +1,6 @@
<script setup lang="ts">
import type { VNodeRef } from '@vue/runtime-core'
import { EditModeInj, inject, useVModel } from '#imports'
import { EditModeInj, IsExpandedFormOpenInj, inject, useVModel } from '#imports'
interface Props {
// when we set a number, then it is number type
@ -36,7 +36,9 @@ const vModel = computed({
},
})
const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
const isExpandedFormOpen = inject(IsExpandedFormOpenInj)!
const focus: VNodeRef = (el) => !isExpandedFormOpen && (el as HTMLInputElement)?.focus()
function onKeyDown(evt: KeyboardEvent) {
return evt.key === '.' && evt.preventDefault()

8
packages/nc-gui/components/cell/Percent.vue

@ -1,6 +1,6 @@
<script setup lang="ts">
import type { VNodeRef } from '@vue/runtime-core'
import { EditModeInj, inject, useVModel } from '#imports'
import { EditModeInj, IsExpandedFormOpenInj, inject, useVModel } from '#imports'
interface Props {
modelValue?: number | string | null
@ -27,9 +27,9 @@ const vModel = computed({
},
})
const focus: VNodeRef = (el) => {
;(el as HTMLInputElement)?.focus()
}
const isExpandedFormOpen = inject(IsExpandedFormOpenInj)!
const focus: VNodeRef = (el) => !isExpandedFormOpen && (el as HTMLInputElement)?.focus()
</script>
<template>

8
packages/nc-gui/components/cell/Text.vue

@ -1,6 +1,6 @@
<script setup lang="ts">
import type { VNodeRef } from '@vue/runtime-core'
import { EditModeInj, ReadonlyInj, inject, ref, useVModel } from '#imports'
import { EditModeInj, IsExpandedFormOpenInj, ReadonlyInj, inject, ref, useVModel } from '#imports'
interface Props {
modelValue?: string | null
@ -23,9 +23,9 @@ const readonly = inject(ReadonlyInj, ref(false))
const vModel = useVModel(props, 'modelValue', emits)
const focus: VNodeRef = (el) => {
;(el as HTMLInputElement)?.focus()
}
const isExpandedFormOpen = inject(IsExpandedFormOpenInj)!
const focus: VNodeRef = (el) => !isExpandedFormOpen && (el as HTMLInputElement)?.focus()
</script>
<template>

6
packages/nc-gui/components/cell/TextArea.vue

@ -1,6 +1,6 @@
<script setup lang="ts">
import type { VNodeRef } from '@vue/runtime-core'
import { EditModeInj, RowHeightInj, inject, useVModel } from '#imports'
import { EditModeInj, IsExpandedFormOpenInj, RowHeightInj, inject, useVModel } from '#imports'
const props = defineProps<{
modelValue?: string | number
@ -19,7 +19,9 @@ const { showNull } = useGlobal()
const vModel = useVModel(props, 'modelValue', emits, { defaultValue: '' })
const focus: VNodeRef = (el) => (el as HTMLTextAreaElement)?.focus()
const isExpandedFormOpen = inject(IsExpandedFormOpenInj)!
const focus: VNodeRef = (el) => !isExpandedFormOpen && (el as HTMLTextAreaElement)?.focus()
</script>
<template>

5
packages/nc-gui/components/cell/Url.vue

@ -4,6 +4,7 @@ import {
CellUrlDisableOverlayInj,
ColumnInj,
EditModeInj,
IsExpandedFormOpenInj,
IsSurveyFormInj,
computed,
inject,
@ -62,7 +63,9 @@ const url = computed(() => {
const { cellUrlOptions } = useCellUrlConfig(url)
const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
const isExpandedFormOpen = inject(IsExpandedFormOpenInj)!
const focus: VNodeRef = (el) => !isExpandedFormOpen && (el as HTMLInputElement)?.focus()
watch(
() => editEnabled.value,

19
packages/nc-gui/components/smartsheet/Grid.vue

@ -752,6 +752,23 @@ const closeAddColumnDropdown = () => {
columnOrder.value = null
addColumnDropdown.value = false
}
const confirmDeleteRow = (row: number) => {
Modal.confirm({
title: `Do you want to delete this row?`,
wrapClassName: 'nc-modal-row-delete',
okText: 'Yes',
okType: 'danger',
cancelText: 'No',
onOk() {
try {
deleteRow(row)
} catch (e: any) {
message.error(e.message)
}
},
})
}
</script>
<template>
@ -973,7 +990,7 @@ const closeAddColumnDropdown = () => {
<template v-if="!isLocked && hasEditPermission" #overlay>
<a-menu class="shadow !rounded !py-0" @click="contextMenu = false">
<a-menu-item v-if="contextMenuTarget" @click="deleteRow(contextMenuTarget.row)">
<a-menu-item v-if="contextMenuTarget" @click="confirmDeleteRow(contextMenuTarget.row)">
<div v-e="['a:row:delete']" class="nc-project-menu-item">
<!-- Delete Row -->
{{ $t('activity.deleteRow') }}

10
packages/nc-gui/components/smartsheet/expanded-form/Comments.vue

@ -158,11 +158,17 @@ watch(
class="block caption my-2 nc-chip w-full min-h-20px p-2 rounded"
:style="{ backgroundColor: enumColor.light[2] }"
>
{{ log.description }}
<!--
retrieve the comment part from the audit description
`The following comment has been created: foo` -> `foo`
-->
{{ log.description.substring(log.description.indexOf(':') + 1) }}
</p>
</div>
<p v-else v-dompurify-html="log.details" class="caption my-3" style="word-break: break-all" />
<p v-else-if="log.details" v-dompurify-html="log.details" class="caption my-3" style="word-break: break-all" />
<p v-else>{{ log.description }}</p>
<p class="time text-right text-[10px] mb-0 mt-1 text-gray-500">
{{ timeAgo(log.created_at) }}

9
packages/nc-gui/components/smartsheet/expanded-form/index.vue

@ -5,6 +5,7 @@ import type { Ref } from 'vue'
import {
CellClickHookInj,
FieldsInj,
IsExpandedFormOpenInj,
IsFormInj,
IsKanbanInj,
MetaInj,
@ -180,10 +181,14 @@ if (isKanban.value) {
}
}
const cellWrapperEl = ref<HTMLElement>()
provide(IsExpandedFormOpenInj, isExpanded)
const cellWrapperEl = ref()
onMounted(() => {
setTimeout(() => (cellWrapperEl.value?.querySelector('input,select,textarea') as HTMLInputElement)?.focus())
setTimeout(() => {
cellWrapperEl.value?.$el?.querySelector('input,select,textarea')?.focus()
}, 300)
})
const addNewRow = () => {

1
packages/nc-gui/context/index.ts

@ -19,6 +19,7 @@ export const IsGridInj: InjectionKey<Ref<boolean>> = Symbol('is-grid-injection')
export const IsGalleryInj: InjectionKey<Ref<boolean>> = Symbol('is-gallery-injection')
export const IsKanbanInj: InjectionKey<Ref<boolean>> = Symbol('is-kanban-injection')
export const IsLockedInj: InjectionKey<Ref<boolean>> = Symbol('is-locked-injection')
export const IsExpandedFormOpenInj: InjectionKey<Ref<boolean>> = Symbol('is-expanded-form-open-injection')
export const CellValueInj: InjectionKey<Ref<any>> = Symbol('cell-value-injection')
export const ActiveViewInj: InjectionKey<Ref<ViewType>> = Symbol('active-view-injection')
export const ReadonlyInj: InjectionKey<Ref<boolean>> = Symbol('readonly-injection')

28
packages/nc-gui/lang/cs.json

@ -101,7 +101,7 @@
"form": "Formulář",
"kanban": "Kanban",
"calendar": "Kalendář",
"map": "Map"
"map": "Mapa"
},
"user": "Uživatel",
"users": "Uživatelé",
@ -260,9 +260,9 @@
"barcodeFormat": "Formát čárového kódu",
"qrCodeValueTooLong": "Příliš mnoho znaků pro QR kód",
"barcodeValueTooLong": "Příliš mnoho znaků pro čárový kód",
"currentLocation": "Current Location",
"lng": "Lng",
"lat": "Lat",
"currentLocation": "Současná poloha",
"lng": "Zem. délka",
"lat": "Zem. šířka",
"aggregateFunction": "Agregační funkce",
"dbCreateIfNotExists": "Databáze : vytvořit, pokud neexistuje",
"clientKey": "Klíč klienta",
@ -396,7 +396,7 @@
"saveAndExit": "Uložit a odejít",
"saveAndStay": "Uložit a zůstat",
"insertRow": "Vložit nový řádek",
"duplicateRow": "Duplicate Row",
"duplicateRow": "Duplikovat řádek",
"deleteRow": "Odstranit řádek",
"deleteSelectedRow": "Odstranit vybrané řádky",
"importExcel": "Importovat Excel",
@ -429,10 +429,10 @@
"openTab": "Otevřít novou kartu",
"iFrame": "Kopírování vložitelného kódu HTML",
"addWebhook": "Přidat nový webhook",
"enableWebhook": "Enable Webhook",
"testWebhook": "Test Webhook",
"copyWebhook": "Copy Webhook",
"deleteWebhook": "Delete Webhook",
"enableWebhook": "Povolit Webhook",
"testWebhook": "Otestovat Webhook",
"copyWebhook": "Kopírovat Webhook",
"deleteWebhook": "Smazat Webhook",
"newToken": "Přidat nový token",
"exportZip": "Exportovat zip archív",
"importZip": "Importovat zip archív",
@ -472,7 +472,7 @@
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field",
"openInGoogleMaps": "Google Maps",
"openInGoogleMaps": "Google mapy",
"openInOpenStreetMap": "OSM"
},
"toggleMobileMode": "Toggle Mobile Mode"
@ -634,7 +634,7 @@
"gallery": "Přidat zobrazení galerie",
"form": "Přidání zobrazení formuláře",
"kanban": "Přidání zobrazení Kanban",
"map": "Add Map View",
"map": "Přidat zobrazení mapy",
"calendar": "Přidat zobrazení kalendáře"
},
"tablesMetadataInSync": "Metadata tabulek jsou synchronizována",
@ -667,7 +667,7 @@
"deleteTableConfirmation": "Chcete odstranit tabulku",
"showM2mTables": "Zobrazit tabulky M2M",
"showM2mTablesDesc": "Many-to-many relation is supported via a junction table & is hidden by default. Enable this option to list all such tables along with existing tables.",
"showNullInCells": "Show NULL in Cells",
"showNullInCells": "Zobrazit NULL v buňkách",
"showNullInCellsDesc": "Display 'NULL' tag in cells holding NULL value. This helps differentiate against cells holding EMPTY string.",
"showNullAndEmptyInFilter": "Show NULL and EMPTY in Filter",
"showNullAndEmptyInFilterDesc": "Enable 'additional' filters to differentiate fields containing NULL & Empty Strings. Default support for Blank treats both NULL & Empty strings alike.",
@ -698,7 +698,7 @@
"allowedSpecialCharList": "Seznam povolených speciálních znaků"
},
"invalidURL": "Neplatná adresa URL",
"invalidEmail": "Invalid Email",
"invalidEmail": "Neplatný e-mail",
"internalError": "Došlo k nějaké interní chybě",
"templateGeneratorNotFound": "Generátor šablon nelze najít!",
"fileUploadFailed": "Nepodařilo se nahrát soubor",
@ -759,7 +759,7 @@
},
"success": {
"columnDuplicated": "Sloupec úspěšně duplikován",
"rowDuplicatedWithoutSavedYet": "Row duplicated (not saved)",
"rowDuplicatedWithoutSavedYet": "Řádek duplikován (neuložen)",
"updatedUIACL": "Úspěšná aktualizace uživatelského rozhraní ACL pro tabulky",
"pluginUninstalled": "Plugin byl úspěšně odinstalován",
"pluginSettingsSaved": "Nastavení zásuvného modulu bylo úspěšně uloženo",

54
packages/nc-gui/lang/ru.json

@ -39,7 +39,7 @@
"signIn": "ВОЙТИ",
"signOut": "Выход",
"required": "Обязательно",
"enableScanner": "Enable Scanner for filling",
"enableScanner": "Включить сканер для заполнения",
"preferred": "Рекомендовано",
"mandatory": "Обязательный",
"loading": "Загрузка ...",
@ -76,7 +76,7 @@
"hideField": "Скрыть поле",
"sortAsc": "По Возрастанию",
"sortDesc": "По убыванию",
"geoDataField": "GeoData Field"
"geoDataField": "Поле геоданных"
},
"objects": {
"project": "Проект",
@ -101,7 +101,7 @@
"form": "Форма",
"kanban": "Канбан",
"calendar": "Календарь",
"map": "Map"
"map": "Карта"
},
"user": "Пользователь",
"users": "Пользователи",
@ -139,7 +139,7 @@
"Currency": "Валюта",
"Percent": "Процент",
"Duration": "Интервал времени",
"GeoData": "GeoData",
"GeoData": "Геоданные",
"Rating": "Рейтинг",
"Formula": "Формула",
"Rollup": "Итоги (Rollup)",
@ -211,7 +211,7 @@
"codeSnippet": "Сниппет кода",
"keyboardShortcut": "Горячие клавиши",
"generateRandomName": "Сгенерировать случайное имя",
"findRowByScanningCode": "Find row by scanning a QR or Barcode"
"findRowByScanningCode": "Найти строку путем сканирования QR или штрих-кода"
},
"labels": {
"createdBy": "Автор",
@ -260,9 +260,9 @@
"barcodeFormat": "Формат штрих-кода",
"qrCodeValueTooLong": "Слишком много символов для QR-кода",
"barcodeValueTooLong": "Слишком много символов для штрихкода",
"currentLocation": "Current Location",
"lng": "Lng",
"lat": "Lat",
"currentLocation": "Текущее местоположение",
"lng": "Долг",
"lat": "Шир",
"aggregateFunction": "Агрегатная функция",
"dbCreateIfNotExists": "База данных: создать, если не существует",
"clientKey": "Ключ клиента",
@ -396,7 +396,7 @@
"saveAndExit": "Сохранить и выйти",
"saveAndStay": "Сохранить и остаться",
"insertRow": "Вставить новую строку",
"duplicateRow": "Duplicate Row",
"duplicateRow": "Копировать запись",
"deleteRow": "Удалить строку",
"deleteSelectedRow": "Удалить выбранные строки",
"importExcel": "Импорт из Excel",
@ -412,7 +412,7 @@
"changePwd": "Изменить пароль",
"createView": "Создать представление",
"shareView": "Поделиться представлением",
"findRowByCodeScan": "Find row by scan",
"findRowByCodeScan": "Найти строку путем сканирования",
"fillByCodeScan": "Fill by scan",
"listSharedView": "Общий список представлений",
"ListView": "Список представлений",
@ -429,10 +429,10 @@
"openTab": "Открыть новую вкладку",
"iFrame": "Копировать встраеваемый HTML-код",
"addWebhook": "Добавить новый Web Hook",
"enableWebhook": "Enable Webhook",
"testWebhook": "Test Webhook",
"copyWebhook": "Copy Webhook",
"deleteWebhook": "Delete Webhook",
"enableWebhook": "Включить Webhook",
"testWebhook": "Проверить Webhook",
"copyWebhook": "Копировать Webhook",
"deleteWebhook": "Удалить Webhook",
"newToken": "Добавить новый токен",
"exportZip": "Экспорт в zip",
"importZip": "Импорт из zip",
@ -472,10 +472,10 @@
"map": {
"mappedBy": "Mapped By",
"chooseMappingField": "Choose a Mapping Field",
"openInGoogleMaps": "Google Maps",
"openInGoogleMaps": "Google Карты",
"openInOpenStreetMap": "OSM"
},
"toggleMobileMode": "Toggle Mobile Mode"
"toggleMobileMode": "Переключить мобильный режим"
},
"tooltip": {
"saveChanges": "Сохранить изменения",
@ -542,15 +542,15 @@
"orgViewer": "Наблюдатель не может создавать новые проекты, но может получить доступ к любому проекту по приглашению."
},
"codeScanner": {
"loadingScanner": "Loading the scanner...",
"loadingScanner": "Загрузка сканера...",
"selectColumn": "Select a column (QR code or Barcode) that you want to use for finding a row by scanning.",
"moreThanOneRowFoundForCode": "More than one row found for this code. Currently only unique codes are supported.",
"noRowFoundForCode": "No row found for this code for the selected column"
},
"map": {
"overLimit": "You're over the limit.",
"closeLimit": "You're getting close to the limit.",
"limitNumber": "The limit of markers shown in a Map View is 1000 records."
"overLimit": "Вы превысили лимит.",
"closeLimit": "Вы приближаетесь к лимиту.",
"limitNumber": "Предельное количество маркеров, отображаемых в представлении карты, составляет 1000 записей."
},
"footerInfo": "Количество строк на странице",
"upload": "Выберите файл для загрузки",
@ -634,7 +634,7 @@
"gallery": "Добавить представление галерея",
"form": "Добавить представление форма",
"kanban": "Добавить представление Канбан",
"map": "Add Map View",
"map": "Добавить представление карты",
"calendar": "Добавить представление календарь"
},
"tablesMetadataInSync": "Таблицы метаданные синхронизируются",
@ -667,9 +667,9 @@
"deleteTableConfirmation": "Вы действительно хотите удалить эту таблицу",
"showM2mTables": "Показать таблицы M2M",
"showM2mTablesDesc": "Many-to-many relation is supported via a junction table & is hidden by default. Enable this option to list all such tables along with existing tables.",
"showNullInCells": "Show NULL in Cells",
"showNullInCellsDesc": "Display 'NULL' tag in cells holding NULL value. This helps differentiate against cells holding EMPTY string.",
"showNullAndEmptyInFilter": "Show NULL and EMPTY in Filter",
"showNullInCells": "Показывать NULL в ячейках",
"showNullInCellsDesc": "Отображайте тег 'NULL' в ячейках, содержащих значение NULL. Это помогает отличить ячейки, содержащие строку EMPTY.",
"showNullAndEmptyInFilter": "Показать NULL и EMPTY в фильтре",
"showNullAndEmptyInFilterDesc": "Enable 'additional' filters to differentiate fields containing NULL & Empty Strings. Default support for Blank treats both NULL & Empty strings alike.",
"deleteKanbanStackConfirmation": "Удаление этого стека также удалит опцию выбора `{stackToBeDeleted}` из `{groupingField}`. Записи переместятся в стек без категории.",
"computedFieldEditWarning": "Вычисляемое поле: содержимое доступно только для чтения. Используйте меню редактирования столбцов для изменения конфигурации",
@ -698,7 +698,7 @@
"allowedSpecialCharList": "Список разрешенных специальных символов"
},
"invalidURL": "Неверный URL",
"invalidEmail": "Invalid Email",
"invalidEmail": "Неверный адрес электронной почты",
"internalError": "Произошла какая-то внутренняя ошибка",
"templateGeneratorNotFound": "Генератор шаблонов не найден!",
"fileUploadFailed": "Не удалось загрузить файл",
@ -759,7 +759,7 @@
},
"success": {
"columnDuplicated": "Столбец успешно скопирован",
"rowDuplicatedWithoutSavedYet": "Row duplicated (not saved)",
"rowDuplicatedWithoutSavedYet": "Строка продублирована (не сохранена)",
"updatedUIACL": "Успешно обновлен пользовательский интерфейс ACL для таблиц",
"pluginUninstalled": "Плагин успешно удален",
"pluginSettingsSaved": "Настройки плагина сохранены",
@ -779,7 +779,7 @@
"userDeletedFromProject": "Успешное удаление пользователя из проекта",
"inviteEmailSent": "Приглашение по электронной почте отправлено успешно",
"inviteURLCopied": "URL-адрес приглашения скопирован в буфер обмена",
"commentCopied": "Comment copied to clipboard",
"commentCopied": "Комментарий скопирован в буфер обмена",
"passwordResetURLCopied": "URL-адрес сброса пароля скопирован в буфер обмена",
"shareableURLCopied": "Копирование URL базы с возможностью совместного использования в буфер обмена!",
"embeddableHTMLCodeCopied": "Скопированный встраиваемый HTML-код!",

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

@ -414,62 +414,184 @@
"to-fast-properties": "^2.0.0"
}
},
"node_modules/@commitlint/config-validator": {
"version": "17.4.4",
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.4.4.tgz",
"integrity": "sha512-bi0+TstqMiqoBAQDvdEP4AFh0GaKyLFlPPEObgI29utoKEYoPQTvF0EYqIwYYLEoJYhj5GfMIhPHJkTJhagfeg==",
"dev": true,
"optional": true,
"dependencies": {
"@commitlint/types": "^17.4.4",
"ajv": "^8.11.0"
},
"engines": {
"node": ">=v14"
}
},
"node_modules/@commitlint/config-validator/node_modules/ajv": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dev": true,
"optional": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true,
"optional": true
},
"node_modules/@commitlint/execute-rule": {
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-12.1.1.tgz",
"integrity": "sha512-6mplMGvLCKF5LieL7BRhydpg32tm6LICnWQADrWU4S5g9PKi2utNvhiaiuNPoHUXr29RdbNaGNcyyPv8DSjJsQ==",
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz",
"integrity": "sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==",
"dev": true,
"optional": true,
"engines": {
"node": ">=v10"
"node": ">=v14"
}
},
"node_modules/@commitlint/load": {
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-12.1.1.tgz",
"integrity": "sha512-qOQtgNdJRULUQWP9jkpTwhj7aEtnqUtqeUpbQ9rjS+GIUST65HZbteNUX4S0mAEGPWqy2aK5xGd73cUfFSvuuw==",
"version": "17.5.0",
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.5.0.tgz",
"integrity": "sha512-l+4W8Sx4CD5rYFsrhHH8HP01/8jEP7kKf33Xlx2Uk2out/UKoKPYMOIRcDH5ppT8UXLMV+x6Wm5osdRKKgaD1Q==",
"dev": true,
"optional": true,
"dependencies": {
"@commitlint/execute-rule": "^12.1.1",
"@commitlint/resolve-extends": "^12.1.1",
"@commitlint/types": "^12.1.1",
"chalk": "^4.0.0",
"cosmiconfig": "^7.0.0",
"lodash": "^4.17.19",
"resolve-from": "^5.0.0"
"@commitlint/config-validator": "^17.4.4",
"@commitlint/execute-rule": "^17.4.0",
"@commitlint/resolve-extends": "^17.4.4",
"@commitlint/types": "^17.4.4",
"@types/node": "*",
"chalk": "^4.1.0",
"cosmiconfig": "^8.0.0",
"cosmiconfig-typescript-loader": "^4.0.0",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2",
"lodash.uniq": "^4.5.0",
"resolve-from": "^5.0.0",
"ts-node": "^10.8.1",
"typescript": "^4.6.4 || ^5.0.0"
},
"engines": {
"node": ">=v10"
"node": ">=v14"
}
},
"node_modules/@commitlint/load/node_modules/cosmiconfig-typescript-loader": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz",
"integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==",
"dev": true,
"optional": true,
"engines": {
"node": ">=12",
"npm": ">=6"
},
"peerDependencies": {
"@types/node": "*",
"cosmiconfig": ">=7",
"ts-node": ">=10",
"typescript": ">=3"
}
},
"node_modules/@commitlint/load/node_modules/ts-node": {
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
"dev": true,
"optional": true,
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/@commitlint/load/node_modules/typescript": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
"integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
"dev": true,
"optional": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=12.20"
}
},
"node_modules/@commitlint/resolve-extends": {
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-12.1.1.tgz",
"integrity": "sha512-/DXRt0S0U3o9lq5cc8OL1Lkx0IjW0HcDWjUkUXshAajBIKBYSJB8x/loNCi1krNEJ8SwLXUEFt5OLxNO6wE9yQ==",
"version": "17.4.4",
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.4.4.tgz",
"integrity": "sha512-znXr1S0Rr8adInptHw0JeLgumS11lWbk5xAWFVno+HUFVN45875kUtqjrI6AppmD3JI+4s0uZlqqlkepjJd99A==",
"dev": true,
"optional": true,
"dependencies": {
"@commitlint/config-validator": "^17.4.4",
"@commitlint/types": "^17.4.4",
"import-fresh": "^3.0.0",
"lodash": "^4.17.19",
"lodash.mergewith": "^4.6.2",
"resolve-from": "^5.0.0",
"resolve-global": "^1.0.0"
},
"engines": {
"node": ">=v10"
"node": ">=v14"
}
},
"node_modules/@commitlint/types": {
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-12.1.1.tgz",
"integrity": "sha512-+qGH+s2Lo6qwacV2X3/ZypZwaAI84ift+1HBjXdXtI/q0F5NtmXucV3lcQOTviMTNiJhq4qWON2fjci2NItASw==",
"version": "17.4.4",
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.4.4.tgz",
"integrity": "sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ==",
"dev": true,
"optional": true,
"dependencies": {
"chalk": "^4.0.0"
"chalk": "^4.1.0"
},
"engines": {
"node": ">=v10"
"node": ">=v14"
}
},
"node_modules/@cspell/dict-aws": {
@ -670,6 +792,19 @@
"integrity": "sha512-CXCuXcrgAc56P3kL9I6gW6bZwTs6t3duyAtHerHg5YAYbPs6/4nXgniQgLgu8kjFHFy07XrqaaBdLU9V2DmMtQ==",
"dev": true
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dev": true,
"optional": true,
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@eslint/eslintrc": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz",
@ -774,6 +909,34 @@
"node": ">=8"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
"dev": true,
"optional": true,
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true,
"optional": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dev": true,
"optional": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
@ -824,6 +987,34 @@
"node": ">= 6"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
"dev": true,
"optional": true
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true,
"optional": true
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true,
"optional": true
},
"node_modules/@tsconfig/node16": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
"dev": true,
"optional": true
},
"node_modules/@types/json-schema": {
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
@ -842,19 +1033,19 @@
"integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==",
"dev": true
},
"node_modules/@types/node": {
"version": "18.16.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.0.tgz",
"integrity": "sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==",
"dev": true,
"optional": true
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true,
"optional": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.21.0.tgz",
@ -3060,20 +3251,42 @@
"dev": true
},
"node_modules/cosmiconfig": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz",
"integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==",
"version": "8.1.3",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz",
"integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==",
"dev": true,
"optional": true,
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
"parse-json": "^5.0.0",
"path-type": "^4.0.0",
"yaml": "^1.10.0"
"path-type": "^4.0.0"
},
"engines": {
"node": ">=10"
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
}
},
"node_modules/cosmiconfig/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"optional": true
},
"node_modules/cosmiconfig/node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"optional": true,
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/cosmiconfig/node_modules/parse-json": {
@ -6577,18 +6790,46 @@
"integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=",
"dev": true
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"dev": true,
"optional": true
},
"node_modules/lodash.map": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
"integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=",
"dev": true
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true,
"optional": true
},
"node_modules/lodash.mergewith": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
"dev": true,
"optional": true
},
"node_modules/lodash.truncate": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
"integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=",
"dev": true
},
"node_modules/lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
"integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
"dev": true,
"optional": true
},
"node_modules/log-symbols": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
@ -10166,6 +10407,13 @@
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true,
"optional": true
},
"node_modules/validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@ -10348,16 +10596,6 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"dev": true,
"optional": true,
"engines": {
"node": ">= 6"
}
},
"node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
@ -10742,50 +10980,131 @@
"to-fast-properties": "^2.0.0"
}
},
"@commitlint/config-validator": {
"version": "17.4.4",
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.4.4.tgz",
"integrity": "sha512-bi0+TstqMiqoBAQDvdEP4AFh0GaKyLFlPPEObgI29utoKEYoPQTvF0EYqIwYYLEoJYhj5GfMIhPHJkTJhagfeg==",
"dev": true,
"optional": true,
"requires": {
"@commitlint/types": "^17.4.4",
"ajv": "^8.11.0"
},
"dependencies": {
"ajv": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dev": true,
"optional": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
},
"json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true,
"optional": true
}
}
},
"@commitlint/execute-rule": {
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-12.1.1.tgz",
"integrity": "sha512-6mplMGvLCKF5LieL7BRhydpg32tm6LICnWQADrWU4S5g9PKi2utNvhiaiuNPoHUXr29RdbNaGNcyyPv8DSjJsQ==",
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz",
"integrity": "sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==",
"dev": true,
"optional": true
},
"@commitlint/load": {
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-12.1.1.tgz",
"integrity": "sha512-qOQtgNdJRULUQWP9jkpTwhj7aEtnqUtqeUpbQ9rjS+GIUST65HZbteNUX4S0mAEGPWqy2aK5xGd73cUfFSvuuw==",
"version": "17.5.0",
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.5.0.tgz",
"integrity": "sha512-l+4W8Sx4CD5rYFsrhHH8HP01/8jEP7kKf33Xlx2Uk2out/UKoKPYMOIRcDH5ppT8UXLMV+x6Wm5osdRKKgaD1Q==",
"dev": true,
"optional": true,
"requires": {
"@commitlint/execute-rule": "^12.1.1",
"@commitlint/resolve-extends": "^12.1.1",
"@commitlint/types": "^12.1.1",
"chalk": "^4.0.0",
"cosmiconfig": "^7.0.0",
"lodash": "^4.17.19",
"resolve-from": "^5.0.0"
"@commitlint/config-validator": "^17.4.4",
"@commitlint/execute-rule": "^17.4.0",
"@commitlint/resolve-extends": "^17.4.4",
"@commitlint/types": "^17.4.4",
"@types/node": "*",
"chalk": "^4.1.0",
"cosmiconfig": "^8.0.0",
"cosmiconfig-typescript-loader": "^4.0.0",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2",
"lodash.uniq": "^4.5.0",
"resolve-from": "^5.0.0",
"ts-node": "^10.8.1",
"typescript": "^4.6.4 || ^5.0.0"
},
"dependencies": {
"cosmiconfig-typescript-loader": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz",
"integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==",
"dev": true,
"optional": true,
"requires": {}
},
"ts-node": {
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
"dev": true,
"optional": true,
"requires": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
}
},
"typescript": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
"integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
"dev": true,
"optional": true
}
}
},
"@commitlint/resolve-extends": {
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-12.1.1.tgz",
"integrity": "sha512-/DXRt0S0U3o9lq5cc8OL1Lkx0IjW0HcDWjUkUXshAajBIKBYSJB8x/loNCi1krNEJ8SwLXUEFt5OLxNO6wE9yQ==",
"version": "17.4.4",
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.4.4.tgz",
"integrity": "sha512-znXr1S0Rr8adInptHw0JeLgumS11lWbk5xAWFVno+HUFVN45875kUtqjrI6AppmD3JI+4s0uZlqqlkepjJd99A==",
"dev": true,
"optional": true,
"requires": {
"@commitlint/config-validator": "^17.4.4",
"@commitlint/types": "^17.4.4",
"import-fresh": "^3.0.0",
"lodash": "^4.17.19",
"lodash.mergewith": "^4.6.2",
"resolve-from": "^5.0.0",
"resolve-global": "^1.0.0"
}
},
"@commitlint/types": {
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-12.1.1.tgz",
"integrity": "sha512-+qGH+s2Lo6qwacV2X3/ZypZwaAI84ift+1HBjXdXtI/q0F5NtmXucV3lcQOTviMTNiJhq4qWON2fjci2NItASw==",
"version": "17.4.4",
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.4.4.tgz",
"integrity": "sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.0.0"
"chalk": "^4.1.0"
}
},
"@cspell/dict-aws": {
@ -10986,6 +11305,16 @@
"integrity": "sha512-CXCuXcrgAc56P3kL9I6gW6bZwTs6t3duyAtHerHg5YAYbPs6/4nXgniQgLgu8kjFHFy07XrqaaBdLU9V2DmMtQ==",
"dev": true
},
"@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dev": true,
"optional": true,
"requires": {
"@jridgewell/trace-mapping": "0.3.9"
}
},
"@eslint/eslintrc": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz",
@ -11062,6 +11391,31 @@
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
"dev": true
},
"@jridgewell/resolve-uri": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
"dev": true,
"optional": true
},
"@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true,
"optional": true
},
"@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dev": true,
"optional": true,
"requires": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"@nodelib/fs.scandir": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
@ -11100,6 +11454,34 @@
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
"dev": true
},
"@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
"dev": true,
"optional": true
},
"@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true,
"optional": true
},
"@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true,
"optional": true
},
"@tsconfig/node16": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
"dev": true,
"optional": true
},
"@types/json-schema": {
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
@ -11118,19 +11500,19 @@
"integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==",
"dev": true
},
"@types/node": {
"version": "18.16.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.0.tgz",
"integrity": "sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==",
"dev": true,
"optional": true
},
"@types/normalize-package-data": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true,
"optional": true
},
"@typescript-eslint/eslint-plugin": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.21.0.tgz",
@ -12730,19 +13112,35 @@
"dev": true
},
"cosmiconfig": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz",
"integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==",
"version": "8.1.3",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz",
"integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==",
"dev": true,
"optional": true,
"requires": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
"parse-json": "^5.0.0",
"path-type": "^4.0.0",
"yaml": "^1.10.0"
"path-type": "^4.0.0"
},
"dependencies": {
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"optional": true
},
"js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"optional": true,
"requires": {
"argparse": "^2.0.1"
}
},
"parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@ -15378,18 +15776,46 @@
"integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=",
"dev": true
},
"lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"dev": true,
"optional": true
},
"lodash.map": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
"integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=",
"dev": true
},
"lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true,
"optional": true
},
"lodash.mergewith": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
"dev": true,
"optional": true
},
"lodash.truncate": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
"integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=",
"dev": true
},
"lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
"integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
"dev": true,
"optional": true
},
"log-symbols": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
@ -17947,6 +18373,13 @@
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true
},
"v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true,
"optional": true
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@ -18098,13 +18531,6 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"dev": true,
"optional": true
},
"yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",

12
packages/nocodb-nest/package-lock.json generated

@ -17464,9 +17464,9 @@
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
},
"node_modules/vm2": {
"version": "3.9.16",
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.16.tgz",
"integrity": "sha512-3T9LscojNTxdOyG+e8gFeyBXkMlOBYDoF6dqZbj+MPVHi9x10UfiTAJIobuchRCp3QvC+inybTbMJIUrLsig0w==",
"version": "3.9.17",
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.17.tgz",
"integrity": "sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==",
"dependencies": {
"acorn": "^8.7.0",
"acorn-walk": "^8.2.0"
@ -31578,9 +31578,9 @@
}
},
"vm2": {
"version": "3.9.16",
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.16.tgz",
"integrity": "sha512-3T9LscojNTxdOyG+e8gFeyBXkMlOBYDoF6dqZbj+MPVHi9x10UfiTAJIobuchRCp3QvC+inybTbMJIUrLsig0w==",
"version": "3.9.17",
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.17.tgz",
"integrity": "sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==",
"requires": {
"acorn": "^8.7.0",
"acorn-walk": "^8.2.0"

4
packages/nocodb-nest/src/app.module.ts

@ -16,6 +16,7 @@ import { GlobalModule } from './modules/global/global.module';
import { LocalStrategy } from './strategies/local.strategy';
import { AuthTokenStrategy } from './strategies/authtoken.strategy/authtoken.strategy';
import { BaseViewStrategy } from './strategies/base-view.strategy/base-view.strategy';
import NcConfigFactory from './utils/NcConfigFactory'
import NcUpgrader from './version-upgrader/NcUpgrader';
import { MetasModule } from './modules/metas/metas.module';
import NocoCache from './cache/NocoCache';
@ -68,6 +69,9 @@ export class AppModule implements OnApplicationBootstrap {
await NocoCache.init();
await this.connection.init();
await NcConfigFactory.metaDbCreateIfNotExist(this.connection.config);
await this.metaService.init();
// todo: remove

14
packages/nocodb-nest/src/db/BaseModelSqlv2.ts

@ -15,10 +15,6 @@ import ejs from 'ejs';
import Validator from 'validator';
import { customAlphabet } from 'nanoid';
import DOMPurify from 'isomorphic-dompurify';
const GROUP_COL = '__nc_group_id';
const nanoidv2 = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 14);
import { v4 as uuidv4 } from 'uuid';
import { NcError } from '../helpers/catchError';
import getAst from '../helpers/getAst';
@ -67,6 +63,10 @@ import type {
import type { Knex } from 'knex';
import type { SortType } from 'nocodb-sdk';
const GROUP_COL = '__nc_group_id';
const nanoidv2 = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 14);
export async function getViewAndModelByAliasOrId(param: {
projectName: string;
tableName: string;
@ -2434,6 +2434,7 @@ class BaseModelSqlv2 {
): Promise<void> {
const id = this._extractPksValues(newData);
let desc = `Record with ID ${id} has been updated in Table ${this.model.title}.`;
let details = '';
if (updateObj) {
updateObj = await this.model.mapColumnToAlias(updateObj);
for (const k of Object.keys(updateObj)) {
@ -2447,6 +2448,9 @@ class BaseModelSqlv2 {
: newData[k];
desc += `\n`;
desc += `Column "${k}" got changed from "${prevValue}" to "${newValue}"`;
details += DOMPurify.sanitize(`<span class="">${k}</span>
: <span class="text-decoration-line-through red px-2 lighten-4 black--text">${prevValue}</span>
<span class="black--text green lighten-4 px-2">${newValue}</span>`);
}
}
await Audit.insert({
@ -2455,7 +2459,7 @@ class BaseModelSqlv2 {
op_type: AuditOperationTypes.DATA,
op_sub_type: AuditOperationSubTypes.UPDATE,
description: DOMPurify.sanitize(desc),
// details: JSON.stringify(data),
details,
ip: req?.clientIp,
user: req?.user?.email,
});

4
packages/nocodb-nest/src/meta/migrations/XcMigrationSourcev2.ts

@ -17,6 +17,7 @@ import * as nc_026_map_view from './v2/nc_026_map_view';
import * as nc_027_add_comparison_sub_op from './v2/nc_027_add_comparison_sub_op';
import * as nc_028_add_enable_scanner_in_form_columns_meta_table from './v2/nc_028_add_enable_scanner_in_form_columns_meta_table';
import * as nc_029_webhook from './v2/nc_029_webhook';
import * as nc_030_add_description_field from './v2/nc_030_add_description_field';
// Create a custom migration source class
export default class XcMigrationSourcev2 {
@ -45,6 +46,7 @@ export default class XcMigrationSourcev2 {
'nc_027_add_comparison_sub_op',
'nc_028_add_enable_scanner_in_form_columns_meta_table',
'nc_029_webhook',
'nc_030_add_description_field',
]);
}
@ -92,6 +94,8 @@ export default class XcMigrationSourcev2 {
return nc_028_add_enable_scanner_in_form_columns_meta_table;
case 'nc_029_webhook':
return nc_029_webhook;
case 'nc_030_add_description_field':
return nc_030_add_description_field;
}
}
}

34
packages/nocodb-nest/src/meta/migrations/v2/nc_030_add_description_field.ts

@ -0,0 +1,34 @@
import type { Knex } from 'knex';
import { MetaTable } from '../../meta.service'
const up = async (knex: Knex) => {
await knex.schema.alterTable(MetaTable.BASES, (table) => {
table.string('description', 255);
});
await knex.schema.alterTable(MetaTable.MODELS, (table) => {
table.string('description', 255);
});
await knex.schema.alterTable(MetaTable.VIEWS, (table) => {
table.string('description', 255);
});
await knex.schema.alterTable(MetaTable.COLUMNS, (table) => {
table.string('description', 255);
});
};
const down = async (knex) => {
await knex.schema.alterTable(MetaTable.BASES, (table) => {
table.dropColumn('description');
});
await knex.schema.alterTable(MetaTable.MODELS, (table) => {
table.dropColumn('description');
});
await knex.schema.alterTable(MetaTable.VIEWS, (table) => {
table.dropColumn('description');
});
await knex.schema.alterTable(MetaTable.COLUMNS, (table) => {
table.dropColumn('description');
});
};
export { up, down };

6
packages/nocodb-nest/src/services/public-datas.service.ts

@ -360,7 +360,8 @@ export class PublicDatasService {
const view = await View.getByUUID(param.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (view.type !== ViewTypes.GRID) NcError.notFound('Not found');
if (view.type !== ViewTypes.GRID && view.type !== ViewTypes.KANBAN)
NcError.notFound('Not found');
if (view.password && view.password !== param.password) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
@ -425,7 +426,8 @@ export class PublicDatasService {
const view = await View.getByUUID(param.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (view.type !== ViewTypes.GRID) NcError.notFound('Not found');
if (view.type !== ViewTypes.GRID && view.type !== ViewTypes.KANBAN)
NcError.notFound('Not found');
if (view.password && view.password !== param.password) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);

12
packages/nocodb/package-lock.json generated

@ -17364,9 +17364,9 @@
"dev": true
},
"node_modules/vm2": {
"version": "3.9.16",
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.16.tgz",
"integrity": "sha512-3T9LscojNTxdOyG+e8gFeyBXkMlOBYDoF6dqZbj+MPVHi9x10UfiTAJIobuchRCp3QvC+inybTbMJIUrLsig0w==",
"version": "3.9.17",
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.17.tgz",
"integrity": "sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==",
"dependencies": {
"acorn": "^8.7.0",
"acorn-walk": "^8.2.0"
@ -32786,9 +32786,9 @@
"dev": true
},
"vm2": {
"version": "3.9.16",
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.16.tgz",
"integrity": "sha512-3T9LscojNTxdOyG+e8gFeyBXkMlOBYDoF6dqZbj+MPVHi9x10UfiTAJIobuchRCp3QvC+inybTbMJIUrLsig0w==",
"version": "3.9.17",
"resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.17.tgz",
"integrity": "sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==",
"requires": {
"acorn": "^8.7.0",
"acorn-walk": "^8.2.0"

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

@ -2402,6 +2402,7 @@ class BaseModelSqlv2 {
): Promise<void> {
const id = this._extractPksValues(newData);
let desc = `Record with ID ${id} has been updated in Table ${this.model.title}.`;
let details = '';
if (updateObj) {
updateObj = await this.model.mapColumnToAlias(updateObj);
for (const k of Object.keys(updateObj)) {
@ -2415,6 +2416,9 @@ class BaseModelSqlv2 {
: newData[k];
desc += `\n`;
desc += `Column "${k}" got changed from "${prevValue}" to "${newValue}"`;
details += DOMPurify.sanitize(`<span class="">${k}</span>
: <span class="text-decoration-line-through red px-2 lighten-4 black--text">${prevValue}</span>
<span class="black--text green lighten-4 px-2">${newValue}</span>`);
}
}
await Audit.insert({
@ -2423,7 +2427,7 @@ class BaseModelSqlv2 {
op_type: AuditOperationTypes.DATA,
op_sub_type: AuditOperationSubTypes.UPDATE,
description: DOMPurify.sanitize(desc),
// details: JSON.stringify(data),
details,
ip: req?.clientIp,
user: req?.user?.email,
});

4
packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts

@ -17,6 +17,7 @@ import * as nc_026_map_view from './v2/nc_026_map_view';
import * as nc_027_add_comparison_sub_op from './v2/nc_027_add_comparison_sub_op';
import * as nc_028_add_enable_scanner_in_form_columns_meta_table from './v2/nc_028_add_enable_scanner_in_form_columns_meta_table';
import * as nc_029_webhook from './v2/nc_029_webhook';
import * as nc_030_add_description_field from './v2/nc_030_add_description_field';
// Create a custom migration source class
export default class XcMigrationSourcev2 {
@ -45,6 +46,7 @@ export default class XcMigrationSourcev2 {
'nc_027_add_comparison_sub_op',
'nc_028_add_enable_scanner_in_form_columns_meta_table',
'nc_029_webhook',
'nc_030_add_description_field',
]);
}
@ -92,6 +94,8 @@ export default class XcMigrationSourcev2 {
return nc_028_add_enable_scanner_in_form_columns_meta_table;
case 'nc_029_webhook':
return nc_029_webhook;
case 'nc_030_add_description_field':
return nc_030_add_description_field;
}
}
}

34
packages/nocodb/src/lib/migrations/v2/nc_030_add_description_field.ts

@ -0,0 +1,34 @@
import { MetaTable } from '../../utils/globals';
import type { Knex } from 'knex';
const up = async (knex: Knex) => {
await knex.schema.alterTable(MetaTable.BASES, (table) => {
table.string('description', 255);
});
await knex.schema.alterTable(MetaTable.MODELS, (table) => {
table.string('description', 255);
});
await knex.schema.alterTable(MetaTable.VIEWS, (table) => {
table.string('description', 255);
});
await knex.schema.alterTable(MetaTable.COLUMNS, (table) => {
table.string('description', 255);
});
};
const down = async (knex) => {
await knex.schema.alterTable(MetaTable.BASES, (table) => {
table.dropColumn('description');
});
await knex.schema.alterTable(MetaTable.MODELS, (table) => {
table.dropColumn('description');
});
await knex.schema.alterTable(MetaTable.VIEWS, (table) => {
table.dropColumn('description');
});
await knex.schema.alterTable(MetaTable.COLUMNS, (table) => {
table.dropColumn('description');
});
};
export { up, down };

14
tests/playwright/package-lock.json generated

@ -37,7 +37,7 @@
}
},
"../../packages/nocodb-sdk": {
"version": "0.105.3",
"version": "0.106.1",
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",
@ -4991,9 +4991,9 @@
"dev": true
},
"node_modules/yaml": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz",
"integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==",
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
"dev": true,
"engines": {
"node": ">= 14"
@ -8651,9 +8651,9 @@
"dev": true
},
"yaml": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz",
"integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==",
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
"dev": true
}
}

12
tests/playwright/pages/Account/AppStore.ts

@ -11,13 +11,11 @@ export class AccountAppStorePage extends BasePage {
}
async goto() {
await this.rootPage.goto('/#/account/apps', { waitUntil: 'networkidle' });
}
async waitUntilContentLoads() {
return this.rootPage.waitForResponse(
resp => resp.url().includes('api/v1/db/meta/plugins') && resp.status() === 200
);
await this.waitForResponse({
uiAction: () => this.rootPage.goto('/#/account/apps', { waitUntil: 'networkidle' }),
httpMethodsToMatch: ['GET'],
requestUrlPathToMatch: 'api/v1/db/meta/plugins',
});
}
get() {

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

@ -25,6 +25,14 @@ export class ChangePasswordPage extends BasePage {
const newPassword = this.get().locator('input[data-testid="nc-user-settings-form__new-password"]');
const confirmPassword = this.get().locator('input[data-testid="nc-user-settings-form__new-password-repeat"]');
await currentPassword.waitFor({ state: 'visible' });
await newPassword.waitFor({ state: 'visible' });
await confirmPassword.waitFor({ state: 'visible' });
// in-spite of the waitFor above, the input fields are not always ready to be filled
// this is a workaround
await this.rootPage.waitForTimeout(500);
await currentPassword.fill(oldPass);
await newPassword.fill(newPass);
await confirmPassword.fill(repeatPass);

11
tests/playwright/pages/Account/Settings.ts

@ -11,11 +11,12 @@ export class AccountSettingsPage extends BasePage {
}
async goto() {
await this.rootPage.goto('/#/account/users/settings', { waitUntil: 'networkidle' });
}
async waitUntilContentLoads() {
return this.rootPage.waitForResponse(resp => resp.url().includes('api/v1/app-settings') && resp.status() === 200);
// await this.rootPage.goto('/#/account/users/settings', { waitUntil: 'networkidle' });
await this.waitForResponse({
uiAction: () => this.rootPage.goto('/#/account/users/settings', { waitUntil: 'networkidle' }),
httpMethodsToMatch: ['GET'],
requestUrlPathToMatch: `api/v1/app-settings`,
});
}
get() {

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

@ -252,7 +252,7 @@ export class ColumnPageObject extends BasePage {
async delete({ title }: { title: string }) {
await this.getColumnHeader(title).locator('div.ant-dropdown-trigger').locator('.nc-ui-dt-dropdown').click();
// await this.rootPage.locator('li[role="menuitem"]:has-text("Delete")').waitFor();
await this.rootPage.locator('li[role="menuitem"]:has-text("Delete")').click();
await this.rootPage.locator('li[role="menuitem"]:has-text("Delete"):visible').click();
await this.rootPage.locator('button:has-text("Delete")').click();

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

@ -72,10 +72,12 @@ export class GridPage extends BasePage {
networkValidation?: boolean;
} = {}) {
const rowValue = value ?? `Row ${index}`;
// wait for render to complete before count
if (index !== 0) await this.get().locator('.nc-grid-row').nth(0).waitFor({ state: 'attached' });
const rowCount = await this.get().locator('.nc-grid-row').count();
await this.get().locator('.nc-grid-add-new-cell').click();
await expect(this.get().locator('.nc-grid-row')).toHaveCount(rowCount + 1);
await expect(await this.get().locator('.nc-grid-row')).toHaveCount(rowCount + 1);
await this._fillRow({ index, columnHeader, value: rowValue });
@ -151,6 +153,7 @@ export class GridPage extends BasePage {
// Click text=Delete Row
await this.rootPage.locator('text=Delete Row').click();
await this.rootPage.locator('text=Yes').click();
// todo: improve selector
await this.rootPage
.locator('span.ant-dropdown-menu-title-content > nc-project-menu-item')

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

@ -82,7 +82,7 @@ export class TreeViewPage extends BasePage {
await this.dashboard.waitForTabRender({ title, mode });
} else {
await this.get().locator(`.nc-project-tree-tbl-${title}`).click();
await this.rootPage.waitForTimeout(3000);
await this.rootPage.waitForTimeout(1000);
}
}

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

@ -46,6 +46,7 @@ export class ViewSidebarPage extends BasePage {
private async createView({ title, locator }: { title: string; locator: Locator }) {
await locator.click();
await this.rootPage.locator('input[id="form_item_title"]:visible').waitFor({ state: 'visible' });
await this.rootPage.locator('input[id="form_item_title"]:visible').fill(title);
const submitAction = () =>
this.rootPage.locator('.ant-modal-content').locator('button:has-text("Submit"):visible').click();

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

@ -68,25 +68,16 @@ export class WebhookFormPage extends BasePage {
await this.get().locator(`.nc-check-box-hook-condition`).click();
const modal = await this.get().locator(`.menu-filter-dropdown`).last();
// todo: All delays are for api calls that filter does, which rerenders
await this.rootPage.waitForTimeout(1000);
await modal.locator(`button:has-text("Add Filter")`).click();
await this.rootPage.waitForTimeout(1500);
await modal.locator('.nc-filter-field-select').click();
const modalField = await this.dashboard.rootPage.locator('.nc-dropdown-toolbar-field-list:visible');
await modalField.locator(`.ant-select-item:has-text("${column}")`).click();
await this.rootPage.waitForTimeout(1500);
await modal.locator('.nc-filter-operation-select').click();
const modalOp = await this.dashboard.rootPage.locator('.nc-dropdown-filter-comp-op:visible');
await modalOp.locator(`.ant-select-item:has-text("${operator}")`).click();
await this.rootPage.waitForTimeout(1500);
if (operator != 'is null' && operator != 'is not null') {
await modal.locator('.nc-filter-value-select > input').fill(value);
}

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

@ -56,7 +56,7 @@ export class ToolbarFilterPage extends BasePage {
}) {
if (!openModal) await this.get().locator(`button:has-text("Add Filter")`).first().click();
const selectedField = await this.rootPage.locator('.nc-filter-field-select').textContent();
const selectedField = await getTextExcludeIconText(await this.rootPage.locator('.nc-filter-field-select'));
if (selectedField !== title) {
await this.rootPage.locator('.nc-filter-field-select').last().click();
await this.rootPage
@ -65,7 +65,7 @@ export class ToolbarFilterPage extends BasePage {
.click();
}
const selectedOpType = await this.rootPage.locator('.nc-filter-operation-select').textContent();
const selectedOpType = await getTextExcludeIconText(await this.rootPage.locator('.nc-filter-operation-select'));
if (selectedOpType !== operation) {
await this.rootPage.locator('.nc-filter-operation-select').click();
// first() : filter list has >, >=
@ -78,7 +78,9 @@ export class ToolbarFilterPage extends BasePage {
// subtype for date
if (dataType === UITypes.Date && subOperation) {
const selectedSubType = await this.rootPage.locator('.nc-filter-sub_operation-select').textContent();
const selectedSubType = await getTextExcludeIconText(
await this.rootPage.locator('.nc-filter-sub_operation-select')
);
if (selectedSubType !== subOperation) {
await this.rootPage.locator('.nc-filter-sub_operation-select').click();
// first() : filter list has >, >=
@ -197,6 +199,18 @@ export class ToolbarFilterPage extends BasePage {
await this.toolbar.clickFilter();
}
async remove({ networkValidation = true }: { networkValidation?: boolean } = {}) {
if (networkValidation) {
await this.waitForResponse({
uiAction: () => this.get().locator('.nc-filter-item-remove-btn').click(),
httpMethodsToMatch: ['DELETE'],
requestUrlPathToMatch: '/api/v1/db/meta/filters/',
});
} else {
await this.get().locator('.nc-filter-item-remove-btn').click();
}
}
async columnOperatorList(param: { columnTitle: string }) {
await this.get().locator(`button:has-text("Add Filter")`).first().click();

11
tests/playwright/pages/Dashboard/index.ts

@ -212,4 +212,15 @@ export class DashboardPage extends BasePage {
async waitForLoaderToDisappear() {
await this.rootPage.locator('[data-testid="nc-loading"]').waitFor({ state: 'hidden' });
}
async closeAllTabs() {
await this.tabBar.locator(`.ant-tabs-tab`).waitFor({ state: 'visible' });
const tab = await this.tabBar.locator(`.ant-tabs-tab`);
const tabCount = await tab.count();
for (let i = 0; i < tabCount; i++) {
await tab.nth(i).locator('button.ant-tabs-tab-remove').click();
await tab.nth(i).waitFor({ state: 'detached' });
}
}
}

2
tests/playwright/playwright.config.ts

@ -11,7 +11,7 @@ require('dotenv').config();
* See https://playwright.dev/docs/test-configuration.
*/
const config: PlaywrightTestConfig = {
testDir: process.env.PW_QUICK_TEST ? './quickTests' : './tests',
testDir: process.env.PW_QUICK_TEST ? './quickTests' : './tests/db',
/* Maximum time one test can run for. */
timeout: process.env.CI ? 140 * 1000 : 100 * 1000,
expect: {

3
tests/playwright/quickTests/quickTests.spec.ts

@ -4,6 +4,7 @@ import { LoginPage } from '../pages/LoginPage';
import { ProjectsPage } from '../pages/ProjectsPage';
import { quickVerify } from './commonTest';
import { NcContext } from '../setup';
import { getDefaultPwd } from '../tests/utils/general';
test.describe('Quick tests', () => {
let dashboard: DashboardPage;
@ -12,7 +13,7 @@ test.describe('Quick tests', () => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.fillEmail({ email: 'user@nocodb.com', withoutPrefix: true });
await loginPage.fillPassword('Password123.');
await loginPage.fillPassword(getDefaultPwd());
await loginPage.submit();
const projectsPage = new ProjectsPage(page);

2
tests/playwright/scripts/docker-compose-playwright-pg.yml

@ -1,4 +1,4 @@
version: "2.1"
version: "2.2"
services:
pg147:

23
tests/playwright/tests/01-webhook.spec.ts → tests/playwright/tests/db/01-webhook.spec.ts

@ -1,11 +1,11 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import makeServer from '../setup/server';
import { WebhookFormPage } from '../pages/Dashboard/WebhookForm';
import { isSubset } from './utils/general';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import makeServer from '../../setup/server';
import { WebhookFormPage } from '../../pages/Dashboard/WebhookForm';
import { isSubset } from '../utils/general';
import { Api, UITypes } from 'nocodb-sdk';
import { isMysql, isPg, isSqlite } from '../setup/db';
import { isMysql, isPg, isSqlite } from '../../setup/db';
const hookPath = 'http://localhost:9090/hook';
let api: Api<any>;
@ -427,8 +427,8 @@ test.describe.serial('Webhook', () => {
async function verifyBulkOperationTrigger(rsp, type) {
for (let i = 0; i < rsp.length; i++) {
expect(rsp[i].type).toBe(type);
expect(rsp[i].data.table_name).toBe('numberBased');
expect(rsp[i].data.view_name).toBe('numberBased');
expect(rsp[i].data.table_name).toBe('Test');
expect(rsp[i].data.view_name).toBe('Test');
// only for insert, rows inserted will not be returned in response. just count
if (type === 'records.after.bulkInsert') {
@ -476,8 +476,8 @@ test.describe.serial('Webhook', () => {
try {
project = await api.project.read(context.project.id);
table = await api.base.tableCreate(context.project.id, project.bases?.[0].id, {
table_name: 'numberBased',
title: 'numberBased',
table_name: 'Test',
title: 'Test',
columns: columns,
});
} catch (e) {
@ -485,7 +485,7 @@ test.describe.serial('Webhook', () => {
}
await page.reload();
await dashboard.treeView.openTable({ title: 'numberBased' });
await dashboard.treeView.openTable({ title: 'Test' });
// create after insert webhook
await webhook.create({
@ -508,7 +508,6 @@ test.describe.serial('Webhook', () => {
}));
await api.dbTableRow.bulkCreate('noco', context.project.id, table.id, rowAttributesForInsert);
await page.reload();
// 50 records inserted, we expect 2 webhook responses
let rsp = await getWebhookResponses({ request, count: 1 });
await verifyBulkOperationTrigger(rsp, 'records.after.bulkInsert');

10
tests/playwright/tests/accountLicense.spec.ts → tests/playwright/tests/db/accountLicense.spec.ts

@ -1,8 +1,8 @@
import { test } from '@playwright/test';
import { AccountPage } from '../pages/Account';
import setup from '../setup';
import { AccountLicensePage } from '../pages/Account/License';
import { DashboardPage } from '../pages/Dashboard';
import { AccountPage } from '../../pages/Account';
import setup from '../../setup';
import { AccountLicensePage } from '../../pages/Account/License';
import { DashboardPage } from '../../pages/Dashboard';
test.describe('Enterprise License', () => {
// @ts-ignore
@ -11,7 +11,7 @@ test.describe('Enterprise License', () => {
let accountLicensePage: AccountLicensePage, accountPage: AccountPage, context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
accountPage = new AccountPage(page);
accountLicensePage = new AccountLicensePage(accountPage);
dashboard = new DashboardPage(page, context.project);

8
tests/playwright/tests/accountTokenManagement.spec.ts → tests/playwright/tests/db/accountTokenManagement.spec.ts

@ -1,7 +1,7 @@
import { test } from '@playwright/test';
import { AccountPage } from '../pages/Account';
import { AccountTokenPage } from '../pages/Account/Token';
import setup from '../setup';
import { AccountPage } from '../../pages/Account';
import { AccountTokenPage } from '../../pages/Account/Token';
import setup from '../../setup';
test.describe('User roles', () => {
let accountTokenPage: AccountTokenPage;
@ -10,7 +10,7 @@ test.describe('User roles', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
accountPage = new AccountPage(page);
accountTokenPage = new AccountTokenPage(accountPage);
});

15
tests/playwright/tests/accountUserManagement.spec.ts → tests/playwright/tests/db/accountUserManagement.spec.ts

@ -1,9 +1,10 @@
import { test } from '@playwright/test';
import { AccountPage } from '../pages/Account';
import { AccountUsersPage } from '../pages/Account/Users';
import { ProjectsPage } from '../pages/ProjectsPage';
import { SignupPage } from '../pages/SignupPage';
import setup from '../setup';
import { AccountPage } from '../../pages/Account';
import { AccountUsersPage } from '../../pages/Account/Users';
import { ProjectsPage } from '../../pages/ProjectsPage';
import { SignupPage } from '../../pages/SignupPage';
import setup from '../../setup';
import { getDefaultPwd } from '../utils/general';
const roleDb = [
{ email: 'creator@nocodb.com', role: 'Organization Level Creator', url: '' },
@ -19,7 +20,7 @@ test.describe('User roles', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
accountPage = new AccountPage(page);
accountUsersPage = new AccountUsersPage(accountPage);
@ -68,7 +69,7 @@ test.describe('User roles', () => {
await signupPage.signUp({
email: roleDb[roleIdx].email,
password: 'Password123.',
password: getDefaultPwd(),
});
await projectsPage.checkProjectCreateButton({

19
tests/playwright/tests/accountUserSettings.spec.ts → tests/playwright/tests/db/accountUserSettings.spec.ts

@ -1,8 +1,9 @@
import { test } from '@playwright/test';
import { AccountPage } from '../pages/Account';
import { AccountSettingsPage } from '../pages/Account/Settings';
import { SignupPage } from '../pages/SignupPage';
import setup from '../setup';
import { AccountPage } from '../../pages/Account';
import { AccountSettingsPage } from '../../pages/Account/Settings';
import { SignupPage } from '../../pages/SignupPage';
import setup from '../../setup';
import { getDefaultPwd } from '../utils/general';
test.describe('App settings', () => {
let accountSettingsPage: AccountSettingsPage;
@ -11,7 +12,7 @@ test.describe('App settings', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
accountPage = new AccountPage(page);
accountSettingsPage = accountPage.settings;
});
@ -24,8 +25,6 @@ test.describe('App settings', () => {
// todo: remove after route navigation issue resolved
await accountSettingsPage.rootPage.reload({ waitUntil: 'networkidle' });
await accountSettingsPage.waitUntilContentLoads();
// enable invite only signup
if (!(await accountSettingsPage.getInviteOnlyCheckboxValue())) {
await accountSettingsPage.toggleInviteOnlyCheckbox();
@ -39,7 +38,7 @@ test.describe('App settings', () => {
await signupPage.signUp({
email: 'test-user-1@nocodb.com',
password: 'Password123.',
password: getDefaultPwd(),
expectedError: 'Not allowed to signup, contact super admin.',
});
@ -47,8 +46,6 @@ test.describe('App settings', () => {
await accountSettingsPage.goto();
await accountSettingsPage.waitUntilContentLoads();
await accountSettingsPage.checkInviteOnlySignupCheckbox(true);
await accountSettingsPage.toggleInviteOnlyCheckbox();
await accountSettingsPage.checkInviteOnlySignupCheckbox(false);
@ -59,7 +56,7 @@ test.describe('App settings', () => {
await signupPage.signUp({
email: 'test-user-1@nocodb.com',
password: 'Password123.',
password: getDefaultPwd(),
});
});
});

23
tests/playwright/tests/authChangePassword.spec.ts → tests/playwright/tests/db/authChangePassword.spec.ts

@ -1,11 +1,12 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { LoginPage } from '../pages/LoginPage';
import { SettingsPage, SettingTab } from '../pages/Dashboard/Settings';
import { SignupPage } from '../pages/SignupPage';
import { ProjectsPage } from '../pages/ProjectsPage';
import { AccountPage } from '../pages/Account';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { LoginPage } from '../../pages/LoginPage';
import { SettingsPage, SettingTab } from '../../pages/Dashboard/Settings';
import { SignupPage } from '../../pages/SignupPage';
import { ProjectsPage } from '../../pages/ProjectsPage';
import { AccountPage } from '../../pages/Account';
import { getDefaultPwd } from '../utils/general';
test.describe('Auth', () => {
let context: any;
@ -16,7 +17,7 @@ test.describe('Auth', () => {
let accountPage: AccountPage;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
signupPage = new SignupPage(page);
projectsPage = new ProjectsPage(page);
@ -41,7 +42,7 @@ test.describe('Auth', () => {
await dashboard.rootPage.goto(url);
await signupPage.signUp({
email: 'user-1@nocodb.com',
password: 'Password123.',
password: getDefaultPwd(),
});
await projectsPage.openPasswordChangeModal();
@ -56,7 +57,7 @@ test.describe('Auth', () => {
// New pass and repeat pass mismatch
await accountPage.users.changePasswordPage.changePassword({
oldPass: 'Password123.',
oldPass: getDefaultPwd(),
newPass: '123456789',
repeatPass: '987654321',
networkValidation: false,
@ -65,7 +66,7 @@ test.describe('Auth', () => {
// All good
await accountPage.users.changePasswordPage.changePassword({
oldPass: 'Password123.',
oldPass: getDefaultPwd(),
newPass: 'NewPasswordConfigured',
repeatPass: 'NewPasswordConfigured',
networkValidation: true,

15
tests/playwright/tests/baseShare.spec.ts → tests/playwright/tests/db/baseShare.spec.ts

@ -1,9 +1,10 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { LoginPage } from '../pages/LoginPage';
import { ProjectsPage } from '../pages/ProjectsPage';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
import { LoginPage } from '../../pages/LoginPage';
import { ProjectsPage } from '../../pages/ProjectsPage';
import { getDefaultPwd } from '../utils/general';
test.describe('Shared base', () => {
let dashboard: DashboardPage;
@ -47,7 +48,7 @@ test.describe('Shared base', () => {
}
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
projectPage = new ProjectsPage(page);
toolbar = dashboard.grid.toolbar;
@ -77,7 +78,7 @@ test.describe('Shared base', () => {
await loginPage.signIn({
email: 'user@nocodb.com',
password: 'Password123.',
password: getDefaultPwd(),
withoutPrefix: true,
});

55
tests/playwright/tests/cellSelection.spec.ts → tests/playwright/tests/db/cellSelection.spec.ts

@ -1,109 +1,98 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { GridPage } from '../pages/Dashboard/Grid';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import { GridPage } from '../../pages/Dashboard/Grid';
import setup from '../../setup';
test.describe('Verify cell selection', () => {
let dashboard: DashboardPage, grid: GridPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
grid = dashboard.grid;
await dashboard.closeAllTabs();
});
test('#1 when range is selected, it has correct number of selected cells', async () => {
test('Suite-1', async () => {
// #1 when range is selected, it has correct number of selected cells
await dashboard.treeView.openTable({ title: 'Customer' });
await grid.selectRange({
start: { index: 0, columnHeader: 'FirstName' },
end: { index: 2, columnHeader: 'Email' },
});
expect(await grid.selectedCount()).toBe(9);
});
await dashboard.closeAllTabs();
test('#2 when copied with clipboard, it copies correct text', async () => {
// #2 when copied with clipboard, it copies correct text
await dashboard.treeView.openTable({ title: 'Customer' });
await grid.selectRange({
start: { index: 0, columnHeader: 'FirstName' },
end: { index: 1, columnHeader: 'LastName' },
});
expect(await grid.copyWithKeyboard()).toBe('MARY \t SMITH\n' + ' PATRICIA \t JOHNSON\n');
});
await dashboard.closeAllTabs();
test('#3 when copied with mouse, it copies correct text', async () => {
// #3 when copied with mouse, it copies correct text
await dashboard.treeView.openTable({ title: 'Customer' });
await grid.selectRange({
start: { index: 0, columnHeader: 'FirstName' },
end: { index: 1, columnHeader: 'LastName' },
});
expect(await grid.copyWithMouse({ index: 0, columnHeader: 'FirstName' })).toBe(
'MARY \t SMITH\n' + ' PATRICIA \t JOHNSON\n'
);
await dashboard.closeAllTabs();
});
// FIXME: this is edge case, better be moved to integration tests
test('#4 when cell inside selection range is clicked, it clears previous selection', async () => {
test('Suite-2', async ({ page }) => {
// #4 when cell inside selection range is clicked, it clears previous selection
await dashboard.treeView.openTable({ title: 'Country' });
await grid.selectRange({
start: { index: 0, columnHeader: 'Country' },
end: { index: 2, columnHeader: 'City List' },
});
expect(await grid.selectedCount()).toBe(9);
await grid.cell.get({ index: 0, columnHeader: 'Country' }).click();
expect(await grid.selectedCount()).toBe(1);
expect(await grid.cell.verifyCellActiveSelected({ index: 0, columnHeader: 'Country' }));
});
await dashboard.closeAllTabs();
// FIXME: this is edge case, better be moved to integration tests
test('#5 when cell outside selection range is clicked, it clears previous selection', async () => {
// #5 when cell outside selection range is clicked, it clears previous selection
await dashboard.treeView.openTable({ title: 'Country' });
await grid.selectRange({
start: { index: 0, columnHeader: 'Country' },
end: { index: 2, columnHeader: 'City List' },
});
expect(await grid.selectedCount()).toBe(9);
await grid.cell.get({ index: 5, columnHeader: 'Country' }).click();
expect(await grid.selectedCount()).toBe(1);
expect(await grid.cell.verifyCellActiveSelected({ index: 5, columnHeader: 'Country' }));
});
await dashboard.closeAllTabs();
// FIXME: this is edge case, better be moved to integration tests
test('#6 when selection ends on locked field, it still works as expected', async () => {
// #6 when selection ends on locked field, it still works as expected
await dashboard.treeView.openTable({ title: 'Country' });
await dashboard.grid.toolbar.fields.toggleShowSystemFields();
await grid.selectRange({
start: { index: 2, columnHeader: 'City List' },
end: { index: 0, columnHeader: 'Country' },
});
expect(await grid.selectedCount()).toBe(12);
await grid.cell.get({ index: 1, columnHeader: 'Country' }).click();
expect(await grid.selectedCount()).toBe(1);
expect(await grid.cell.verifyCellActiveSelected({ index: 1, columnHeader: 'Country' }));
});
await dashboard.grid.toolbar.fields.toggleShowSystemFields();
await dashboard.closeAllTabs();
// FIXME: this is edge case, better be moved to integration tests
test('#7 when navigated with keyboard, only active cell is affected', async ({ page }) => {
// #7 when navigated with keyboard, only active cell is affected
await dashboard.treeView.openTable({ title: 'Country' });
await grid.selectRange({
start: { index: 0, columnHeader: 'Country' },
end: { index: 2, columnHeader: 'City List' },
});
await page.keyboard.press('ArrowRight');
expect(await grid.selectedCount()).toBe(1);
expect(await grid.cell.verifyCellActiveSelected({ index: 0, columnHeader: 'LastUpdate' }));
await dashboard.closeAllTabs();
});
});

12
tests/playwright/tests/columnAttachments.spec.ts → tests/playwright/tests/db/columnAttachments.spec.ts

@ -1,16 +1,16 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { SharedFormPage } from '../pages/SharedForm';
import setup from '../setup';
import { AccountPage } from '../pages/Account';
import { AccountLicensePage } from '../pages/Account/License';
import { DashboardPage } from '../../pages/Dashboard';
import { SharedFormPage } from '../../pages/SharedForm';
import setup from '../../setup';
import { AccountPage } from '../../pages/Account';
import { AccountLicensePage } from '../../pages/Account/License';
test.describe('Attachment column', () => {
let dashboard: DashboardPage;
let accountLicensePage: AccountLicensePage, accountPage: AccountPage, context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
accountPage = new AccountPage(page);
accountLicensePage = new AccountLicensePage(accountPage);

8
tests/playwright/tests/columnBarcode.spec.ts → tests/playwright/tests/db/columnBarcode.spec.ts

@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { GridPage } from '../pages/Dashboard/Grid';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { GridPage } from '../../pages/Dashboard/Grid';
interface ExpectedBarcodeData {
referencedValue: string;
@ -14,7 +14,7 @@ test.describe('Virtual Columns', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
grid = dashboard.grid;
});

65
tests/playwright/tests/columnCheckbox.spec.ts → tests/playwright/tests/db/columnCheckbox.spec.ts

@ -1,7 +1,10 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
import { UITypes } from 'nocodb-sdk';
import { Api } from 'nocodb-sdk';
let api: Api<any>;
test.describe('Checkbox - cell, filter, sort', () => {
let dashboard: DashboardPage, toolbar: ToolbarPage;
@ -37,23 +40,61 @@ test.describe('Checkbox - cell, filter, sort', () => {
}
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
api = new Api({
baseURL: `http://localhost:8080/`,
headers: {
'xc-auth': context.token,
},
});
const columns = [
{
column_name: 'Id',
title: 'Id',
uidt: UITypes.ID,
},
{
column_name: 'Title',
title: 'Title',
uidt: UITypes.SingleLineText,
},
];
try {
const project = await api.project.read(context.project.id);
const table = await api.base.tableCreate(context.project.id, project.bases?.[0].id, {
table_name: 'Sheet-1',
title: 'Sheet-1',
columns: columns,
});
const rowAttributes = [];
for (let i = 0; i < 6; i++) {
const row = {
Id: i + 1,
Title: `1${String.fromCharCode(97 + i)}`,
};
rowAttributes.push(row);
}
await api.dbTableRow.bulkCreate('noco', context.project.id, table.id, rowAttributes);
} catch (e) {
console.error(e);
}
// page reload
await page.reload();
});
test('Checkbox', async () => {
// close 'Team & Auth' tab
await dashboard.closeTab({ title: 'Team & Auth' });
await dashboard.treeView.createTable({ title: 'Sheet1' });
await dashboard.grid.addNewRow({ index: 0, value: '1a' });
await dashboard.grid.addNewRow({ index: 1, value: '1b' });
await dashboard.grid.addNewRow({ index: 2, value: '1c' });
await dashboard.grid.addNewRow({ index: 3, value: '1d' });
await dashboard.grid.addNewRow({ index: 4, value: '1e' });
await dashboard.grid.addNewRow({ index: 5, value: '1f' });
await dashboard.treeView.openTable({ title: 'Sheet-1' });
// Create Checkbox column
await dashboard.grid.column.create({

6
tests/playwright/tests/columnDateTime.spec.ts → tests/playwright/tests/db/columnDateTime.spec.ts

@ -1,6 +1,6 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
const dateTimeData = [
{
@ -61,7 +61,7 @@ test.describe('DateTime Column', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
});

6
tests/playwright/tests/columnDuration.spec.ts → tests/playwright/tests/db/columnDuration.spec.ts

@ -1,6 +1,6 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
// Storing one additional dummy value "10" at end of every input array
// this will trigger update to previously committed data
@ -45,7 +45,7 @@ test.describe.skip('Duration column', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
});

8
tests/playwright/tests/columnFormula.spec.ts → tests/playwright/tests/db/columnFormula.spec.ts

@ -1,7 +1,7 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup, { NcContext } from '../setup';
import { isPg, isSqlite } from '../setup/db';
import { DashboardPage } from '../../pages/Dashboard';
import setup, { NcContext } from '../../setup';
import { isPg, isSqlite } from '../../setup/db';
// Add formula to be verified here & store expected results for 5 rows
// Column data from City table (Sakila DB)
@ -141,7 +141,7 @@ test.describe('Virtual Columns', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
});

8
tests/playwright/tests/columnGeoData.spec.ts → tests/playwright/tests/db/columnGeoData.spec.ts

@ -1,7 +1,7 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { GridPage } from '../pages/Dashboard/Grid';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { GridPage } from '../../pages/Dashboard/Grid';
test.describe('Geo Data column', () => {
let dashboard: DashboardPage;
@ -9,7 +9,7 @@ test.describe('Geo Data column', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
grid = dashboard.grid;
});

6
tests/playwright/tests/columnLinkToAnotherRecord.spec.ts → tests/playwright/tests/db/columnLinkToAnotherRecord.spec.ts

@ -1,6 +1,6 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
test.describe('LTAR create & update', () => {
let dashboard: DashboardPage;
@ -10,7 +10,7 @@ test.describe('LTAR create & update', () => {
test.setTimeout(150000);
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
});

6
tests/playwright/tests/columnLookupRollup.spec.ts → tests/playwright/tests/db/columnLookupRollup.spec.ts

@ -1,13 +1,13 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
test.describe('Virtual columns', () => {
let dashboard: DashboardPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
});

6
tests/playwright/tests/columnMenuOperations.spec.ts → tests/playwright/tests/db/columnMenuOperations.spec.ts

@ -1,6 +1,6 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
const columns = [
{
@ -43,7 +43,7 @@ test.describe('Column menu operations', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
});

10
tests/playwright/tests/columnMultiSelect.spec.ts → tests/playwright/tests/db/columnMultiSelect.spec.ts

@ -1,15 +1,15 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { GridPage } from '../pages/Dashboard/Grid';
import setup from '../setup';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { DashboardPage } from '../../pages/Dashboard';
import { GridPage } from '../../pages/Dashboard/Grid';
import setup from '../../setup';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
test.describe('Multi select', () => {
let dashboard: DashboardPage, grid: GridPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
grid = dashboard.grid;

8
tests/playwright/tests/columnQrCode.spec.ts → tests/playwright/tests/db/columnQrCode.spec.ts

@ -1,7 +1,7 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { GridPage } from '../pages/Dashboard/Grid';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { GridPage } from '../../pages/Dashboard/Grid';
type ExpectedQrCodeData = {
referencedValue: string;
@ -14,7 +14,7 @@ test.describe('Virtual Columns', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
grid = dashboard.grid;
});

8
tests/playwright/tests/columnRating.spec.ts → tests/playwright/tests/db/columnRating.spec.ts

@ -1,7 +1,7 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
test.describe('Rating - cell, filter, sort', () => {
let dashboard: DashboardPage, toolbar: ToolbarPage;
@ -37,7 +37,7 @@ test.describe('Rating - cell, filter, sort', () => {
}
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
});

8
tests/playwright/tests/columnRelationalExtendedTests.spec.ts → tests/playwright/tests/db/columnRelationalExtendedTests.spec.ts

@ -1,14 +1,14 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { isPg } from '../setup/db';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { isPg } from '../../setup/db';
test.describe('Relational Columns', () => {
let dashboard: DashboardPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
});

10
tests/playwright/tests/columnSingleSelect.spec.ts → tests/playwright/tests/db/columnSingleSelect.spec.ts

@ -1,15 +1,15 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { GridPage } from '../pages/Dashboard/Grid';
import setup from '../setup';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { DashboardPage } from '../../pages/Dashboard';
import { GridPage } from '../../pages/Dashboard/Grid';
import setup from '../../setup';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
test.describe('Single select', () => {
let dashboard: DashboardPage, grid: GridPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
grid = dashboard.grid;

16
tests/playwright/tests/erd.spec.ts → tests/playwright/tests/db/erd.spec.ts

@ -5,13 +5,13 @@ import {
pgSakilaSqlViews,
pgSakilaTables,
sqliteSakilaSqlViews,
} from './utils/sakila';
import { DashboardPage } from '../pages/Dashboard';
import { SettingsSubTab, SettingTab } from '../pages/Dashboard/Settings';
import setup from '../setup';
import { isMysql, isPg, isSqlite } from '../setup/db';
import { SettingsErdPage } from '../pages/Dashboard/Settings/Erd';
import { defaultBaseName } from '../constants';
} from '../utils/sakila';
import { DashboardPage } from '../../pages/Dashboard';
import { SettingsSubTab, SettingTab } from '../../pages/Dashboard/Settings';
import setup from '../../setup';
import { isMysql, isPg, isSqlite } from '../../setup/db';
import { SettingsErdPage } from '../../pages/Dashboard/Settings/Erd';
import { defaultBaseName } from '../../constants';
test.describe('Erd', () => {
let dashboard: DashboardPage;
@ -21,7 +21,7 @@ test.describe('Erd', () => {
test.slow();
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
if (isPg(context)) {

12
tests/playwright/tests/expandedFormUrl.spec.ts → tests/playwright/tests/db/expandedFormUrl.spec.ts

@ -1,16 +1,16 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { GalleryPage } from '../pages/Dashboard/Gallery';
import { GridPage } from '../pages/Dashboard/Grid';
import setup from '../setup';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { DashboardPage } from '../../pages/Dashboard';
import { GalleryPage } from '../../pages/Dashboard/Gallery';
import { GridPage } from '../../pages/Dashboard/Grid';
import setup from '../../setup';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
test.describe('Expanded form URL', () => {
let dashboard: DashboardPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
});

56
tests/playwright/tests/filters.spec.ts → tests/playwright/tests/db/filters.spec.ts

@ -1,10 +1,10 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
import { UITypes } from 'nocodb-sdk';
import { Api } from 'nocodb-sdk';
import { rowMixedValue } from '../setup/xcdb-records';
import { rowMixedValue } from '../../setup/xcdb-records';
import dayjs from 'dayjs';
let dashboard: DashboardPage, toolbar: ToolbarPage;
@ -89,7 +89,6 @@ async function verifyFilter(param: {
return;
}
await toolbar.clickFilter();
await toolbar.filter.add({
title: param.column,
operation: param.opType,
@ -97,16 +96,13 @@ async function verifyFilter(param: {
value: param.value,
locallySaved: false,
dataType: param?.dataType,
openModal: true,
});
await toolbar.clickFilter();
// verify filtered rows
await validateRowArray({
rowCount: param.result.rowCount,
});
// Reset filter
await toolbar.filter.reset();
}
// Number based filters
@ -206,6 +202,8 @@ test.describe('Filter Tests: Numerical', () => {
},
];
await toolbar.clickFilter();
await toolbar.filter.clickAddFilter();
for (let i = 0; i < filterList.length; i++) {
await verifyFilter({
column: dataType,
@ -218,7 +216,7 @@ test.describe('Filter Tests: Numerical', () => {
}
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
@ -305,6 +303,8 @@ test.describe('Filter Tests: Numerical', () => {
} catch (e) {
console.error(e);
}
await page.reload();
});
test('Filter: Number', async () => {
@ -327,7 +327,7 @@ test.describe('Filter Tests: Numerical', () => {
await numBasedFilterTest('Rating', '3', '2');
});
test('Filter: Duration', async () => {
test.skip('Filter: Duration', async () => {
await numBasedFilterTest('Duration', '00:01', '01:03');
});
@ -405,6 +405,8 @@ test.describe('Filter Tests: Text based', () => {
},
];
await toolbar.clickFilter();
await toolbar.filter.clickAddFilter();
for (let i = 0; i < filterList.length; i++) {
await verifyFilter({
column: dataType,
@ -416,7 +418,7 @@ test.describe('Filter Tests: Text based', () => {
}
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
@ -485,6 +487,7 @@ test.describe('Filter Tests: Text based', () => {
} catch (e) {
console.error(e);
}
await page.reload();
});
test('Filter: Single Line Text', async () => {
@ -577,6 +580,8 @@ test.describe('Filter Tests: Select based', () => {
},
];
await toolbar.clickFilter();
await toolbar.filter.clickAddFilter();
for (let i = 0; i < filterList.length; i++) {
await verifyFilter({
column: dataType,
@ -587,8 +592,9 @@ test.describe('Filter Tests: Select based', () => {
});
}
}
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
@ -641,14 +647,16 @@ test.describe('Filter Tests: Select based', () => {
} catch (e) {
console.error(e);
}
await page.reload();
});
test('Filter: Single Select', async () => {
await selectBasedFilterTest('SingleSelect', 'jan', 'jan,feb,mar', '');
// hack. jan inserted twice as in filter, toggling operation is not clearing value
await selectBasedFilterTest('SingleSelect', 'jan', 'jan,jan,feb,mar', '');
});
test('Filter: Multi Select', async () => {
await selectBasedFilterTest('MultiSelect', '', 'jan,feb,mar', 'jan,feb,mar');
await selectBasedFilterTest('MultiSelect', '', 'jan,jan,feb,mar', 'jan,feb,mar');
});
});
@ -933,7 +941,7 @@ test.describe('Filter Tests: Date based', () => {
}
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
@ -978,6 +986,7 @@ test.describe('Filter Tests: Date based', () => {
} catch (e) {
console.error(e);
}
await page.reload();
});
test('Date : filters-1', async () => {
@ -1018,6 +1027,8 @@ test.describe('Filter Tests: AddOn', () => {
},
];
await toolbar.clickFilter();
await toolbar.filter.clickAddFilter();
for (let i = 0; i < filterList.length; i++) {
await verifyFilter({
column: dataType,
@ -1029,7 +1040,7 @@ test.describe('Filter Tests: AddOn', () => {
}
}
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
@ -1080,6 +1091,7 @@ test.describe('Filter Tests: AddOn', () => {
} catch (e) {
console.error(e);
}
await page.reload();
});
test('Filter: Checkbox', async () => {
@ -1108,6 +1120,8 @@ test.describe('Filter Tests: Link to another record, Lookup, Rollup', () => {
{ op: 'is not blank', value: null, rowCount: 109 },
];
await toolbar.clickFilter();
await toolbar.filter.clickAddFilter();
for (let i = 0; i < filterList.length; i++) {
await verifyFilter({
column: 'City List',
@ -1144,6 +1158,8 @@ test.describe('Filter Tests: Link to another record, Lookup, Rollup', () => {
{ op: 'is not blank', value: null, rowCount: 599 },
];
await toolbar.clickFilter();
await toolbar.filter.clickAddFilter();
for (let i = 0; i < filterList.length; i++) {
await verifyFilter({
column: 'Lookup',
@ -1181,6 +1197,8 @@ test.describe('Filter Tests: Link to another record, Lookup, Rollup', () => {
{ op: 'is not blank', value: null, rowCount: 598 },
];
await toolbar.clickFilter();
await toolbar.filter.clickAddFilter();
for (let i = 0; i < filterList.length; i++) {
await verifyFilter({
column: 'Lookup',
@ -1193,7 +1211,7 @@ test.describe('Filter Tests: Link to another record, Lookup, Rollup', () => {
}
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
@ -1237,7 +1255,7 @@ test.describe('Filter Tests: Toggle button', () => {
*/
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
});

8
tests/playwright/tests/findRowByScanner.spec.ts → tests/playwright/tests/db/findRowByScanner.spec.ts

@ -1,8 +1,8 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { FormPage } from '../pages/Dashboard/Form';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
import { FormPage } from '../../pages/Dashboard/Form';
import setup from '../../setup';
// Skip for now as it is not working in CI atm
test.describe.skip('Find row by scanner', () => {

10
tests/playwright/tests/import.spec.ts → tests/playwright/tests/db/import.spec.ts

@ -1,9 +1,9 @@
import { test } from '@playwright/test';
import { airtableApiBase, airtableApiKey } from '../constants';
import { DashboardPage } from '../pages/Dashboard';
import { quickVerify } from '../quickTests/commonTest';
import setup from '../setup';
import { isPg, isSqlite } from '../setup/db';
import { airtableApiBase, airtableApiKey } from '../../constants';
import { DashboardPage } from '../../pages/Dashboard';
import { quickVerify } from '../../quickTests/commonTest';
import setup from '../../setup';
import { isPg, isSqlite } from '../../setup/db';
test.describe('Import', () => {
let dashboard: DashboardPage;

8
tests/playwright/tests/keyboardShortcuts.spec.ts → tests/playwright/tests/db/keyboardShortcuts.spec.ts

@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { GridPage } from '../pages/Dashboard/Grid';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import { GridPage } from '../../pages/Dashboard/Grid';
import setup from '../../setup';
import { Api, UITypes } from 'nocodb-sdk';
let api: Api<any>;
@ -11,7 +11,7 @@ test.describe('Verify shortcuts', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
grid = dashboard.grid;
});

10
tests/playwright/tests/language.spec.ts → tests/playwright/tests/db/language.spec.ts

@ -1,7 +1,7 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { ProjectsPage } from '../pages/ProjectsPage';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import { ProjectsPage } from '../../pages/ProjectsPage';
import setup from '../../setup';
const langMenu = [
'help-translate',
@ -47,7 +47,7 @@ test.describe('Common', () => {
let projectsPage: ProjectsPage;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
projectsPage = new ProjectsPage(page);
});
@ -58,7 +58,7 @@ test.describe('Common', () => {
// Index is the order in which menu options appear
for (let i = 1; i < langMenu.length; i++) {
// scripts/playwright/tests/language.spec.ts
const json = require(`../../../packages/nc-gui/lang/${langMenu[i]}`);
const json = require(`../../../../packages/nc-gui/lang/${langMenu[i]}`);
await projectsPage.openLanguageMenu();
await projectsPage.selectLanguage({ index: i });
await projectsPage.verifyLanguage({ json });

2
tests/playwright/tests/megaTable.spec.ts → tests/playwright/tests/db/megaTable.spec.ts

@ -1,5 +1,5 @@
import { test } from '@playwright/test';
import setup from '../setup';
import setup from '../../setup';
import { UITypes } from 'nocodb-sdk';
import { Api } from 'nocodb-sdk';
let api: Api<any>;

10
tests/playwright/tests/metaSync.spec.ts → tests/playwright/tests/db/metaSync.spec.ts

@ -1,8 +1,8 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { SettingsPage, SettingTab } from '../pages/Dashboard/Settings';
import setup, { NcContext } from '../setup';
import { isMysql, isPg, isSqlite, mysqlExec, pgExec, sqliteExec } from '../setup/db';
import { DashboardPage } from '../../pages/Dashboard';
import { SettingsPage, SettingTab } from '../../pages/Dashboard/Settings';
import setup, { NcContext } from '../../setup';
import { isMysql, isPg, isSqlite, mysqlExec, pgExec, sqliteExec } from '../../setup/db';
test.describe('Meta sync', () => {
let dashboard: DashboardPage;
@ -11,7 +11,7 @@ test.describe('Meta sync', () => {
let dbExec;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
settings = dashboard.settings;

10
tests/playwright/tests/mobileMode.spec.ts → tests/playwright/tests/db/mobileMode.spec.ts

@ -1,8 +1,8 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { FormPage } from '../pages/Dashboard/Form';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
import { FormPage } from '../../pages/Dashboard/Form';
import setup from '../../setup';
test.describe('Mobile Mode', () => {
let dashboard: DashboardPage;
@ -11,7 +11,7 @@ test.describe('Mobile Mode', () => {
let form: FormPage;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
form = dashboard.form;
toolbar = dashboard.grid.toolbar;

6
tests/playwright/tests/pagination.spec.ts → tests/playwright/tests/db/pagination.spec.ts

@ -1,13 +1,13 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
test.describe('Grid pagination', () => {
let dashboard: DashboardPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
});

8
tests/playwright/tests/projectOperations.spec.ts → tests/playwright/tests/db/projectOperations.spec.ts

@ -1,8 +1,8 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { ProjectsPage } from '../pages/ProjectsPage';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
import { ProjectsPage } from '../../pages/ProjectsPage';
import { Api } from 'nocodb-sdk';
test.describe('Project operations', () => {

15
tests/playwright/tests/rolesCreate.spec.ts → tests/playwright/tests/db/rolesCreate.spec.ts

@ -1,9 +1,10 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { SettingsPage, SettingTab } from '../pages/Dashboard/Settings';
import { SignupPage } from '../pages/SignupPage';
import { ProjectsPage } from '../pages/ProjectsPage';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { SettingsPage, SettingTab } from '../../pages/Dashboard/Settings';
import { SignupPage } from '../../pages/SignupPage';
import { ProjectsPage } from '../../pages/ProjectsPage';
import { getDefaultPwd } from '../utils/general';
const roleDb = [
{ email: 'creator@nocodb.com', role: 'creator', url: '' },
@ -20,7 +21,7 @@ test.describe('User roles', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
settings = dashboard.settings;
signupPage = new SignupPage(page);
@ -110,7 +111,7 @@ test.describe('User roles', () => {
await dashboard.rootPage.goto(roleDb[roleIdx].url);
await signupPage.signUp({
email: roleDb[roleIdx].email,
password: 'Password123.',
password: getDefaultPwd(),
});
await projectsPage.openProject({

10
tests/playwright/tests/rolesPreview.spec.ts → tests/playwright/tests/db/rolesPreview.spec.ts

@ -1,8 +1,8 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { SettingsPage, SettingTab } from '../pages/Dashboard/Settings';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
import { SettingsPage, SettingTab } from '../../pages/Dashboard/Settings';
const roles = ['Editor', 'Commenter', 'Viewer'];
@ -16,7 +16,7 @@ test.describe('Preview Mode', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
settings = dashboard.settings;

6
tests/playwright/tests/rolesSuperUser.spec.ts → tests/playwright/tests/db/rolesSuperUser.spec.ts

@ -1,13 +1,13 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
test.describe('Super user', () => {
let dashboard: DashboardPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
});

6
tests/playwright/tests/swagger.spec.ts → tests/playwright/tests/db/swagger.spec.ts

@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { GridPage } from '../pages/Dashboard/Grid';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import { GridPage } from '../../pages/Dashboard/Grid';
import setup from '../../setup';
test.describe('Table Column Operations', () => {
let grid: GridPage, dashboard: DashboardPage;

8
tests/playwright/tests/tableColumnOperation.spec.ts → tests/playwright/tests/db/tableColumnOperation.spec.ts

@ -1,14 +1,14 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { GridPage } from '../pages/Dashboard/Grid';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import { GridPage } from '../../pages/Dashboard/Grid';
import setup from '../../setup';
test.describe('Table Column Operations', () => {
let grid: GridPage, dashboard: DashboardPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: true });
dashboard = new DashboardPage(page, context.project);
grid = dashboard.grid;

8
tests/playwright/tests/tableOperations.spec.ts → tests/playwright/tests/db/tableOperations.spec.ts

@ -1,14 +1,14 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { SettingsPage, SettingTab } from '../pages/Dashboard/Settings';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import { SettingsPage, SettingTab } from '../../pages/Dashboard/Settings';
import setup from '../../setup';
test.describe('Table Operations', () => {
let dashboard: DashboardPage, settings: SettingsPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
settings = dashboard.settings;
});

8
tests/playwright/tests/toolbarOperations.spec.ts → tests/playwright/tests/db/toolbarOperations.spec.ts

@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
import setup from '../../setup';
test.describe('Toolbar operations (GRID)', () => {
let dashboard: DashboardPage, toolbar: ToolbarPage;
@ -16,7 +16,7 @@ test.describe('Toolbar operations (GRID)', () => {
}
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
});

10
tests/playwright/tests/undo-redo.spec.ts → tests/playwright/tests/db/undo-redo.spec.ts

@ -1,10 +1,10 @@
import { expect, Page, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { Api, UITypes } from 'nocodb-sdk';
import { rowMixedValue } from '../setup/xcdb-records';
import { GridPage } from '../pages/Dashboard/Grid';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { rowMixedValue } from '../../setup/xcdb-records';
import { GridPage } from '../../pages/Dashboard/Grid';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
let dashboard: DashboardPage,
grid: GridPage,

18
tests/playwright/tests/viewForm.spec.ts → tests/playwright/tests/db/viewForm.spec.ts

@ -1,10 +1,10 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { FormPage } from '../pages/Dashboard/Form';
import { SharedFormPage } from '../pages/SharedForm';
import { AccountPage } from '../pages/Account';
import { AccountAppStorePage } from '../pages/Account/AppStore';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { FormPage } from '../../pages/Dashboard/Form';
import { SharedFormPage } from '../../pages/SharedForm';
import { AccountPage } from '../../pages/Account';
import { AccountAppStorePage } from '../../pages/Account/AppStore';
// todo: Move most of the ui actions to page object and await on the api response
test.describe('Form view', () => {
@ -15,7 +15,7 @@ test.describe('Form view', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
form = dashboard.form;
accountPage = new AccountPage(page);
@ -177,8 +177,6 @@ test.describe('Form view', () => {
// activate SMTP plugin
await accountAppStorePage.goto();
await accountAppStorePage.rootPage.reload({ waitUntil: 'networkidle' });
await accountAppStorePage.waitUntilContentLoads();
// install SMTP
await accountAppStorePage.install({ name: 'SMTP' });
@ -205,8 +203,6 @@ test.describe('Form view', () => {
// Uninstall SMTP
await accountAppStorePage.goto();
await accountAppStorePage.rootPage.reload({ waitUntil: 'networkidle' });
await accountAppStorePage.waitUntilContentLoads();
await accountAppStorePage.uninstall({ name: 'SMTP' });
await dashboard.verifyToast({

8
tests/playwright/tests/viewFormShareSurvey.spec.ts → tests/playwright/tests/db/viewFormShareSurvey.spec.ts

@ -1,7 +1,7 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { SurveyFormPage } from '../pages/Dashboard/SurveyForm';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import { SurveyFormPage } from '../../pages/Dashboard/SurveyForm';
import setup from '../../setup';
test.describe('Share form', () => {
let dashboard: DashboardPage;
@ -9,7 +9,7 @@ test.describe('Share form', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
});

8
tests/playwright/tests/viewGridShare.spec.ts → tests/playwright/tests/db/viewGridShare.spec.ts

@ -1,7 +1,7 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { isMysql, isPg, isSqlite } from '../setup/db';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { isMysql, isPg, isSqlite } from '../../setup/db';
test.describe('Shared view', () => {
let dashboard: DashboardPage;
@ -10,7 +10,7 @@ test.describe('Shared view', () => {
let sharedLink: string;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
});

10
tests/playwright/tests/viewKanban.spec.ts → tests/playwright/tests/db/viewKanban.spec.ts

@ -1,9 +1,9 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { DashboardPage } from '../../pages/Dashboard';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
import setup from '../setup';
import { isPg, isSqlite } from '../setup/db';
import setup from '../../setup';
import { isPg, isSqlite } from '../../setup/db';
const filmRatings = ['G', 'PG', 'PG-13', 'R', 'NC-17'];
@ -12,7 +12,7 @@ test.describe('View', () => {
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
toolbar = toolbar = dashboard.kanban.toolbar;

8
tests/playwright/tests/viewMap.spec.ts → tests/playwright/tests/db/viewMap.spec.ts

@ -1,8 +1,8 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import { DashboardPage } from '../../pages/Dashboard';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
import setup from '../setup';
import setup from '../../setup';
test.describe('Map View', () => {
let dashboard: DashboardPage, toolbar: ToolbarPage;
@ -15,7 +15,7 @@ test.describe('Map View', () => {
const longitudeInShortDecimalLength = '30.5234';
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.map.toolbar;

8
tests/playwright/tests/viewMenu.spec.ts → tests/playwright/tests/db/viewMenu.spec.ts

@ -1,14 +1,14 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
import { isPg } from '../setup/db';
import { DashboardPage } from '../../pages/Dashboard';
import setup from '../../setup';
import { isPg } from '../../setup/db';
test.describe('Grid view locked', () => {
let dashboard: DashboardPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
});

8
tests/playwright/tests/views.spec.ts → tests/playwright/tests/db/views.spec.ts

@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import setup from '../setup';
import { DashboardPage } from '../../pages/Dashboard';
import { ToolbarPage } from '../../pages/Dashboard/common/Toolbar';
import setup from '../../setup';
test.describe('Views CRUD Operations', () => {
let dashboard: DashboardPage;
@ -9,7 +9,7 @@ test.describe('Views CRUD Operations', () => {
let toolbar: ToolbarPage;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
context = await setup({ page, isEmptyProject: false });
dashboard = new DashboardPage(page, context.project);
toolbar = dashboard.grid.toolbar;
});

14
tests/playwright/tests/utils/general.ts

@ -19,6 +19,14 @@ async function getTextExcludeIconText(selector) {
return text.trim();
}
async function getIconText(selector) {
// List of icons
const icons = await selector.locator('.material-symbols-outlined');
await icons.nth(0).waitFor();
return await icons.nth(0).textContent();
}
function isSubset(obj, potentialSubset) {
for (const prop in potentialSubset) {
// eslint-disable-next-line no-prototype-builtins
@ -38,4 +46,8 @@ function isSubset(obj, potentialSubset) {
return true;
}
export { getTextExcludeIconText, isSubset };
function getDefaultPwd() {
return 'Password123.';
}
export { getTextExcludeIconText, isSubset, getIconText, getDefaultPwd };

Loading…
Cancel
Save