Browse Source

Merge pull request #4376 from nocodb/refactor/playwright-refactor

fix(test): Moved playwright folder outside nc-gui
pull/4385/head
navi 2 years ago committed by GitHub
parent
commit
d193a32aec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      .github/workflows/playwright-test-workflow.yml
  2. 2
      package.json
  3. 2
      packages/nc-gui/components/cell/MultiSelect.vue
  4. 2
      packages/nc-gui/components/cell/SingleSelect.vue
  5. 2
      packages/nc-gui/components/cell/attachment/index.vue
  6. 8
      packages/nc-gui/components/dashboard/TreeView.vue
  7. 2
      packages/nc-gui/components/dashboard/settings/AuditTab.vue
  8. 4
      packages/nc-gui/components/dashboard/settings/Modal.vue
  9. 2
      packages/nc-gui/components/dlg/TableCreate.vue
  10. 2
      packages/nc-gui/components/general/TruncateText.vue
  11. 46
      packages/nc-gui/components/smartsheet/Form.vue
  12. 4
      packages/nc-gui/components/smartsheet/Gallery.vue
  13. 22
      packages/nc-gui/components/smartsheet/Grid.vue
  14. 2
      packages/nc-gui/components/smartsheet/Kanban.vue
  15. 2
      packages/nc-gui/components/smartsheet/Pagination.vue
  16. 2
      packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
  17. 6
      packages/nc-gui/components/smartsheet/column/SelectOptions.vue
  18. 2
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  19. 8
      packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue
  20. 2
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilterMenu.vue
  21. 4
      packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue
  22. 26
      packages/nc-gui/components/smartsheet/toolbar/ShareView.vue
  23. 2
      packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue
  24. 2
      packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue
  25. 2
      packages/nc-gui/components/tabs/auth/user-management/ShareBase.vue
  26. 13
      packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
  27. 8
      packages/nc-gui/layouts/base.vue
  28. 2
      packages/nc-gui/layouts/shared-view.vue
  29. 4
      packages/nc-gui/pages/[projectType]/[projectId]/index.vue
  30. 6
      packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue
  31. 6
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/index.vue
  32. 43
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue
  33. 6
      packages/nc-gui/pages/index/index/index.vue
  34. 12
      packages/nc-gui/pages/index/index/user.vue
  35. 15
      packages/nc-gui/pages/signin.vue
  36. 0
      tests/playwright/.env.example
  37. 0
      tests/playwright/.eslintrc.json
  38. 0
      tests/playwright/.gitignore
  39. 0
      tests/playwright/.lintstagedrc.json
  40. 0
      tests/playwright/.prettierignore
  41. 0
      tests/playwright/.prettierrc.js
  42. 8
      tests/playwright/README.md
  43. 0
      tests/playwright/constants/index.ts
  44. 0
      tests/playwright/fixtures/expectedBaseDownloadData.txt
  45. 0
      tests/playwright/fixtures/expectedBaseDownloadDataPg.txt
  46. 0
      tests/playwright/fixtures/expectedData.txt
  47. 0
      tests/playwright/fixtures/expectedDataSqlite.txt
  48. 0
      tests/playwright/fixtures/sampleFiles/1.json
  49. 0
      tests/playwright/fixtures/sampleFiles/2.json
  50. 0
      tests/playwright/fixtures/sampleFiles/3.json
  51. 0
      tests/playwright/fixtures/sampleFiles/4.json
  52. 0
      tests/playwright/fixtures/sampleFiles/5.json
  53. 0
      tests/playwright/fixtures/sampleFiles/6.json
  54. 0
      tests/playwright/fixtures/sampleFiles/simple.xlsx
  55. 0
      tests/playwright/fixtures/template.spec.ts
  56. 0
      tests/playwright/package-lock.json
  57. 1
      tests/playwright/package.json
  58. 0
      tests/playwright/pages/Base.ts
  59. 8
      tests/playwright/pages/Dashboard/ExpandedForm/index.ts
  60. 72
      tests/playwright/pages/Dashboard/Form/index.ts
  61. 2
      tests/playwright/pages/Dashboard/Gallery/index.ts
  62. 10
      tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts
  63. 0
      tests/playwright/pages/Dashboard/Grid/Column/LTAR/LinkRecord.ts
  64. 14
      tests/playwright/pages/Dashboard/Grid/Column/SelectOptionColumn.ts
  65. 2
      tests/playwright/pages/Dashboard/Grid/Column/index.ts
  66. 36
      tests/playwright/pages/Dashboard/Grid/index.ts
  67. 0
      tests/playwright/pages/Dashboard/Import/Airtable.ts
  68. 0
      tests/playwright/pages/Dashboard/Import/ImportTemplate.ts
  69. 4
      tests/playwright/pages/Dashboard/Kanban/index.ts
  70. 2
      tests/playwright/pages/Dashboard/Settings/Acl.ts
  71. 2
      tests/playwright/pages/Dashboard/Settings/AppStore.ts
  72. 2
      tests/playwright/pages/Dashboard/Settings/Audit.ts
  73. 2
      tests/playwright/pages/Dashboard/Settings/Erd.ts
  74. 2
      tests/playwright/pages/Dashboard/Settings/Metadata.ts
  75. 2
      tests/playwright/pages/Dashboard/Settings/Miscellaneous.ts
  76. 4
      tests/playwright/pages/Dashboard/Settings/Teams.ts
  77. 2
      tests/playwright/pages/Dashboard/Settings/index.ts
  78. 26
      tests/playwright/pages/Dashboard/SurveyForm/index.ts
  79. 6
      tests/playwright/pages/Dashboard/TreeView.ts
  80. 28
      tests/playwright/pages/Dashboard/ViewSidebar/index.ts
  81. 0
      tests/playwright/pages/Dashboard/WebhookForm/index.ts
  82. 4
      tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts
  83. 0
      tests/playwright/pages/Dashboard/common/Cell/CheckboxCell.ts
  84. 0
      tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts
  85. 5
      tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts
  86. 4
      tests/playwright/pages/Dashboard/common/Cell/index.ts
  87. 4
      tests/playwright/pages/Dashboard/common/ProjectMenu/index.ts
  88. 0
      tests/playwright/pages/Dashboard/common/Toolbar/Actions/Erd.ts
  89. 2
      tests/playwright/pages/Dashboard/common/Toolbar/Actions/index.ts
  90. 0
      tests/playwright/pages/Dashboard/common/Toolbar/AddEditKanbanStack.ts
  91. 8
      tests/playwright/pages/Dashboard/common/Toolbar/Fields.ts
  92. 4
      tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts
  93. 14
      tests/playwright/pages/Dashboard/common/Toolbar/ShareView.ts
  94. 4
      tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts
  95. 0
      tests/playwright/pages/Dashboard/common/Toolbar/StackBy.ts
  96. 0
      tests/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts
  97. 0
      tests/playwright/pages/Dashboard/common/Toolbar/index.ts
  98. 0
      tests/playwright/pages/Dashboard/commonBase/Erd.ts
  99. 33
      tests/playwright/pages/Dashboard/index.ts
  100. 8
      tests/playwright/pages/LoginPage/index.ts
  101. Some files were not shown because too many files have changed in this diff Show More

22
.github/workflows/playwright-test-workflow.yml

@ -56,15 +56,15 @@ jobs:
- name: setup mysql - name: setup mysql
if: ${{ inputs.db == 'mysql' }} if: ${{ inputs.db == 'mysql' }}
working-directory: ./ working-directory: ./
run: docker-compose -f ./packages/nc-gui/tests/playwright/scripts/docker-compose-mysql-playwright.yml up -d & run: docker-compose -f ./tests/playwright/scripts/docker-compose-mysql-playwright.yml up -d &
- name: setup pg - name: setup pg
if: ${{ inputs.db == 'pg' }} if: ${{ inputs.db == 'pg' }}
working-directory: ./ working-directory: ./
run: docker-compose -f ./packages/nc-gui/tests/playwright/scripts/docker-compose-playwright-pg.yml up -d & run: docker-compose -f ./tests/playwright/scripts/docker-compose-playwright-pg.yml up -d &
- name: setup pg for quick tests - name: setup pg for quick tests
if: ${{ inputs.db == 'sqlite' && inputs.shard == '1' }} if: ${{ inputs.db == 'sqlite' && inputs.shard == '1' }}
working-directory: ./ working-directory: ./
run: docker-compose -f ./packages/nc-gui/tests/playwright/scripts/docker-compose-pg-pw-quick.yml up -d & run: docker-compose -f ./tests/playwright/scripts/docker-compose-pg-pw-quick.yml up -d &
- name: run frontend - name: run frontend
working-directory: ./packages/nc-gui working-directory: ./packages/nc-gui
run: npm run ci:run run: npm run ci:run
@ -78,14 +78,14 @@ jobs:
id: playwright-cache id: playwright-cache
with: with:
path: | path: |
**/packages/nc-gui/tests/playwright/node_modules **/tests/playwright/node_modules
key: cache-nc-playwright-${{ hashFiles('**/packages/nc-gui/tests/playwright/package-lock.json') }} key: cache-nc-playwright-${{ hashFiles('**/tests/playwright/package-lock.json') }}
- name: Install dependencies - name: Install dependencies
if: steps.playwright-cache.outputs.cache-hit != 'true' if: steps.playwright-cache.outputs.cache-hit != 'true'
working-directory: ./packages/nc-gui/tests/playwright working-directory: ./tests/playwright
run: npm install run: npm install
- name: Install Playwright Browsers - name: Install Playwright Browsers
working-directory: ./packages/nc-gui/tests/playwright working-directory: ./tests/playwright
run: npx playwright install chromium --with-deps run: npx playwright install chromium --with-deps
- name: Wait for backend - name: Wait for backend
run: | run: |
@ -95,7 +95,7 @@ jobs:
done done
- name: Run Playwright tests - name: Run Playwright tests
working-directory: ./packages/nc-gui/tests/playwright working-directory: ./tests/playwright
run: E2E_DB_TYPE=${{ inputs.db }} npm run ci:test:shard:${{ inputs.shard }} run: E2E_DB_TYPE=${{ inputs.db }} npm run ci:test:shard:${{ inputs.shard }}
# Quick tests (pg on sqlite shard 0 and sqlite on sqlite shard 1) # Quick tests (pg on sqlite shard 0 and sqlite on sqlite shard 1)
@ -113,7 +113,7 @@ jobs:
npm run watch:run:playwright:quick > quick_${{ inputs.shard }}_test_backend.log & npm run watch:run:playwright:quick > quick_${{ inputs.shard }}_test_backend.log &
- name: Wait for backend & run quick tests - name: Wait for backend & run quick tests
if: ${{ inputs.db == 'sqlite' }} if: ${{ inputs.db == 'sqlite' }}
working-directory: ./packages/nc-gui/tests/playwright working-directory: ./tests/playwright
run: | run: |
while ! curl --output /dev/null --silent --head --fail http://localhost:8080; do while ! curl --output /dev/null --silent --head --fail http://localhost:8080; do
printf '.' printf '.'
@ -130,14 +130,14 @@ jobs:
if: ${{ inputs.db == 'sqlite' }} if: ${{ inputs.db == 'sqlite' }}
with: with:
name: playwright-report-quick-${{ inputs.shard }} name: playwright-report-quick-${{ inputs.shard }}
path: ./packages/nc-gui/tests/playwright/playwright-report-quick/ path: ./tests/playwright/playwright-report-quick/
retention-days: 2 retention-days: 2
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: always() if: always()
with: with:
name: playwright-report-${{ inputs.db }}-${{ inputs.shard }} name: playwright-report-${{ inputs.db }}-${{ inputs.shard }}
path: ./packages/nc-gui/tests/playwright/playwright-report/ path: ./tests/playwright/playwright-report/
retention-days: 2 retention-days: 2
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: always() if: always()

2
package.json

@ -36,7 +36,7 @@
] ]
}, },
"scripts": { "scripts": {
"lint:staged:playwright": "cd packages/nc-gui/tests/playwright; npx lint-staged; cd -", "lint:staged:playwright": "cd ./tests/playwright; npx lint-staged; cd -",
"build:common": "cd ./packages/nocodb-sdk; npm install; npm run build", "build:common": "cd ./packages/nocodb-sdk; npm install; npm run build",
"install:common": "cd ./packages/nocodb; npm install ../nocodb-sdk; cd ../nc-gui; npm install ../nocodb-sdk", "install:common": "cd ./packages/nocodb; npm install ../nocodb-sdk; cd ../nc-gui; npm install ../nocodb-sdk",
"start:api": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk; npm install; NC_DISABLE_CACHE=true NC_DISABLE_TELE=true npm run watch:run:cypress", "start:api": "npm run build:common ; cd ./packages/nocodb; npm install ../nocodb-sdk; npm install; NC_DISABLE_CACHE=true NC_DISABLE_TELE=true npm run watch:run:cypress",

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

@ -155,7 +155,7 @@ watch(isOpen, (n, _o) => {
v-for="op of options" v-for="op of options"
:key="op.id" :key="op.id"
:value="op.title" :value="op.title"
:data-nc="`select-option-${column.title}-${rowIndex}`" :data-testid="`select-option-${column.title}-${rowIndex}`"
@click.stop @click.stop
> >
<a-tag class="rounded-tag" :color="op.color"> <a-tag class="rounded-tag" :color="op.color">

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

@ -86,7 +86,7 @@ watch(isOpen, (n, _o) => {
v-for="op of options" v-for="op of options"
:key="op.title" :key="op.title"
:value="op.title" :value="op.title"
:data-nc="`select-option-${column.title}-${rowIndex}`" :data-testid="`select-option-${column.title}-${rowIndex}`"
@click.stop @click.stop
> >
<a-tag class="rounded-tag" :color="op.color"> <a-tag class="rounded-tag" :color="op.color">

2
packages/nc-gui/components/cell/attachment/index.vue

@ -161,7 +161,7 @@ watch(
v-if="!isReadonly" v-if="!isReadonly"
:class="{ 'mx-auto px-4': !visibleItems.length }" :class="{ 'mx-auto px-4': !visibleItems.length }"
class="group cursor-pointer flex gap-1 items-center active:(ring ring-accent ring-opacity-100) rounded border-1 p-1 shadow-sm hover:(bg-primary bg-opacity-10) dark:(!bg-slate-500)" class="group cursor-pointer flex gap-1 items-center active:(ring ring-accent ring-opacity-100) rounded border-1 p-1 shadow-sm hover:(bg-primary bg-opacity-10) dark:(!bg-slate-500)"
data-nc="attachment-cell-file-picker-button" data-testid="attachment-cell-file-picker-button"
@click.stop="open" @click.stop="open"
> >
<MdiReload v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" /> <MdiReload v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" />

8
packages/nc-gui/components/dashboard/TreeView.vue

@ -331,13 +331,13 @@ const onSearchCloseIconClick = () => {
class="nc-tree-item text-sm cursor-pointer group" class="nc-tree-item text-sm cursor-pointer group"
:data-order="table.order" :data-order="table.order"
:data-id="table.id" :data-id="table.id"
:data-nc="`tree-view-table-${table.title}`" :data-testid="`tree-view-table-${table.title}`"
@click="addTableTab(table)" @click="addTableTab(table)"
> >
<GeneralTooltip class="pl-5 pr-3 py-2" modifier-key="Alt"> <GeneralTooltip class="pl-5 pr-3 py-2" modifier-key="Alt">
<template #title>{{ table.table_name }}</template> <template #title>{{ table.table_name }}</template>
<div class="flex items-center gap-2 h-full" @contextmenu="setMenuContext('table', table)"> <div class="flex items-center gap-2 h-full" @contextmenu="setMenuContext('table', table)">
<div class="flex w-auto" :data-nc="`tree-view-table-draggable-handle-${table.title}`"> <div class="flex w-auto" :data-testid="`tree-view-table-draggable-handle-${table.title}`">
<MdiDragVertical <MdiDragVertical
v-if="isUIAllowed('treeview-drag-n-drop')" v-if="isUIAllowed('treeview-drag-n-drop')"
:class="`nc-child-draggable-icon-${table.title}`" :class="`nc-child-draggable-icon-${table.title}`"
@ -366,14 +366,14 @@ const onSearchCloseIconClick = () => {
<template #overlay> <template #overlay>
<a-menu class="!py-0 rounded text-sm"> <a-menu class="!py-0 rounded text-sm">
<a-menu-item v-if="isUIAllowed('table-rename')" @click="openRenameTableDialog(table)"> <a-menu-item v-if="isUIAllowed('table-rename')" @click="openRenameTableDialog(table)">
<div class="nc-project-menu-item" :data-nc="`sidebar-table-rename-${table.title}`"> <div class="nc-project-menu-item" :data-testid="`sidebar-table-rename-${table.title}`">
{{ $t('general.rename') }} {{ $t('general.rename') }}
</div> </div>
</a-menu-item> </a-menu-item>
<a-menu-item <a-menu-item
v-if="isUIAllowed('table-delete')" v-if="isUIAllowed('table-delete')"
:data-nc="`sidebar-table-delete-${table.title}`" :data-testid="`sidebar-table-delete-${table.title}`"
@click="deleteTable(table)" @click="deleteTable(table)"
> >
<div class="nc-project-menu-item"> <div class="nc-project-menu-item">

2
packages/nc-gui/components/dashboard/settings/AuditTab.vue

@ -112,7 +112,7 @@ const columns = [
:columns="columns" :columns="columns"
:pagination="false" :pagination="false"
:loading="isLoading" :loading="isLoading"
data-nc="audit-tab-table" data-testid="audit-tab-table"
> >
<template #emptyText> <template #emptyText>
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" :description="$t('labels.noData')" /> <a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" :description="$t('labels.noData')" />

4
packages/nc-gui/components/dashboard/settings/Modal.vue

@ -178,7 +178,7 @@ watch(
<a-button <a-button
type="text" type="text"
class="!rounded-md border-none -mt-1.5 -mr-1" class="!rounded-md border-none -mt-1.5 -mr-1"
data-nc="settings-modal-close-button" data-testid="settings-modal-close-button"
@click="vModel = false" @click="vModel = false"
> >
<template #icon> <template #icon>
@ -220,7 +220,7 @@ watch(
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
<component :is="selectedSubTab?.body" class="px-2 py-6" :data-nc="`nc-settings-subtab-${selectedSubTab.title}`" /> <component :is="selectedSubTab?.body" class="px-2 py-6" :data-testid="`nc-settings-subtab-${selectedSubTab.title}`" />
</a-layout-content> </a-layout-content>
</a-layout> </a-layout>
</a-modal> </a-modal>

2
packages/nc-gui/components/dlg/TableCreate.vue

@ -105,7 +105,7 @@ onMounted(() => {
v-model:value="table.title" v-model:value="table.title"
size="large" size="large"
hide-details hide-details
data-nc="create-table-title-input" data-testid="create-table-title-input"
:placeholder="$t('msg.info.enterTableName')" :placeholder="$t('msg.info.enterTableName')"
/> />
</a-form-item> </a-form-item>

2
packages/nc-gui/components/general/TruncateText.vue

@ -38,7 +38,7 @@ const shortName = computed(() =>
</template> </template>
<div class="w-full">{{ shortName }}</div> <div class="w-full">{{ shortName }}</div>
</a-tooltip> </a-tooltip>
<div v-else class="w-full" data-nc="truncate-label"> <div v-else class="w-full" data-testid="truncate-label">
<slot /> <slot />
</div> </div>
<div ref="text" class="hidden"> <div ref="text" class="hidden">

46
packages/nc-gui/components/smartsheet/Form.vue

@ -387,7 +387,7 @@ watch(view, (nextView) => {
</script> </script>
<template> <template>
<a-row v-if="submitted" class="h-full" data-nc="nc-form-wrapper-submit"> <a-row v-if="submitted" class="h-full" data-testid="nc-form-wrapper-submit">
<a-col :span="24"> <a-col :span="24">
<div v-if="formViewData" class="items-center justify-center text-center mt-2"> <div v-if="formViewData" class="items-center justify-center text-center mt-2">
<a-alert type="success"> <a-alert type="success">
@ -407,7 +407,7 @@ watch(view, (nextView) => {
</a-col> </a-col>
</a-row> </a-row>
<a-row v-else class="h-full flex" data-nc="nc-form-wrapper"> <a-row v-else class="h-full flex" data-testid="nc-form-wrapper">
<a-col v-if="isEditable" :span="8" class="shadow p-2 md:p-4 h-full overflow-auto scrollbar-thin-dull nc-form-left-drawer"> <a-col v-if="isEditable" :span="8" class="shadow p-2 md:p-4 h-full overflow-auto scrollbar-thin-dull nc-form-left-drawer">
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<div class="flex-1 text-lg"> <div class="flex-1 text-lg">
@ -419,7 +419,7 @@ watch(view, (nextView) => {
v-if="hiddenColumns.length" v-if="hiddenColumns.length"
type="button" type="button"
class="nc-form-add-all color-transition bg-white transform hover:(text-primary ring ring-accent ring-opacity-100) active:translate-y-[1px] px-2 py-1 shadow-md rounded" class="nc-form-add-all color-transition bg-white transform hover:(text-primary ring ring-accent ring-opacity-100) active:translate-y-[1px] px-2 py-1 shadow-md rounded"
data-nc="nc-form-add-all" data-testid="nc-form-add-all"
@click="addAllColumns" @click="addAllColumns"
> >
<!-- Add all --> <!-- Add all -->
@ -430,7 +430,7 @@ watch(view, (nextView) => {
v-if="localColumns.length" v-if="localColumns.length"
type="button" type="button"
class="nc-form-remove-all color-transition bg-white transform hover:(text-primary ring ring-accent ring-opacity-100) active:translate-y-[1px] px-2 py-1 shadow-md rounded" class="nc-form-remove-all color-transition bg-white transform hover:(text-primary ring ring-accent ring-opacity-100) active:translate-y-[1px] px-2 py-1 shadow-md rounded"
data-nc="nc-form-remove-all" data-testid="nc-form-remove-all"
@click="removeAllColumns" @click="removeAllColumns"
> >
<!-- Remove all --> <!-- Remove all -->
@ -452,7 +452,7 @@ watch(view, (nextView) => {
<a-card <a-card
size="small" size="small"
class="!border-0 color-transition cursor-pointer item hover:(bg-primary ring-1 ring-accent ring-opacity-100) bg-opacity-10 !rounded !shadow-lg" class="!border-0 color-transition cursor-pointer item hover:(bg-primary ring-1 ring-accent ring-opacity-100) bg-opacity-10 !rounded !shadow-lg"
:data-nc="`nc-form-hidden-column-${element.label || element.title}`" :data-testid="`nc-form-hidden-column-${element.label || element.title}`"
@mousedown="moved = false" @mousedown="moved = false"
@mousemove="moved = false" @mousemove="moved = false"
@mouseup="handleMouseUp(element, index)" @mouseup="handleMouseUp(element, index)"
@ -480,7 +480,7 @@ watch(view, (nextView) => {
<template #footer> <template #footer>
<div <div
class="my-4 select-none border-dashed border-2 border-gray-400 py-3 text-gray-400 text-center nc-drag-n-drop-to-hide" class="my-4 select-none border-dashed border-2 border-gray-400 py-3 text-gray-400 text-center nc-drag-n-drop-to-hide"
data-nc="nc-drag-n-drop-to-hide" data-testid="nc-drag-n-drop-to-hide"
> >
<!-- Drag and drop fields here to hide --> <!-- Drag and drop fields here to hide -->
{{ $t('msg.info.dragDropHide') }} {{ $t('msg.info.dragDropHide') }}
@ -538,7 +538,7 @@ watch(view, (nextView) => {
hide-details hide-details
placeholder="Form Title" placeholder="Form Title"
:bordered="false" :bordered="false"
data-nc="nc-form-heading" data-testid="nc-form-heading"
@blur="updateView" @blur="updateView"
@keydown.enter="updateView" @keydown.enter="updateView"
/> />
@ -558,7 +558,7 @@ watch(view, (nextView) => {
:placeholder="$t('msg.info.formDesc')" :placeholder="$t('msg.info.formDesc')"
:bordered="false" :bordered="false"
:disabled="!isEditable" :disabled="!isEditable"
data-nc="nc-form-sub-heading" data-testid="nc-form-sub-heading"
@blur="updateView" @blur="updateView"
@click="updateView" @click="updateView"
/> />
@ -588,7 +588,7 @@ watch(view, (nextView) => {
'bg-primary bg-opacity-5 ring-0.5 ring-accent ring-opacity-100': activeRow === element.title, 'bg-primary bg-opacity-5 ring-0.5 ring-accent ring-opacity-100': activeRow === element.title,
}, },
]" ]"
data-nc="nc-form-fields" data-testid="nc-form-fields"
@click="activeRow = element.title" @click="activeRow = element.title"
> >
<div <div
@ -597,7 +597,7 @@ watch(view, (nextView) => {
> >
<MdiEyeOffOutline <MdiEyeOffOutline
class="opacity-0 nc-field-remove-icon" class="opacity-0 nc-field-remove-icon"
data-nc="nc-field-remove-icon" data-testid="nc-field-remove-icon"
@click.stop="hideColumn(index)" @click.stop="hideColumn(index)"
/> />
</div> </div>
@ -606,7 +606,7 @@ watch(view, (nextView) => {
<div class="flex gap-2 items-center"> <div class="flex gap-2 items-center">
<span <span
class="text-gray-500 mr-2 nc-form-input-required" class="text-gray-500 mr-2 nc-form-input-required"
data-nc="nc-form-input-required" data-testid="nc-form-input-required"
@click=" @click="
() => { () => {
element.required = !element.required element.required = !element.required
@ -630,7 +630,7 @@ watch(view, (nextView) => {
v-model:value="element.label" v-model:value="element.label"
type="text" type="text"
class="form-meta-input nc-form-input-label" class="form-meta-input nc-form-input-label"
data-nc="nc-form-input-label" data-testid="nc-form-input-label"
:placeholder="$t('msg.info.formInput')" :placeholder="$t('msg.info.formInput')"
@change="updateColMeta(element)" @change="updateColMeta(element)"
> >
@ -642,7 +642,7 @@ watch(view, (nextView) => {
v-model:value="element.description" v-model:value="element.description"
type="text" type="text"
class="form-meta-input text-sm nc-form-input-help-text" class="form-meta-input text-sm nc-form-input-help-text"
data-nc="nc-form-input-help-text" data-testid="nc-form-input-help-text"
:placeholder="$t('msg.info.formHelpText')" :placeholder="$t('msg.info.formHelpText')"
@change="updateColMeta(element)" @change="updateColMeta(element)"
/> />
@ -655,7 +655,7 @@ watch(view, (nextView) => {
:column="{ ...element, title: element.label || element.title }" :column="{ ...element, title: element.label || element.title }"
:required="isRequired(element, element.required)" :required="isRequired(element, element.required)"
:hide-menu="true" :hide-menu="true"
data-nc="nc-form-input-label" data-testid="nc-form-input-label"
/> />
<LazySmartsheetHeaderCell <LazySmartsheetHeaderCell
@ -663,7 +663,7 @@ watch(view, (nextView) => {
:column="{ ...element, title: element.label || element.title }" :column="{ ...element, title: element.label || element.title }"
:required="isRequired(element, element.required)" :required="isRequired(element, element.required)"
:hide-menu="true" :hide-menu="true"
data-nc="nc-form-input-label" data-testid="nc-form-input-label"
/> />
</div> </div>
@ -678,7 +678,7 @@ watch(view, (nextView) => {
:row="row" :row="row"
class="nc-input" class="nc-input"
:class="`nc-form-input-${element.title.replaceAll(' ', '')}`" :class="`nc-form-input-${element.title.replaceAll(' ', '')}`"
:data-nc="`nc-form-input-${element.title.replaceAll(' ', '')}`" :data-testid="`nc-form-input-${element.title.replaceAll(' ', '')}`"
:column="element" :column="element"
@click.stop.prevent @click.stop.prevent
/> />
@ -694,14 +694,14 @@ watch(view, (nextView) => {
v-model="formState[element.title]" v-model="formState[element.title]"
class="nc-input" class="nc-input"
:class="`nc-form-input-${element.title.replaceAll(' ', '')}`" :class="`nc-form-input-${element.title.replaceAll(' ', '')}`"
:data-nc="`nc-form-input-${element.title.replaceAll(' ', '')}`" :data-testid="`nc-form-input-${element.title.replaceAll(' ', '')}`"
:column="element" :column="element"
:edit-enabled="true" :edit-enabled="true"
@click.stop.prevent @click.stop.prevent
/> />
</a-form-item> </a-form-item>
<div class="text-gray-500 text-xs" data-nc="nc-form-input-help-text-label">{{ element.description }}</div> <div class="text-gray-500 text-xs" data-testid="nc-form-input-help-text-label">{{ element.description }}</div>
</div> </div>
</template> </template>
@ -716,7 +716,7 @@ watch(view, (nextView) => {
</Draggable> </Draggable>
<div class="justify-center flex mt-6"> <div class="justify-center flex mt-6">
<button type="submit" class="uppercase scaling-btn nc-form-submit" data-nc="nc-form-submit" @click="submitForm"> <button type="submit" class="uppercase scaling-btn nc-form-submit" data-testid="nc-form-submit" @click="submitForm">
{{ $t('general.submit') }} {{ $t('general.submit') }}
</button> </button>
</div> </div>
@ -738,7 +738,7 @@ watch(view, (nextView) => {
:rows="3" :rows="3"
hide-details hide-details
class="nc-form-after-submit-msg" class="nc-form-after-submit-msg"
data-nc="nc-form-after-submit-msg" data-testid="nc-form-after-submit-msg"
@change="updateView" @change="updateView"
/> />
@ -751,7 +751,7 @@ watch(view, (nextView) => {
v-e="[`a:form-view:submit-another-form`]" v-e="[`a:form-view:submit-another-form`]"
size="small" size="small"
class="nc-form-checkbox-submit-another-form" class="nc-form-checkbox-submit-another-form"
data-nc="nc-form-checkbox-submit-another-form" data-testid="nc-form-checkbox-submit-another-form"
@change="updateView" @change="updateView"
/> />
<span class="ml-4">{{ $t('msg.info.submitAnotherForm') }}</span> <span class="ml-4">{{ $t('msg.info.submitAnotherForm') }}</span>
@ -764,7 +764,7 @@ watch(view, (nextView) => {
v-e="[`a:form-view:show-blank-form`]" v-e="[`a:form-view:show-blank-form`]"
size="small" size="small"
class="nc-form-checkbox-show-blank-form" class="nc-form-checkbox-show-blank-form"
data-nc="nc-form-checkbox-show-blank-form" data-testid="nc-form-checkbox-show-blank-form"
@change="updateView" @change="updateView"
/> />
@ -777,7 +777,7 @@ watch(view, (nextView) => {
v-e="[`a:form-view:email-me`]" v-e="[`a:form-view:email-me`]"
size="small" size="small"
class="nc-form-checkbox-send-email" class="nc-form-checkbox-send-email"
data-nc="nc-form-checkbox-send-email" data-testid="nc-form-checkbox-send-email"
@change="onEmailChange" @change="onEmailChange"
/> />

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

@ -163,14 +163,14 @@ watch(view, async (nextView) => {
</script> </script>
<template> <template>
<div class="flex flex-col h-full w-full overflow-auto nc-gallery" data-nc="nc-gallery-wrapper"> <div class="flex flex-col h-full w-full overflow-auto nc-gallery" data-testid="nc-gallery-wrapper">
<div class="nc-gallery-container grid gap-2 my-4 px-3"> <div class="nc-gallery-container grid gap-2 my-4 px-3">
<div v-for="record in data" :key="`record-${record.row.id}`"> <div v-for="record in data" :key="`record-${record.row.id}`">
<LazySmartsheetRow :row="record"> <LazySmartsheetRow :row="record">
<a-card <a-card
hoverable hoverable
class="!rounded-lg h-full overflow-hidden break-all max-w-[450px]" class="!rounded-lg h-full overflow-hidden break-all max-w-[450px]"
:data-nc="`nc-gallery-card-${record.row.id}`" :data-testid="`nc-gallery-card-${record.row.id}`"
@click="expandFormClick($event, record)" @click="expandFormClick($event, record)"
> >
<template v-if="galleryData?.fk_cover_image_col_id" #cover> <template v-if="galleryData?.fk_cover_image_col_id" #cover>

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

@ -460,8 +460,8 @@ watch(
</script> </script>
<template> <template>
<div class="relative flex flex-col h-full min-h-0 w-full" data-nc="nc-grid-wrapper"> <div class="relative flex flex-col h-full min-h-0 w-full" data-testid="nc-grid-wrapper">
<general-overlay :model-value="isLoading" inline transition class="!bg-opacity-15" data-nc="grid-load-spinner"> <general-overlay :model-value="isLoading" inline transition class="!bg-opacity-15" data-testid="grid-load-spinner">
<div class="flex items-center justify-center h-full w-full !bg-white !bg-opacity-85 z-1000"> <div class="flex items-center justify-center h-full w-full !bg-white !bg-opacity-85 z-1000">
<a-spin size="large" /> <a-spin size="large" />
</div> </div>
@ -480,8 +480,8 @@ watch(
> >
<thead ref="tableHead"> <thead ref="tableHead">
<tr class="nc-grid-header border-1 bg-gray-100 sticky top[-1px]"> <tr class="nc-grid-header border-1 bg-gray-100 sticky top[-1px]">
<th data-nc="grid-id-column"> <th data-testid="grid-id-column">
<div class="w-full h-full bg-gray-100 flex min-w-[70px] pl-5 pr-1 items-center" data-nc="nc-check-all"> <div class="w-full h-full bg-gray-100 flex min-w-[70px] pl-5 pr-1 items-center" data-testid="nc-check-all">
<template v-if="!readOnly"> <template v-if="!readOnly">
<div class="nc-no-label text-gray-500" :class="{ hidden: selectedAllRecords }">#</div> <div class="nc-no-label text-gray-500" :class="{ hidden: selectedAllRecords }">#</div>
<div <div
@ -546,8 +546,8 @@ watch(
<tbody ref="tbodyEl" @selectstart.prevent> <tbody ref="tbodyEl" @selectstart.prevent>
<LazySmartsheetRow v-for="(row, rowIndex) of data" ref="rowRefs" :key="rowIndex" :row="row"> <LazySmartsheetRow v-for="(row, rowIndex) of data" ref="rowRefs" :key="rowIndex" :row="row">
<template #default="{ state }"> <template #default="{ state }">
<tr class="nc-grid-row" :data-nc="`grid-row-${rowIndex}`"> <tr class="nc-grid-row" :data-testid="`grid-row-${rowIndex}`">
<td key="row-index" class="caption nc-grid-cell pl-5 pr-1" :data-nc="`cell-Id-${rowIndex}`"> <td key="row-index" class="caption nc-grid-cell pl-5 pr-1" :data-testid="`cell-Id-${rowIndex}`">
<div class="items-center flex gap-1 min-w-[55px]"> <div class="items-center flex gap-1 min-w-[55px]">
<div <div
v-if="!readOnly || !isLocked" v-if="!readOnly || !isLocked"
@ -568,10 +568,14 @@ watch(
<div <div
v-if="!readOnly || hasRole('commenter', true) || hasRole('viewer', true)" v-if="!readOnly || hasRole('commenter', true) || hasRole('viewer', true)"
class="nc-expand" class="nc-expand"
:data-nc="`nc-expand-${rowIndex}`" :data-testid="`nc-expand-${rowIndex}`"
:class="{ 'nc-comment': row.rowMeta?.commentCount }" :class="{ 'nc-comment': row.rowMeta?.commentCount }"
> >
<a-spin v-if="row.rowMeta.saving" class="!flex items-center" :data-nc="`row-save-spinner-${rowIndex}`" /> <a-spin
v-if="row.rowMeta.saving"
class="!flex items-center"
:data-testid="`row-save-spinner-${rowIndex}`"
/>
<template v-else> <template v-else>
<span <span
v-if="row.rowMeta?.commentCount" v-if="row.rowMeta?.commentCount"
@ -605,7 +609,7 @@ watch(
(hasEditPermission && selectedRange(rowIndex, colIndex)), (hasEditPermission && selectedRange(rowIndex, colIndex)),
'nc-required-cell': isColumnRequiredAndNull(columnObj, row.row), 'nc-required-cell': isColumnRequiredAndNull(columnObj, row.row),
}" }"
:data-nc="`cell-${columnObj.title}-${rowIndex}`" :data-testid="`cell-${columnObj.title}-${rowIndex}`"
:data-key="rowIndex + columnObj.id" :data-key="rowIndex + columnObj.id"
:data-col="columnObj.id" :data-col="columnObj.id"
:data-title="columnObj.title" :data-title="columnObj.title"

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

@ -309,7 +309,7 @@ watch(view, async (nextView) => {
</script> </script>
<template> <template>
<div class="flex h-full bg-white px-2" data-nc="nc-kanban-wrapper"> <div class="flex h-full bg-white px-2" data-testid="nc-kanban-wrapper">
<div ref="kanbanContainerRef" class="nc-kanban-container flex my-4 px-3 overflow-x-scroll overflow-y-hidden"> <div ref="kanbanContainerRef" class="nc-kanban-container flex my-4 px-3 overflow-x-scroll overflow-y-hidden">
<a-dropdown v-model:visible="contextMenu" :trigger="['contextmenu']" overlay-class-name="nc-dropdown-kanban-context-menu"> <a-dropdown v-model:visible="contextMenu" :trigger="['contextmenu']" overlay-class-name="nc-dropdown-kanban-context-menu">
<!-- Draggable Stack --> <!-- Draggable Stack -->

2
packages/nc-gui/components/smartsheet/Pagination.vue

@ -19,7 +19,7 @@ const page = computed({
<template> <template>
<div class="flex items-center mb-1"> <div class="flex items-center mb-1">
<span v-if="count !== null && count !== Infinity" class="caption ml-5 text-gray-500" data-nc="grid-pagination"> <span v-if="count !== null && count !== Infinity" class="caption ml-5 text-gray-500" data-testid="grid-pagination">
{{ count }} {{ count !== 1 ? $t('objects.records') : $t('objects.record') }} {{ count }} {{ count !== 1 ? $t('objects.records') : $t('objects.record') }}
</span> </span>

2
packages/nc-gui/components/smartsheet/column/EditOrAdd.vue

@ -124,7 +124,7 @@ onMounted(() => {
:class="{ '!w-[600px]': formState.uidt === UITypes.Formula }" :class="{ '!w-[600px]': formState.uidt === UITypes.Formula }"
@click.stop @click.stop
> >
<a-form v-model="formState" no-style name="column-create-or-edit" layout="vertical" data-nc="add-or-edit-column"> <a-form v-model="formState" no-style name="column-create-or-edit" layout="vertical" data-testid="add-or-edit-column">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<a-form-item :label="$t('labels.columnName')" v-bind="validateInfos.title"> <a-form-item :label="$t('labels.columnName')" v-bind="validateInfos.title">
<a-input <a-input

6
packages/nc-gui/components/smartsheet/column/SelectOptions.vue

@ -137,7 +137,7 @@ watch(inputs, () => {
v-if="!isKanban" v-if="!isKanban"
small small
class="nc-child-draggable-icon handle" class="nc-child-draggable-icon handle"
:data-nc="`select-option-column-handle-icon-${element.title}`" :data-testid="`select-option-column-handle-icon-${element.title}`"
/> />
<a-dropdown <a-dropdown
v-model:visible="colorMenus[index]" v-model:visible="colorMenus[index]"
@ -162,14 +162,14 @@ watch(inputs, () => {
ref="inputs" ref="inputs"
v-model:value="element.title" v-model:value="element.title"
class="caption" class="caption"
:data-nc="`select-column-option-input-${index}`" :data-testid="`select-column-option-input-${index}`"
@change="optionChanged(element.id)" @change="optionChanged(element.id)"
/> />
<MdiClose <MdiClose
class="ml-2 hover:!text-black" class="ml-2 hover:!text-black"
:style="{ color: 'red' }" :style="{ color: 'red' }"
:data-nc="`select-column-option-remove-${index}`" :data-testid="`select-column-option-remove-${index}`"
@click="removeOption(index)" @click="removeOption(index)"
/> />
</div> </div>

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

@ -150,7 +150,7 @@ export default {
:key="col.title" :key="col.title"
class="mt-2 py-2" class="mt-2 py-2"
:class="`nc-expand-col-${col.title}`" :class="`nc-expand-col-${col.title}`"
:data-nc="`nc-expand-col-${col.title}`" :data-testid="`nc-expand-col-${col.title}`"
> >
<LazySmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" /> <LazySmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" />

8
packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue

@ -167,12 +167,12 @@ function onStopEdit() {
<template> <template>
<a-menu-item <a-menu-item
class="select-none group !flex !items-center !my-0 hover:(bg-primary !bg-opacity-5)" class="select-none group !flex !items-center !my-0 hover:(bg-primary !bg-opacity-5)"
:data-nc="`view-sidebar-view-${vModel.alias || vModel.title}`" :data-testid="`view-sidebar-view-${vModel.alias || vModel.title}`"
@dblclick.stop="onDblClick" @dblclick.stop="onDblClick"
@click.stop="onClick" @click.stop="onClick"
> >
<div v-e="['a:view:open', { view: vModel.type }]" class="text-xs flex items-center w-full gap-2" data-nc="view-item"> <div v-e="['a:view:open', { view: vModel.type }]" class="text-xs flex items-center w-full gap-2" data-testid="view-item">
<div class="flex w-auto" :data-nc="`view-sidebar-drag-handle-${vModel.alias || vModel.title}`"> <div class="flex w-auto" :data-testid="`view-sidebar-drag-handle-${vModel.alias || vModel.title}`">
<MdiDrag <MdiDrag
class="nc-drag-icon hidden group-hover:block transition-opacity opacity-0 group-hover:opacity-100 text-gray-500 !cursor-move" class="nc-drag-icon hidden group-hover:block transition-opacity opacity-0 group-hover:opacity-100 text-gray-500 !cursor-move"
@click.stop.prevent @click.stop.prevent
@ -194,7 +194,7 @@ function onStopEdit() {
<div class="flex-1" /> <div class="flex-1" />
<template v-if="!isEditing && !isLocked && isUIAllowed('virtualViewsCreateOrEdit')"> <template v-if="!isEditing && !isLocked && isUIAllowed('virtualViewsCreateOrEdit')">
<div class="flex items-center gap-1" :data-nc="`view-sidebar-view-actions-${vModel.alias || vModel.title}`"> <div class="flex items-center gap-1" :data-testid="`view-sidebar-view-actions-${vModel.alias || vModel.title}`">
<a-tooltip placement="left"> <a-tooltip placement="left">
<template #title> <template #title>
{{ $t('activity.copyView') }} {{ $t('activity.copyView') }}

2
packages/nc-gui/components/smartsheet/toolbar/ColumnFilterMenu.vue

@ -82,7 +82,7 @@ const filterAutoSaveLoc = computed({
ref="filterComp" ref="filterComp"
class="nc-table-toolbar-menu shadow-lg" class="nc-table-toolbar-menu shadow-lg"
:auto-save="filterAutoSave" :auto-save="filterAutoSave"
data-nc="nc-filter-menu" data-testid="nc-filter-menu"
@update:filters-length="filtersLength = $event" @update:filters-length="filtersLength = $event"
> >
<div v-if="!isPublic" class="flex items-end mt-2 min-h-[30px]" @click.stop> <div v-if="!isPublic" class="flex items-end mt-2 min-h-[30px]" @click.stop>

4
packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue

@ -139,7 +139,7 @@ const getIcon = (c: ColumnType) =>
<template #overlay> <template #overlay>
<div <div
class="p-3 min-w-[280px] bg-gray-50 shadow-lg nc-table-toolbar-menu max-h-[max(80vh,500px)] overflow-auto !border" class="p-3 min-w-[280px] bg-gray-50 shadow-lg nc-table-toolbar-menu max-h-[max(80vh,500px)] overflow-auto !border"
data-nc="nc-fields-menu" data-testid="nc-fields-menu"
@click.stop @click.stop
> >
<a-card <a-card
@ -167,7 +167,7 @@ const getIcon = (c: ColumnType) =>
v-show="filteredFieldList.includes(field)" v-show="filteredFieldList.includes(field)"
:key="field.id" :key="field.id"
class="px-2 py-1 flex items-center" class="px-2 py-1 flex items-center"
:data-nc="`nc-fields-menu-${field.title}`" :data-testid="`nc-fields-menu-${field.title}`"
@click.stop @click.stop
> >
<a-checkbox <a-checkbox

26
packages/nc-gui/components/smartsheet/toolbar/ShareView.vue

@ -212,8 +212,7 @@ watch(passwordProtected, (value) => {
wrap-class-name="nc-modal-share-view" wrap-class-name="nc-modal-share-view"
> >
<div <div
data-cy="nc-modal-share-view__link" data-testid="nc-modal-share-view__link"
data-nc="nc-modal-share-view__link"
class="share-link-box !bg-primary !bg-opacity-5 ring-1 ring-accent ring-opacity-100" class="share-link-box !bg-primary !bg-opacity-5 ring-1 ring-accent ring-opacity-100"
> >
<div class="flex-1 h-min text-xs">{{ sharedViewUrl }}</div> <div class="flex-1 h-min text-xs">{{ sharedViewUrl }}</div>
@ -235,8 +234,7 @@ watch(passwordProtected, (value) => {
<a-checkbox <a-checkbox
v-if="shared.type === ViewTypes.FORM" v-if="shared.type === ViewTypes.FORM"
v-model:checked="surveyMode" v-model:checked="surveyMode"
data-cy="nc-modal-share-view__survey-mode" data-testid="nc-modal-share-view__survey-mode"
data-nc="nc-modal-share-view__survey-mode"
class="!text-sm" class="!text-sm"
> >
Use Survey Mode Use Survey Mode
@ -250,7 +248,7 @@ watch(passwordProtected, (value) => {
</template> </template>
<a-input <a-input
v-model:value="transitionDuration" v-model:value="transitionDuration"
data-cy="nc-form-signin__email" data-testid="nc-form-signin__email"
size="small" size="small"
class="!w-32" class="!w-32"
type="number" type="number"
@ -266,8 +264,7 @@ watch(passwordProtected, (value) => {
<a-checkbox <a-checkbox
v-if="shared.type === ViewTypes.FORM" v-if="shared.type === ViewTypes.FORM"
v-model:checked="viewTheme" v-model:checked="viewTheme"
data-cy="nc-modal-share-view__with-theme" data-testid="nc-modal-share-view__with-theme"
data-nc="nc-modal-share-view__with-theme"
class="!text-sm" class="!text-sm"
> >
Use Theme Use Theme
@ -276,13 +273,12 @@ watch(passwordProtected, (value) => {
<Transition name="layout" mode="out-in"> <Transition name="layout" mode="out-in">
<div v-if="viewTheme" class="flex pl-6"> <div v-if="viewTheme" class="flex pl-6">
<LazyGeneralColorPicker <LazyGeneralColorPicker
data-cy="nc-modal-share-view__theme-picker" data-testid="nc-modal-share-view__theme-picker"
class="!p-0" class="!p-0"
:model-value="shared.meta.theme?.primaryColor" :model-value="shared.meta.theme?.primaryColor"
:colors="projectThemeColors" :colors="projectThemeColors"
:row-size="9" :row-size="9"
:advanced="false" :advanced="false"
data-nc="nc-modal-share-view__theme-picker"
@input="onChangeTheme" @input="onChangeTheme"
/> />
</div> </div>
@ -293,9 +289,8 @@ watch(passwordProtected, (value) => {
<!-- Password Protection --> <!-- Password Protection -->
<a-checkbox <a-checkbox
v-model:checked="passwordProtected" v-model:checked="passwordProtected"
data-cy="nc-modal-share-view__with-password" data-testid="nc-modal-share-view__with-password"
class="!text-sm !my-1" class="!text-sm !my-1"
data-nc="nc-modal-share-view__with-password"
> >
{{ $t('msg.info.beforeEnablePwd') }} {{ $t('msg.info.beforeEnablePwd') }}
</a-checkbox> </a-checkbox>
@ -304,8 +299,7 @@ watch(passwordProtected, (value) => {
<div v-if="passwordProtected" class="pl-6 flex gap-2 mt-2 mb-4"> <div v-if="passwordProtected" class="pl-6 flex gap-2 mt-2 mb-4">
<a-input <a-input
v-model:value="shared.password" v-model:value="shared.password"
data-cy="nc-modal-share-view__password" data-testid="nc-modal-share-view__password"
data-nc="nc-modal-share-view__password"
size="small" size="small"
class="!text-xs max-w-[250px]" class="!text-xs max-w-[250px]"
type="password" type="password"
@ -313,8 +307,7 @@ watch(passwordProtected, (value) => {
/> />
<a-button <a-button
data-cy="nc-modal-share-view__save-password" data-testid="nc-modal-share-view__save-password"
data-nc="nc-modal-share-view__save-password"
size="small" size="small"
class="!text-xs" class="!text-xs"
@click="saveShareLinkPassword" @click="saveShareLinkPassword"
@ -333,8 +326,7 @@ watch(passwordProtected, (value) => {
(shared.type === ViewTypes.GRID || shared.type === ViewTypes.KANBAN || shared.type === ViewTypes.GALLERY) (shared.type === ViewTypes.GRID || shared.type === ViewTypes.KANBAN || shared.type === ViewTypes.GALLERY)
" "
v-model:checked="allowCSVDownload" v-model:checked="allowCSVDownload"
data-cy="nc-modal-share-view__with-csv-download" data-testid="nc-modal-share-view__with-csv-download"
data-nc="nc-modal-share-view__with-csv-download"
class="!text-sm" class="!text-sm"
> >
{{ $t('labels.downloadAllowed') }} {{ $t('labels.downloadAllowed') }}

2
packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue

@ -55,7 +55,7 @@ watch(
<template #overlay> <template #overlay>
<div <div
class="bg-gray-50 p-6 shadow-lg menu-filter-dropdown min-w-[400px] max-h-[max(80vh,500px)] overflow-auto !border" class="bg-gray-50 p-6 shadow-lg menu-filter-dropdown min-w-[400px] max-h-[max(80vh,500px)] overflow-auto !border"
data-nc="nc-sorts-menu" data-testid="nc-sorts-menu"
> >
<div v-if="sorts?.length" class="sort-grid mb-2" @click.stop> <div v-if="sorts?.length" class="sort-grid mb-2" @click.stop>
<template v-for="(sort, i) in sorts || []" :key="i"> <template v-for="(sort, i) in sorts || []" :key="i">

2
packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue

@ -103,7 +103,7 @@ const { isSqlView } = useSmartsheetStoreOrThrow()
</a-button> </a-button>
<template #overlay> <template #overlay>
<a-menu class="ml-6 !text-sm !px-0 !py-2 !rounded" data-nc="toolbar-actions"> <a-menu class="ml-6 !text-sm !px-0 !py-2 !rounded" data-testid="toolbar-actions">
<a-menu-item-group> <a-menu-item-group>
<a-sub-menu <a-sub-menu
v-if="isUIAllowed('view-type')" v-if="isUIAllowed('view-type')"

2
packages/nc-gui/components/tabs/auth/user-management/ShareBase.vue

@ -136,7 +136,7 @@ onMounted(() => {
</script> </script>
<template> <template>
<div class="flex flex-col w-full" data-nc="nc-share-base-sub-modal"> <div class="flex flex-col w-full" data-testid="nc-share-base-sub-modal">
<div class="flex flex-row items-center space-x-0.5 pl-2 h-[0.8rem]"> <div class="flex flex-row items-center space-x-0.5 pl-2 h-[0.8rem]">
<MdiOpenInNew /> <MdiOpenInNew />

13
packages/nc-gui/components/virtual-cell/components/ListChildItems.vue

@ -102,14 +102,19 @@ watch(
<div class="max-h-[max(calc(100vh_-_300px)_,500px)] flex flex-col py-6"> <div class="max-h-[max(calc(100vh_-_300px)_,500px)] flex flex-col py-6">
<div class="flex mb-4 items-center gap-2 px-12"> <div class="flex mb-4 items-center gap-2 px-12">
<div class="flex-1" /> <div class="flex-1" />
<MdiReload v-if="!isForm" class="cursor-pointer text-gray-500" data-cy="nc-child-list-reload" @click="loadChildrenList" /> <MdiReload
v-if="!isForm"
class="cursor-pointer text-gray-500"
data-testid="nc-child-list-reload"
@click="loadChildrenList"
/>
<a-button <a-button
v-if="!readonly" v-if="!readonly"
type="primary" type="primary"
ghost ghost
class="!text-xs" class="!text-xs"
data-cy="nc-child-list-button-link-to" data-testid="nc-child-list-button-link-to"
size="small" size="small"
@click="emit('attachRecord')" @click="emit('attachRecord')"
> >
@ -143,13 +148,13 @@ watch(
<div v-if="!readonly" class="flex gap-2"> <div v-if="!readonly" class="flex gap-2">
<MdiLinkVariantRemove <MdiLinkVariantRemove
class="text-xs text-grey hover:(!text-red-500) cursor-pointer" class="text-xs text-grey hover:(!text-red-500) cursor-pointer"
data-cy="nc-child-list-icon-unlink" data-testid="nc-child-list-icon-unlink"
@click.stop="unlinkRow(row)" @click.stop="unlinkRow(row)"
/> />
<MdiDeleteOutline <MdiDeleteOutline
v-if="!readonly && !isPublic" v-if="!readonly && !isPublic"
class="text-xs text-grey hover:(!text-red-500) cursor-pointer" class="text-xs text-grey hover:(!text-red-500) cursor-pointer"
data-cy="nc-child-list-icon-delete" data-testid="nc-child-list-icon-delete"
@click.stop="deleteRelatedRow(row, unlinkIfNewRow)" @click.stop="deleteRelatedRow(row, unlinkIfNewRow)"
/> />
</div> </div>

8
packages/nc-gui/layouts/base.vue

@ -44,7 +44,7 @@ hooks.hook('page:finish', () => {
<div <div
v-if="!route.params.projectType" v-if="!route.params.projectType"
v-e="['c:navbar:home']" v-e="['c:navbar:home']"
data-cy="nc-noco-brand-icon" data-testid="nc-noco-brand-icon"
class="transition-all duration-200 p-2 cursor-pointer transform hover:scale-105 nc-noco-brand-icon" class="transition-all duration-200 p-2 cursor-pointer transform hover:scale-105 nc-noco-brand-icon"
@click="navigateTo('/')" @click="navigateTo('/')"
> >
@ -57,7 +57,7 @@ hooks.hook('page:finish', () => {
</div> </div>
<div class="!text-white flex justify-center"> <div class="!text-white flex justify-center">
<div v-show="isLoading" class="flex items-center gap-2 ml-3" data-nc="nc-loading"> <div v-show="isLoading" class="flex items-center gap-2 ml-3" data-testid="nc-loading">
{{ $t('general.loading') }} {{ $t('general.loading') }}
<MdiReload :class="{ 'animate-infinite animate-spin': isLoading }" /> <MdiReload :class="{ 'animate-infinite animate-spin': isLoading }" />
@ -79,14 +79,14 @@ hooks.hook('page:finish', () => {
<template v-if="signedIn"> <template v-if="signedIn">
<a-dropdown :trigger="['click']" overlay-class-name="nc-dropdown-user-accounts-menu"> <a-dropdown :trigger="['click']" overlay-class-name="nc-dropdown-user-accounts-menu">
<MdiDotsVertical <MdiDotsVertical
data-cy="nc-menu-accounts" data-testid="nc-menu-accounts"
class="md:text-xl cursor-pointer hover:text-accent nc-menu-accounts text-white" class="md:text-xl cursor-pointer hover:text-accent nc-menu-accounts text-white"
@click.prevent @click.prevent
/> />
<template #overlay> <template #overlay>
<a-menu class="!py-0 leading-8 !rounded"> <a-menu class="!py-0 leading-8 !rounded">
<a-menu-item key="0" data-cy="nc-menu-accounts__user-settings" class="!rounded-t"> <a-menu-item key="0" data-testid="nc-menu-accounts__user-settings" class="!rounded-t">
<nuxt-link v-e="['c:navbar:user:email']" class="nc-project-menu-item group !no-underline" to="/user"> <nuxt-link v-e="['c:navbar:user:email']" class="nc-project-menu-item group !no-underline" to="/user">
<MdiAt class="mt-1 group-hover:text-accent" />&nbsp; <MdiAt class="mt-1 group-hover:text-accent" />&nbsp;

2
packages/nc-gui/layouts/shared-view.vue

@ -53,7 +53,7 @@ export default {
<div class="flex justify-center items-center"> <div class="flex justify-center items-center">
<div class="flex items-center gap-2 ml-3 text-white"> <div class="flex items-center gap-2 ml-3 text-white">
<template v-if="isLoading"> <template v-if="isLoading">
<span class="text-white" data-nc="nc-loading">{{ $t('general.loading') }}</span> <span class="text-white" data-testid="nc-loading">{{ $t('general.loading') }}</span>
<MdiReload :class="{ 'animate-infinite animate-spin ': isLoading }" /> <MdiReload :class="{ 'animate-infinite animate-spin ': isLoading }" />
</template> </template>

4
packages/nc-gui/pages/[projectType]/[projectId]/index.vue

@ -194,7 +194,7 @@ onBeforeUnmount(reset)
<div <div
v-if="isOpen && !isSharedBase" v-if="isOpen && !isSharedBase"
v-e="['c:navbar:home']" v-e="['c:navbar:home']"
data-cy="nc-noco-brand-icon" data-testid="nc-noco-brand-icon"
class="w-[40px] min-w-[40px] transition-all duration-200 p-1 cursor-pointer transform hover:scale-105 nc-noco-brand-icon" class="w-[40px] min-w-[40px] transition-all duration-200 p-1 cursor-pointer transform hover:scale-105 nc-noco-brand-icon"
@click="navigateTo('/')" @click="navigateTo('/')"
> >
@ -229,7 +229,7 @@ onBeforeUnmount(reset)
<div <div
:style="{ width: isOpen ? 'calc(100% - 40px) pr-2' : '100%' }" :style="{ width: isOpen ? 'calc(100% - 40px) pr-2' : '100%' }"
:class="[isOpen ? '' : 'justify-center']" :class="[isOpen ? '' : 'justify-center']"
data-nc="nc-project-menu" data-testid="nc-project-menu"
class="group cursor-pointer flex gap-1 items-center nc-project-menu overflow-hidden" class="group cursor-pointer flex gap-1 items-center nc-project-menu overflow-hidden"
> >
<template v-if="isOpen"> <template v-if="isOpen">

6
packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue

@ -52,14 +52,14 @@ function onEdit(targetKey: number, action: 'add' | 'remove' | string) {
</div> </div>
<a-tooltip v-if="tab.title?.length > 12" placement="bottom"> <a-tooltip v-if="tab.title?.length > 12" placement="bottom">
<div class="truncate" :data-nc="`nc-root-tabs-${tab.title}`">{{ tab.title }}</div> <div class="truncate" :data-testid="`nc-root-tabs-${tab.title}`">{{ tab.title }}</div>
<template #title> <template #title>
<div>{{ tab.title }}</div> <div>{{ tab.title }}</div>
</template> </template>
</a-tooltip> </a-tooltip>
<div v-else :data-nc="`nc-root-tabs-${tab.title}`">{{ tab.title }}</div> <div v-else :data-testid="`nc-root-tabs-${tab.title}`">{{ tab.title }}</div>
</div> </div>
</template> </template>
</a-tab-pane> </a-tab-pane>
@ -68,7 +68,7 @@ function onEdit(targetKey: number, action: 'add' | 'remove' | string) {
<span class="flex-1" /> <span class="flex-1" />
<div class="flex justify-center self-center mr-2 min-w-[115px]"> <div class="flex justify-center self-center mr-2 min-w-[115px]">
<div v-show="isLoading" class="flex items-center gap-2 ml-3 text-gray-200" data-nc="nc-loading"> <div v-show="isLoading" class="flex items-center gap-2 ml-3 text-gray-200" data-testid="nc-loading">
{{ $t('general.loading') }} {{ $t('general.loading') }}
<MdiLoading class="animate-infinite animate-spin" /> <MdiLoading class="animate-infinite animate-spin" />

6
packages/nc-gui/pages/[projectType]/form/[viewId]/index/index.vue

@ -85,7 +85,7 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
<LazySmartsheetVirtualCell <LazySmartsheetVirtualCell
v-if="isVirtualCol(field)" v-if="isVirtualCol(field)"
class="mt-0 nc-input" class="mt-0 nc-input"
:data-nc="`nc-form-input-cell-${field.label || field.title}`" :data-testid="`nc-form-input-cell-${field.label || field.title}`"
:class="`nc-form-input-${field.title.replaceAll(' ', '')}`" :class="`nc-form-input-${field.title.replaceAll(' ', '')}`"
:column="field" :column="field"
/> />
@ -94,7 +94,7 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
v-else v-else
v-model="formState[field.title]" v-model="formState[field.title]"
class="nc-input" class="nc-input"
:data-nc="`nc-form-input-cell-${field.label || field.title}`" :data-testid="`nc-form-input-cell-${field.label || field.title}`"
:class="`nc-form-input-${field.title.replaceAll(' ', '')}`" :class="`nc-form-input-${field.title.replaceAll(' ', '')}`"
:column="field" :column="field"
:edit-enabled="true" :edit-enabled="true"
@ -115,7 +115,7 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
<button <button
type="submit" type="submit"
class="uppercase scaling-btn prose-sm" class="uppercase scaling-btn prose-sm"
data-nc="shared-form-submit-button" data-testid="shared-form-submit-button"
@click="submitForm" @click="submitForm"
> >
{{ $t('general.submit') }} {{ $t('general.submit') }}

43
packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue

@ -208,15 +208,14 @@ onMounted(() => {
class="max-w-[max(33%,600px)] mx-auto flex flex-col justify-end" class="max-w-[max(33%,600px)] mx-auto flex flex-col justify-end"
> >
<div class="px-4 md:px-0 flex flex-col justify-end"> <div class="px-4 md:px-0 flex flex-col justify-end">
<h1 class="prose-2xl font-bold self-center my-4" data-cy="nc-survey-form__heading" data-nc="nc-survey-form__heading"> <h1 class="prose-2xl font-bold self-center my-4" data-testid="nc-survey-form__heading">
{{ sharedFormView.heading }} {{ sharedFormView.heading }}
</h1> </h1>
<h2 <h2
v-if="sharedFormView.subheading && sharedFormView.subheading !== ''" v-if="sharedFormView.subheading && sharedFormView.subheading !== ''"
class="prose-lg text-slate-500 dark:text-slate-300 self-center mb-4 leading-6" class="prose-lg text-slate-500 dark:text-slate-300 self-center mb-4 leading-6"
data-cy="nc-survey-form__sub-heading" data-testid="nc-survey-form__sub-heading"
data-nc="nc-survey-form__sub-heading"
> >
{{ sharedFormView?.subheading }} {{ sharedFormView?.subheading }}
</h2> </h2>
@ -231,7 +230,7 @@ onMounted(() => {
class="color-transition h-full flex flex-col mt-6 gap-4 w-full max-w-[max(33%,600px)] m-auto" class="color-transition h-full flex flex-col mt-6 gap-4 w-full max-w-[max(33%,600px)] m-auto"
> >
<div v-if="field && !submitted" class="flex flex-col gap-2"> <div v-if="field && !submitted" class="flex flex-col gap-2">
<div class="flex nc-form-column-label" data-nc="nc-form-column-label"> <div class="flex nc-form-column-label" data-testid="nc-form-column-label">
<LazySmartsheetHeaderVirtualCell <LazySmartsheetHeaderVirtualCell
v-if="isVirtualCol(field)" v-if="isVirtualCol(field)"
:column="{ ...field, title: field.label || field.title }" :column="{ ...field, title: field.label || field.title }"
@ -254,8 +253,7 @@ onMounted(() => {
v-model="formState[field.title]" v-model="formState[field.title]"
class="mt-0 nc-input" class="mt-0 nc-input"
:row="{ row: {}, oldRow: {}, rowMeta: {} }" :row="{ row: {}, oldRow: {}, rowMeta: {} }"
:data-cy="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`" :data-testid="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`"
:data-nc="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`"
:column="field" :column="field"
/> />
@ -263,8 +261,7 @@ onMounted(() => {
v-else v-else
v-model="formState[field.title]" v-model="formState[field.title]"
class="nc-input" class="nc-input"
:data-cy="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`" :data-testid="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`"
:data-nc="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`"
:column="field" :column="field"
:edit-enabled="true" :edit-enabled="true"
/> />
@ -277,8 +274,7 @@ onMounted(() => {
<div <div
class="block text-[14px]" class="block text-[14px]"
:class="field.uidt === UITypes.Checkbox ? 'text-center' : ''" :class="field.uidt === UITypes.Checkbox ? 'text-center' : ''"
data-nc="nc-survey-form__field-description" data-testid="nc-survey-form__field-description"
data-cy="nc-survey-form__field-description"
> >
{{ field.description }} {{ field.description }}
</div> </div>
@ -302,8 +298,7 @@ onMounted(() => {
" "
type="submit" type="submit"
class="uppercase scaling-btn prose-sm" class="uppercase scaling-btn prose-sm"
data-cy="nc-survey-form__btn-submit" data-testid="nc-survey-form__btn-submit"
data-nc="nc-survey-form__btn-submit"
@click="submit" @click="submit"
> >
{{ $t('general.submit') }} {{ $t('general.submit') }}
@ -318,8 +313,7 @@ onMounted(() => {
> >
<button <button
class="bg-opacity-100 scaling-btn flex items-center gap-1" class="bg-opacity-100 scaling-btn flex items-center gap-1"
data-cy="nc-survey-form__btn-next" data-testid="nc-survey-form__btn-next"
data-nc="nc-survey-form__btn-next"
:class="[ :class="[
v$.localState[field.title]?.$error ? 'after:!bg-gray-100 after:!ring-red-500' : '', v$.localState[field.title]?.$error ? 'after:!bg-gray-100 after:!ring-red-500' : '',
animationTarget === AnimationTarget.OkButton && isAnimating animationTarget === AnimationTarget.OkButton && isAnimating
@ -349,11 +343,7 @@ onMounted(() => {
<Transition name="slide-left"> <Transition name="slide-left">
<div v-if="submitted" class="flex flex-col justify-center items-center text-center"> <div v-if="submitted" class="flex flex-col justify-center items-center text-center">
<div <div class="text-lg px-6 py-3 bg-green-300 text-gray-700 rounded" data-testid="nc-survey-form__success-msg">
class="text-lg px-6 py-3 bg-green-300 text-gray-700 rounded"
data-cy="nc-survey-form__success-msg"
data-nc="nc-survey-form__success-msg"
>
<template v-if="sharedFormView?.success_msg"> <template v-if="sharedFormView?.success_msg">
{{ sharedFormView?.success_msg }} {{ sharedFormView?.success_msg }}
</template> </template>
@ -376,8 +366,7 @@ onMounted(() => {
<button <button
type="button" type="button"
class="scaling-btn bg-opacity-100" class="scaling-btn bg-opacity-100"
data-cy="nc-survey-form__btn-submit-another-form" data-testid="nc-survey-form__btn-submit-another-form"
data-nc="nc-survey-form__btn-submit-another-form"
@click="resetForm" @click="resetForm"
> >
Submit Another Form Submit Another Form
@ -391,11 +380,7 @@ onMounted(() => {
</div> </div>
<template v-if="!submitted"> <template v-if="!submitted">
<div <div class="mb-24 md:my-4 select-none text-center text-gray-500 dark:text-slate-200" data-testid="nc-survey-form__footer">
class="mb-24 md:my-4 select-none text-center text-gray-500 dark:text-slate-200"
data-cy="nc-survey-form__footer"
data-nc="nc-survey-form__footer"
>
{{ index + 1 }} / {{ formColumns?.length }} {{ index + 1 }} / {{ formColumns?.length }}
</div> </div>
</template> </template>
@ -414,8 +399,7 @@ onMounted(() => {
: '' : ''
" "
class="p-0.5 flex items-center group color-transition" class="p-0.5 flex items-center group color-transition"
data-cy="nc-survey-form__icon-prev" data-testid="nc-survey-form__icon-prev"
data-nc="nc-survey-form__icon-prev"
@click="goPrevious()" @click="goPrevious()"
> >
<MdiChevronLeft :class="isFirst ? 'text-gray-300' : 'group-hover:text-accent'" class="text-2xl md:text-md" /> <MdiChevronLeft :class="isFirst ? 'text-gray-300' : 'group-hover:text-accent'" class="text-2xl md:text-md" />
@ -434,8 +418,7 @@ onMounted(() => {
: '' : ''
" "
class="p-0.5 flex items-center group color-transition" class="p-0.5 flex items-center group color-transition"
data-cy="nc-survey-form__icon-next" data-testid="nc-survey-form__icon-next"
data-nc="nc-survey-form__icon-next"
@click="goNext()" @click="goNext()"
> >
<MdiChevronRight <MdiChevronRight

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

@ -144,7 +144,7 @@ const copyProjectMeta = async () => {
<template> <template>
<div <div
class="relative flex flex-col justify-center gap-2 w-full p-8 md:(bg-white rounded-lg border-1 border-gray-200 shadow)" class="relative flex flex-col justify-center gap-2 w-full p-8 md:(bg-white rounded-lg border-1 border-gray-200 shadow)"
data-nc="projects-container" data-testid="projects-container"
> >
<h1 class="flex items-center justify-center gap-2 leading-8 mb-8 mt-4"> <h1 class="flex items-center justify-center gap-2 leading-8 mb-8 mt-4">
<span class="text-4xl nc-project-page-title" @dblclick="copyProjectMeta">{{ $t('title.myProject') }}</span> <span class="text-4xl nc-project-page-title" @dblclick="copyProjectMeta">{{ $t('title.myProject') }}</span>
@ -166,7 +166,7 @@ const copyProjectMeta = async () => {
v-e="['a:project:refresh']" v-e="['a:project:refresh']"
class="text-xl text-gray-500 group-hover:text-accent cursor-pointer" class="text-xl text-gray-500 group-hover:text-accent cursor-pointer"
:class="isLoading ? '!text-primary' : ''" :class="isLoading ? '!text-primary' : ''"
data-nc="projects-reload-button" data-testid="projects-reload-button"
@click="loadProjects" @click="loadProjects"
/> />
</div> </div>
@ -290,7 +290,7 @@ const copyProjectMeta = async () => {
<MdiDeleteOutline <MdiDeleteOutline
class="nc-action-btn" class="nc-action-btn"
:data-nc="`delete-project-${record.title}`" :data-testid="`delete-project-${record.title}`"
@click.stop="deleteProject(record)" @click.stop="deleteProject(record)"
/> />
</div> </div>

12
packages/nc-gui/pages/index/index/user.vue

@ -82,7 +82,7 @@ const resetError = () => {
<a-form <a-form
ref="formValidator" ref="formValidator"
data-cy="nc-user-settings-form" data-testid="nc-user-settings-form"
layout="vertical" layout="vertical"
class="change-password lg:max-w-3/4 w-full !mx-auto" class="change-password lg:max-w-3/4 w-full !mx-auto"
no-style no-style
@ -91,7 +91,7 @@ const resetError = () => {
> >
<Transition name="layout"> <Transition name="layout">
<div v-if="error" class="mx-auto mb-4 bg-red-500 text-white rounded-lg w-3/4 p-1"> <div v-if="error" class="mx-auto mb-4 bg-red-500 text-white rounded-lg w-3/4 p-1">
<div data-cy="nc-user-settings-form__error" class="flex items-center gap-2 justify-center"> <div data-testid="nc-user-settings-form__error" class="flex items-center gap-2 justify-center">
<MaterialSymbolsWarning /> <MaterialSymbolsWarning />
{{ error }} {{ error }}
</div> </div>
@ -101,7 +101,7 @@ const resetError = () => {
<a-form-item :label="$t('placeholder.password.current')" name="currentPassword" :rules="formRules.currentPassword"> <a-form-item :label="$t('placeholder.password.current')" name="currentPassword" :rules="formRules.currentPassword">
<a-input-password <a-input-password
v-model:value="form.currentPassword" v-model:value="form.currentPassword"
data-cy="nc-user-settings-form__current-password" data-testid="nc-user-settings-form__current-password"
size="large" size="large"
class="password" class="password"
:placeholder="$t('placeholder.password.current')" :placeholder="$t('placeholder.password.current')"
@ -112,7 +112,7 @@ const resetError = () => {
<a-form-item :label="$t('placeholder.password.new')" name="password" :rules="formRules.password"> <a-form-item :label="$t('placeholder.password.new')" name="password" :rules="formRules.password">
<a-input-password <a-input-password
v-model:value="form.password" v-model:value="form.password"
data-cy="nc-user-settings-form__new-password" data-testid="nc-user-settings-form__new-password"
size="large" size="large"
class="password" class="password"
:placeholder="$t('placeholder.password.new')" :placeholder="$t('placeholder.password.new')"
@ -123,7 +123,7 @@ const resetError = () => {
<a-form-item :label="$t('placeholder.password.confirm')" name="passwordRepeat" :rules="formRules.passwordRepeat"> <a-form-item :label="$t('placeholder.password.confirm')" name="passwordRepeat" :rules="formRules.passwordRepeat">
<a-input-password <a-input-password
v-model:value="form.passwordRepeat" v-model:value="form.passwordRepeat"
data-cy="nc-user-settings-form__new-password-repeat" data-testid="nc-user-settings-form__new-password-repeat"
size="large" size="large"
class="password" class="password"
:placeholder="$t('placeholder.password.confirm')" :placeholder="$t('placeholder.password.confirm')"
@ -132,7 +132,7 @@ const resetError = () => {
</a-form-item> </a-form-item>
<div class="text-center"> <div class="text-center">
<button data-cy="nc-user-settings-form__submit" class="scaling-btn bg-opacity-100" type="submit"> <button data-testid="nc-user-settings-form__submit" class="scaling-btn bg-opacity-100" type="submit">
<span class="flex items-center gap-2"> <span class="flex items-center gap-2">
<MdiKeyChange /> <MdiKeyChange />
{{ $t('activity.changePwd') }} {{ $t('activity.changePwd') }}

15
packages/nc-gui/pages/signin.vue

@ -64,7 +64,7 @@ function resetError() {
<template> <template>
<NuxtLayout> <NuxtLayout>
<div <div
data-cy="nc-form-signin" data-testid="nc-form-signin"
class="md:bg-primary bg-opacity-5 signin h-full min-h-[600px] flex flex-col justify-center items-center nc-form-signin" class="md:bg-primary bg-opacity-5 signin h-full min-h-[600px] flex flex-col justify-center items-center nc-form-signin"
> >
<div <div
@ -87,8 +87,7 @@ function resetError() {
<a-form-item :label="$t('labels.email')" name="email" :rules="formRules.email"> <a-form-item :label="$t('labels.email')" name="email" :rules="formRules.email">
<a-input <a-input
v-model:value="form.email" v-model:value="form.email"
data-cy="nc-form-signin__email" data-testid="nc-form-signin__email"
data-nc="nc-form-signin__email"
size="large" size="large"
:placeholder="$t('msg.info.signUp.workEmail')" :placeholder="$t('msg.info.signUp.workEmail')"
@focus="resetError" @focus="resetError"
@ -98,8 +97,7 @@ function resetError() {
<a-form-item :label="$t('labels.password')" name="password" :rules="formRules.password"> <a-form-item :label="$t('labels.password')" name="password" :rules="formRules.password">
<a-input-password <a-input-password
v-model:value="form.password" v-model:value="form.password"
data-cy="nc-form-signin__password" data-testid="nc-form-signin__password"
data-nc="nc-form-signin__password"
size="large" size="large"
class="password" class="password"
:placeholder="$t('msg.info.signUp.enterPassword')" :placeholder="$t('msg.info.signUp.enterPassword')"
@ -114,12 +112,7 @@ function resetError() {
</div> </div>
<div class="self-center flex flex-col flex-wrap gap-4 items-center mt-4 justify-center"> <div class="self-center flex flex-col flex-wrap gap-4 items-center mt-4 justify-center">
<button <button data-testid="nc-form-signin__submit" class="scaling-btn bg-opacity-100" type="submit">
data-cy="nc-form-signin__submit"
data-nc="nc-form-signin__submit"
class="scaling-btn bg-opacity-100"
type="submit"
>
<span class="flex items-center gap-2"> <span class="flex items-center gap-2">
<MdiLogin /> <MdiLogin />
{{ $t('general.signIn') }} {{ $t('general.signIn') }}

0
packages/nc-gui/tests/playwright/.env.example → tests/playwright/.env.example

0
packages/nc-gui/tests/playwright/.eslintrc.json → tests/playwright/.eslintrc.json

0
packages/nc-gui/tests/playwright/.gitignore → tests/playwright/.gitignore vendored

0
packages/nc-gui/tests/playwright/.lintstagedrc.json → tests/playwright/.lintstagedrc.json

0
packages/nc-gui/tests/playwright/.prettierignore → tests/playwright/.prettierignore

0
packages/nc-gui/tests/playwright/.prettierrc.js → tests/playwright/.prettierrc.js

8
packages/nc-gui/tests/playwright/README.md → tests/playwright/README.md

@ -2,7 +2,7 @@
## Setup ## Setup
Make sure to install the dependencies(in the playwright folder): Make sure to install the dependencies(in the playwright folder, which is `./tests/playwright`):
```bash ```bash
npm install npm install
@ -47,13 +47,13 @@ npm run test:debug
For setting up mysql: For setting up mysql:
```bash ```bash
docker-compose -f ./packages/nc-gui/tests/playwright/scripts/docker-compose-mysql-playwright.yml up -d docker-compose -f ./tests/playwright/scripts/docker-compose-mysql-playwright.yml up -d
``` ```
For setting up postgres: For setting up postgres:
```bash ```bash
docker-compose -f ./packages/nc-gui/tests/playwright/scripts/docker-compose-playwright-pg.yml docker-compose -f ./tests/playwright/scripts/docker-compose-playwright-pg.yml
``` ```
### Running individual tests ### Running individual tests
@ -93,7 +93,7 @@ It will have run button beside each test in the file.
- Do not add any logic to the tests. Instead, create a page object for the page you are testing. - Do not add any logic to the tests. Instead, create a page object for the page you are testing.
All the selection, UI actions and assertions should be in the page object. All the selection, UI actions and assertions should be in the page object.
Page objects should be in `packages/nc-gui/tests/playwright/pages` folder. Page objects should be in `./tests/playwright/pages` folder.
## Verify if tests are not flaky ## Verify if tests are not flaky

0
packages/nc-gui/tests/playwright/constants/index.ts → tests/playwright/constants/index.ts

0
packages/nc-gui/tests/playwright/fixtures/expectedBaseDownloadData.txt → tests/playwright/fixtures/expectedBaseDownloadData.txt

0
packages/nc-gui/tests/playwright/fixtures/expectedBaseDownloadDataPg.txt → tests/playwright/fixtures/expectedBaseDownloadDataPg.txt

0
packages/nc-gui/tests/playwright/fixtures/expectedData.txt → tests/playwright/fixtures/expectedData.txt

0
packages/nc-gui/tests/playwright/fixtures/expectedDataSqlite.txt → tests/playwright/fixtures/expectedDataSqlite.txt

0
packages/nc-gui/tests/playwright/fixtures/sampleFiles/1.json → tests/playwright/fixtures/sampleFiles/1.json

0
packages/nc-gui/tests/playwright/fixtures/sampleFiles/2.json → tests/playwright/fixtures/sampleFiles/2.json

0
packages/nc-gui/tests/playwright/fixtures/sampleFiles/3.json → tests/playwright/fixtures/sampleFiles/3.json

0
packages/nc-gui/tests/playwright/fixtures/sampleFiles/4.json → tests/playwright/fixtures/sampleFiles/4.json

0
packages/nc-gui/tests/playwright/fixtures/sampleFiles/5.json → tests/playwright/fixtures/sampleFiles/5.json

0
packages/nc-gui/tests/playwright/fixtures/sampleFiles/6.json → tests/playwright/fixtures/sampleFiles/6.json

0
packages/nc-gui/tests/playwright/fixtures/sampleFiles/simple.xlsx → tests/playwright/fixtures/sampleFiles/simple.xlsx

0
packages/nc-gui/tests/playwright/fixtures/template.spec.ts → tests/playwright/fixtures/template.spec.ts

0
packages/nc-gui/tests/playwright/package-lock.json → tests/playwright/package-lock.json generated

1
packages/nc-gui/tests/playwright/package.json → tests/playwright/package.json

@ -5,6 +5,7 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "TRACE=true npx playwright test --workers=4", "test": "TRACE=true npx playwright test --workers=4",
"test:fast": "npx playwright test --workers=6",
"test:shard:1": "TRACE=true npx playwright test --workers=4 --shard=1/2", "test:shard:1": "TRACE=true npx playwright test --workers=4 --shard=1/2",
"test:shard:2": "TRACE=true npx playwright test --workers=4 --shard=2/2", "test:shard:2": "TRACE=true npx playwright test --workers=4 --shard=2/2",
"test:repeat": "TRACE=true npx playwright test --workers=4 --repeat-each=12", "test:repeat": "TRACE=true npx playwright test --workers=4 --repeat-each=12",

0
packages/nc-gui/tests/playwright/pages/Base.ts → tests/playwright/pages/Base.ts

8
packages/nc-gui/tests/playwright/pages/Dashboard/ExpandedForm/index.ts → tests/playwright/pages/Dashboard/ExpandedForm/index.ts

@ -28,7 +28,7 @@ export class ExpandedFormPage extends BasePage {
} }
async fillField({ columnTitle, value, type = 'text' }: { columnTitle: string; value: string; type?: string }) { async fillField({ columnTitle, value, type = 'text' }: { columnTitle: string; value: string; type?: string }) {
const field = this.get().locator(`[data-nc="nc-expand-col-${columnTitle}"]`); const field = this.get().locator(`[data-testid="nc-expand-col-${columnTitle}"]`);
await field.hover(); await field.hover();
switch (type) { switch (type) {
case 'text': case 'text':
@ -40,7 +40,7 @@ export class ExpandedFormPage extends BasePage {
break; break;
case 'hasMany': case 'hasMany':
case 'manyToMany': case 'manyToMany':
await field.locator(`[data-cy="nc-child-list-button-link-to"]`).click(); await field.locator(`[data-testid="nc-child-list-button-link-to"]`).click();
await this.dashboard.linkRecord.select(value); await this.dashboard.linkRecord.select(value);
break; break;
} }
@ -70,7 +70,7 @@ export class ExpandedFormPage extends BasePage {
await this.get().press('Escape'); await this.get().press('Escape');
await this.get().waitFor({ state: 'hidden' }); await this.get().waitFor({ state: 'hidden' });
await this.verifyToast({ message: `updated successfully.` }); await this.verifyToast({ message: `updated successfully.` });
await this.rootPage.locator('[data-nc="grid-load-spinner"]').waitFor({ state: 'hidden' }); await this.rootPage.locator('[data-testid="grid-load-spinner"]').waitFor({ state: 'hidden' });
} }
async verify({ header, url }: { header: string; url: string }) { async verify({ header, url }: { header: string; url: string }) {
@ -87,7 +87,7 @@ export class ExpandedFormPage extends BasePage {
} }
async openChildCard(param: { column: string; title: string }) { async openChildCard(param: { column: string; title: string }) {
const childList = await this.get().locator(`[data-nc="nc-expand-col-${param.column}"]`); const childList = await this.get().locator(`[data-testid="nc-expand-col-${param.column}"]`);
await childList.locator(`.ant-card:has-text("${param.title}")`).click(); await childList.locator(`.ant-card:has-text("${param.title}")`).click();
} }

72
packages/nc-gui/tests/playwright/pages/Dashboard/Form/index.ts → tests/playwright/pages/Dashboard/Form/index.ts

@ -24,61 +24,65 @@ export class FormPage extends BasePage {
this.dashboard = dashboard; this.dashboard = dashboard;
this.toolbar = new ToolbarPage(this); this.toolbar = new ToolbarPage(this);
this.addAllButton = dashboard.get().locator('[data-nc="nc-form-add-all"]'); this.addAllButton = dashboard.get().locator('[data-testid="nc-form-add-all"]');
this.removeAllButton = dashboard.get().locator('[data-nc="nc-form-remove-all"]'); this.removeAllButton = dashboard.get().locator('[data-testid="nc-form-remove-all"]');
this.submitButton = dashboard.get().locator('[data-nc="nc-form-submit"]'); this.submitButton = dashboard.get().locator('[data-testid="nc-form-submit"]');
this.showAnotherFormRadioButton = dashboard.get().locator('[data-nc="nc-form-checkbox-submit-another-form"]'); this.showAnotherFormRadioButton = dashboard.get().locator('[data-testid="nc-form-checkbox-submit-another-form"]');
this.showAnotherFormAfter5SecRadioButton = dashboard.get().locator('[data-nc="nc-form-checkbox-show-blank-form"]'); this.showAnotherFormAfter5SecRadioButton = dashboard
this.emailMeRadioButton = dashboard.get().locator('[data-nc="nc-form-checkbox-send-email"]'); .get()
this.formHeading = dashboard.get().locator('[data-nc="nc-form-heading"]'); .locator('[data-testid="nc-form-checkbox-show-blank-form"]');
this.formSubHeading = dashboard.get().locator('[data-nc="nc-form-sub-heading"]'); this.emailMeRadioButton = dashboard.get().locator('[data-testid="nc-form-checkbox-send-email"]');
this.afterSubmitMsg = dashboard.get().locator('[data-nc="nc-form-after-submit-msg"]'); this.formHeading = dashboard.get().locator('[data-testid="nc-form-heading"]');
this.formSubHeading = dashboard.get().locator('[data-testid="nc-form-sub-heading"]');
this.afterSubmitMsg = dashboard.get().locator('[data-testid="nc-form-after-submit-msg"]');
} }
get() { get() {
return this.dashboard.get().locator('[data-nc="nc-form-wrapper"]'); return this.dashboard.get().locator('[data-testid="nc-form-wrapper"]');
} }
getFormAfterSubmit() { getFormAfterSubmit() {
return this.dashboard.get().locator('[data-nc="nc-form-wrapper-submit"]'); return this.dashboard.get().locator('[data-testid="nc-form-wrapper-submit"]');
} }
getFormHiddenColumn() { getFormHiddenColumn() {
return this.get().locator('[data-nc="nc-form-hidden-column"]'); return this.get().locator('[data-testid="nc-form-hidden-column"]');
} }
getFormFields() { getFormFields() {
return this.get().locator('[data-nc="nc-form-fields"]'); return this.get().locator('[data-testid="nc-form-fields"]');
} }
getDragNDropToHide() { getDragNDropToHide() {
return this.get().locator('[data-nc="nc-drag-n-drop-to-hide"]'); return this.get().locator('[data-testid="nc-drag-n-drop-to-hide"]');
} }
getFormFieldsRemoveIcon() { getFormFieldsRemoveIcon() {
return this.get().locator('[data-nc="nc-field-remove-icon"]'); return this.get().locator('[data-testid="nc-field-remove-icon"]');
} }
getFormFieldsRequired() { getFormFieldsRequired() {
return this.get().locator('[data-nc="nc-form-input-required"]'); return this.get().locator('[data-testid="nc-form-input-required"]');
} }
getFormFieldsInputLabel() { getFormFieldsInputLabel() {
return this.get().locator('input[data-nc="nc-form-input-label"]:visible'); return this.get().locator('input[data-testid="nc-form-input-label"]:visible');
} }
getFormFieldsInputHelpText() { getFormFieldsInputHelpText() {
return this.get().locator('input[data-nc="nc-form-input-help-text"]:visible'); return this.get().locator('input[data-testid="nc-form-input-help-text"]:visible');
} }
async verifyFormFieldLabel({ index, label }: { index: number; label: string }) { async verifyFormFieldLabel({ index, label }: { index: number; label: string }) {
await expect(await this.getFormFields().nth(index).locator('[data-nc="nc-form-input-label"]')).toContainText(label); await expect(await this.getFormFields().nth(index).locator('[data-testid="nc-form-input-label"]')).toContainText(
label
);
} }
async verifyFormFieldHelpText({ index, helpText }: { index: number; helpText: string }) { async verifyFormFieldHelpText({ index, helpText }: { index: number; helpText: string }) {
await expect( await expect(
await this.getFormFields().nth(index).locator('[data-nc="nc-form-input-help-text-label"]') await this.getFormFields().nth(index).locator('[data-testid="nc-form-input-help-text-label"]')
).toContainText(helpText); ).toContainText(helpText);
} }
@ -91,7 +95,7 @@ export class FormPage extends BasePage {
} }
async verifyFormViewFieldsOrder({ fields }: { fields: string[] }) { async verifyFormViewFieldsOrder({ fields }: { fields: string[] }) {
const fieldLabels = await this.get().locator('[data-nc="nc-form-input-label"]'); const fieldLabels = await this.get().locator('[data-testid="nc-form-input-label"]');
await expect(await fieldLabels).toHaveCount(fields.length); await expect(await fieldLabels).toHaveCount(fields.length);
for (let i = 0; i < fields.length; i++) { for (let i = 0; i < fields.length; i++) {
await expect(await fieldLabels.nth(i)).toContainText(fields[i]); await expect(await fieldLabels.nth(i)).toContainText(fields[i]);
@ -109,21 +113,21 @@ export class FormPage extends BasePage {
async removeField({ field, mode }: { mode: string; field: string }) { async removeField({ field, mode }: { mode: string; field: string }) {
if (mode === 'dragDrop') { if (mode === 'dragDrop') {
const src = await this.get().locator(`.nc-form-drag-${field.replace(' ', '')}`); const src = await this.get().locator(`.nc-form-drag-${field.replace(' ', '')}`);
const dst = await this.get().locator(`[data-nc="nc-drag-n-drop-to-hide"]`); const dst = await this.get().locator(`[data-testid="nc-drag-n-drop-to-hide"]`);
await src.dragTo(dst); await src.dragTo(dst);
} else if (mode === 'hideField') { } else if (mode === 'hideField') {
const src = await this.get().locator(`.nc-form-drag-${field.replace(' ', '')}`); const src = await this.get().locator(`.nc-form-drag-${field.replace(' ', '')}`);
await src.locator(`[data-nc="nc-field-remove-icon"]`).click(); await src.locator(`[data-testid="nc-field-remove-icon"]`).click();
} }
} }
async addField({ field, mode }: { mode: string; field: string }) { async addField({ field, mode }: { mode: string; field: string }) {
if (mode === 'dragDrop') { if (mode === 'dragDrop') {
const src = await this.get().locator(`[data-nc="nc-form-hidden-column-${field}"]`); const src = await this.get().locator(`[data-testid="nc-form-hidden-column-${field}"]`);
const dst = await this.get().locator(`.nc-form-drag-Country`); const dst = await this.get().locator(`.nc-form-drag-Country`);
await src.dragTo(dst); await src.dragTo(dst);
} else if (mode === 'clickField') { } else if (mode === 'clickField') {
const src = await this.get().locator(`[data-nc="nc-form-hidden-column-${field}"]`); const src = await this.get().locator(`[data-testid="nc-form-hidden-column-${field}"]`);
await src.click(); await src.click();
} }
} }
@ -149,7 +153,7 @@ export class FormPage extends BasePage {
async fillForm(param: { field: string; value: string }[]) { async fillForm(param: { field: string; value: string }[]) {
for (let i = 0; i < param.length; i++) { for (let i = 0; i < param.length; i++) {
await this.get() await this.get()
.locator(`[data-nc="nc-form-input-${param[i].field.replace(' ', '')}"] >> input`) .locator(`[data-testid="nc-form-input-${param[i].field.replace(' ', '')}"] >> input`)
.fill(param[i].value); .fill(param[i].value);
} }
} }
@ -167,7 +171,7 @@ export class FormPage extends BasePage {
}) { }) {
await this.get() await this.get()
.locator(`.nc-form-drag-${field.replace(' ', '')}`) .locator(`.nc-form-drag-${field.replace(' ', '')}`)
.locator('div[data-nc="nc-form-input-label"]') .locator('div[data-testid="nc-form-input-label"]')
.click(); .click();
await this.getFormFieldsInputLabel().fill(label); await this.getFormFieldsInputLabel().fill(label);
await this.getFormFieldsInputHelpText().fill(helpText); await this.getFormFieldsInputHelpText().fill(helpText);
@ -196,12 +200,12 @@ export class FormPage extends BasePage {
const fieldLabel = await this.get() const fieldLabel = await this.get()
.locator(`.nc-form-drag-${field.replace(' ', '')}`) .locator(`.nc-form-drag-${field.replace(' ', '')}`)
.locator('div[data-nc="nc-form-input-label"]'); .locator('div[data-testid="nc-form-input-label"]');
await expect(fieldLabel).toHaveText(expectText); await expect(fieldLabel).toHaveText(expectText);
const fieldHelpText = await this.get() const fieldHelpText = await this.get()
.locator(`.nc-form-drag-${field.replace(' ', '')}`) .locator(`.nc-form-drag-${field.replace(' ', '')}`)
.locator('div[data-nc="nc-form-input-help-text-label"]'); .locator('div[data-testid="nc-form-input-help-text-label"]');
await expect(fieldHelpText).toHaveText(helpText); await expect(fieldHelpText).toHaveText(helpText);
} }
@ -237,16 +241,18 @@ export class FormPage extends BasePage {
async verifyAfterSubmitMenuState(param: { showBlankForm?: boolean; submitAnotherForm?: boolean; emailMe?: boolean }) { async verifyAfterSubmitMenuState(param: { showBlankForm?: boolean; submitAnotherForm?: boolean; emailMe?: boolean }) {
if (true === param.showBlankForm) { if (true === param.showBlankForm) {
await expect( await expect(
this.get().locator('[data-nc="nc-form-checkbox-show-blank-form"][aria-checked="true"]') this.get().locator('[data-testid="nc-form-checkbox-show-blank-form"][aria-checked="true"]')
).toBeVisible(); ).toBeVisible();
} }
if (true === param.submitAnotherForm) { if (true === param.submitAnotherForm) {
await expect( await expect(
this.get().locator('[data-nc="nc-form-checkbox-submit-another-form"][aria-checked="true"]') this.get().locator('[data-testid="nc-form-checkbox-submit-another-form"][aria-checked="true"]')
).toBeVisible(); ).toBeVisible();
} }
if (true === param.emailMe) { if (true === param.emailMe) {
await expect(this.get().locator('[data-nc="nc-form-checkbox-send-email"][aria-checked="true"]')).toBeVisible(); await expect(
this.get().locator('[data-testid="nc-form-checkbox-send-email"][aria-checked="true"]')
).toBeVisible();
} }
} }
} }

2
packages/nc-gui/tests/playwright/pages/Dashboard/Gallery/index.ts → tests/playwright/pages/Dashboard/Gallery/index.ts

@ -13,7 +13,7 @@ export class GalleryPage extends BasePage {
} }
get() { get() {
return this.dashboard.get().locator('[data-nc="nc-gallery-wrapper"]'); return this.dashboard.get().locator('[data-testid="nc-gallery-wrapper"]');
} }
card(index: number) { card(index: number) {

10
packages/nc-gui/tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts → tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts

@ -21,7 +21,7 @@ export class ChildList extends BasePage {
// icon: reload // icon: reload
await expect(this.get().locator(`.ant-modal-title`)).toHaveText(`Child list`); await expect(this.get().locator(`.ant-modal-title`)).toHaveText(`Child list`);
await expect(await this.get().locator(`button:has-text("Link to '${linkField}'")`).isVisible()).toBeTruthy(); await expect(await this.get().locator(`button:has-text("Link to '${linkField}'")`).isVisible()).toBeTruthy();
await expect(await this.get().locator(`[data-cy="nc-child-list-reload"]`).isVisible()).toBeTruthy(); await expect(await this.get().locator(`[data-testid="nc-child-list-reload"]`).isVisible()).toBeTruthy();
// child list body validation (card count, card title) // child list body validation (card count, card title)
const cardCount = cardTitle.length; const cardCount = cardTitle.length;
@ -34,8 +34,12 @@ export class ChildList extends BasePage {
await expect(await childList.nth(i).textContent()).toContain(cardTitle[i]); await expect(await childList.nth(i).textContent()).toContain(cardTitle[i]);
// icon: unlink // icon: unlink
// icon: delete // icon: delete
await expect(await childList.nth(i).locator(`[data-cy="nc-child-list-icon-unlink"]`).isVisible()).toBeTruthy(); await expect(
await expect(await childList.nth(i).locator(`[data-cy="nc-child-list-icon-delete"]`).isVisible()).toBeTruthy(); await childList.nth(i).locator(`[data-testid="nc-child-list-icon-unlink"]`).isVisible()
).toBeTruthy();
await expect(
await childList.nth(i).locator(`[data-testid="nc-child-list-icon-delete"]`).isVisible()
).toBeTruthy();
} }
} }
} }

0
packages/nc-gui/tests/playwright/pages/Dashboard/Grid/Column/LTAR/LinkRecord.ts → tests/playwright/pages/Dashboard/Grid/Column/LTAR/LinkRecord.ts

14
packages/nc-gui/tests/playwright/pages/Dashboard/Grid/Column/SelectOptionColumn.ts → tests/playwright/pages/Dashboard/Grid/Column/SelectOptionColumn.ts

@ -29,8 +29,8 @@ export class SelectOptionColumnPageObject extends BasePage {
await this.column.get().locator('button:has-text("Add option")').click(); await this.column.get().locator('button:has-text("Add option")').click();
// Fill text=Select options can't be nullAdd option >> input[type="text"] // Fill text=Select options can't be nullAdd option >> input[type="text"]
await this.column.get().locator(`[data-nc="select-column-option-input-${index}"]`).click(); await this.column.get().locator(`[data-testid="select-column-option-input-${index}"]`).click();
await this.column.get().locator(`[data-nc="select-column-option-input-${index}"]`).fill(option); await this.column.get().locator(`[data-testid="select-column-option-input-${index}"]`).fill(option);
if (!skipColumnModal && columnTitle) await this.column.save({ isUpdated: true }); if (!skipColumnModal && columnTitle) await this.column.save({ isUpdated: true });
} }
@ -38,8 +38,8 @@ export class SelectOptionColumnPageObject extends BasePage {
async editOption({ columnTitle, index, newOption }: { index: number; columnTitle: string; newOption: string }) { async editOption({ columnTitle, index, newOption }: { index: number; columnTitle: string; newOption: string }) {
await this.column.openEdit({ title: columnTitle }); await this.column.openEdit({ title: columnTitle });
await this.column.get().locator(`[data-nc="select-column-option-input-${index}"]`).click(); await this.column.get().locator(`[data-testid="select-column-option-input-${index}"]`).click();
await this.column.get().locator(`[data-nc="select-column-option-input-${index}"]`).fill(newOption); await this.column.get().locator(`[data-testid="select-column-option-input-${index}"]`).fill(newOption);
await this.column.save({ isUpdated: true }); await this.column.save({ isUpdated: true });
} }
@ -47,7 +47,7 @@ export class SelectOptionColumnPageObject extends BasePage {
async deleteOption({ columnTitle, index }: { index: number; columnTitle: string }) { async deleteOption({ columnTitle, index }: { index: number; columnTitle: string }) {
await this.column.openEdit({ title: columnTitle }); await this.column.openEdit({ title: columnTitle });
await this.column.get().locator(`svg[data-nc="select-column-option-remove-${index}"]`).click(); await this.column.get().locator(`svg[data-testid="select-column-option-remove-${index}"]`).click();
await this.column.save({ isUpdated: true }); await this.column.save({ isUpdated: true });
} }
@ -66,8 +66,8 @@ export class SelectOptionColumnPageObject extends BasePage {
await this.column.rootPage.waitForTimeout(150); await this.column.rootPage.waitForTimeout(150);
await this.column.rootPage.dragAndDrop( await this.column.rootPage.dragAndDrop(
`svg[data-nc="select-option-column-handle-icon-${sourceOption}"]`, `svg[data-testid="select-option-column-handle-icon-${sourceOption}"]`,
`svg[data-nc="select-option-column-handle-icon-${destinationOption}"]`, `svg[data-testid="select-option-column-handle-icon-${destinationOption}"]`,
{ {
force: true, force: true,
} }

2
packages/nc-gui/tests/playwright/pages/Dashboard/Grid/Column/index.ts → tests/playwright/pages/Dashboard/Grid/Column/index.ts

@ -14,7 +14,7 @@ export class ColumnPageObject extends BasePage {
} }
get() { get() {
return this.rootPage.locator('form[data-nc="add-or-edit-column"]'); return this.rootPage.locator('form[data-testid="add-or-edit-column"]');
} }
async create({ async create({

36
packages/nc-gui/tests/playwright/pages/Dashboard/Grid/index.ts → tests/playwright/pages/Dashboard/Grid/index.ts

@ -26,11 +26,11 @@ export class GridPage extends BasePage {
} }
get() { get() {
return this.dashboard.get().locator('[data-nc="nc-grid-wrapper"]'); return this.dashboard.get().locator('[data-testid="nc-grid-wrapper"]');
} }
row(index: number) { row(index: number) {
return this.get().locator(`tr[data-nc="grid-row-${index}"]`); return this.get().locator(`tr[data-testid="grid-row-${index}"]`);
} }
async rowCount() { async rowCount() {
@ -122,17 +122,17 @@ export class GridPage extends BasePage {
} }
async verifyRow({ index }: { index: number }) { async verifyRow({ index }: { index: number }) {
await this.get().locator(`td[data-nc="cell-Title-${index}"]`).waitFor({ state: 'visible' }); await this.get().locator(`td[data-testid="cell-Title-${index}"]`).waitFor({ state: 'visible' });
await expect(this.get().locator(`td[data-nc="cell-Title-${index}"]`)).toHaveCount(1); await expect(this.get().locator(`td[data-testid="cell-Title-${index}"]`)).toHaveCount(1);
} }
async verifyRowDoesNotExist({ index }: { index: number }) { async verifyRowDoesNotExist({ index }: { index: number }) {
await this.get().locator(`td[data-nc="cell-Title-${index}"]`).waitFor({ state: 'hidden' }); await this.get().locator(`td[data-testid="cell-Title-${index}"]`).waitFor({ state: 'hidden' });
return await expect(this.get().locator(`td[data-nc="cell-Title-${index}"]`)).toHaveCount(0); return await expect(this.get().locator(`td[data-testid="cell-Title-${index}"]`)).toHaveCount(0);
} }
async deleteRow(index: number) { async deleteRow(index: number) {
await this.get().locator(`td[data-nc="cell-Title-${index}"]`).click({ await this.get().locator(`td[data-testid="cell-Title-${index}"]`).click({
button: 'right', button: 'right',
}); });
@ -149,7 +149,7 @@ export class GridPage extends BasePage {
async addRowRightClickMenu(index: number) { async addRowRightClickMenu(index: number) {
const rowCount = await this.get().locator('.nc-grid-row').count(); const rowCount = await this.get().locator('.nc-grid-row').count();
await this.get().locator(`td[data-nc="cell-Title-${index}"]`).click({ await this.get().locator(`td[data-testid="cell-Title-${index}"]`).click({
button: 'right', button: 'right',
}); });
// Click text=Insert New Row // Click text=Insert New Row
@ -158,28 +158,30 @@ export class GridPage extends BasePage {
} }
async openExpandedRow({ index }: { index: number }) { async openExpandedRow({ index }: { index: number }) {
await this.row(index).locator(`td[data-nc="cell-Id-${index}"]`).hover(); await this.row(index).locator(`td[data-testid="cell-Id-${index}"]`).hover();
await this.row(index).locator(`div[data-nc="nc-expand-${index}"]`).click(); await this.row(index).locator(`div[data-testid="nc-expand-${index}"]`).click();
await (await this.rootPage.locator('.ant-drawer-body').elementHandle())?.waitForElementState('stable'); await (await this.rootPage.locator('.ant-drawer-body').elementHandle())?.waitForElementState('stable');
} }
async selectAll() { async selectAll() {
await this.get().locator('[data-nc="nc-check-all"]').hover(); await this.get().locator('[data-testid="nc-check-all"]').hover();
await this.get().locator('[data-nc="nc-check-all"]').locator('input[type="checkbox"]').check({ await this.get().locator('[data-testid="nc-check-all"]').locator('input[type="checkbox"]').check({
force: true, force: true,
}); });
const rowCount = await this.rowCount(); const rowCount = await this.rowCount();
for (let i = 0; i < rowCount; i++) { for (let i = 0; i < rowCount; i++) {
await expect(this.row(i).locator(`[data-nc="cell-Id-${i}"]`).locator('span.ant-checkbox-checked')).toHaveCount(1); await expect(
this.row(i).locator(`[data-testid="cell-Id-${i}"]`).locator('span.ant-checkbox-checked')
).toHaveCount(1);
} }
await this.rootPage.waitForTimeout(300); await this.rootPage.waitForTimeout(300);
} }
async deleteAll() { async deleteAll() {
await this.selectAll(); await this.selectAll();
await this.get().locator('[data-nc="nc-check-all"]').nth(0).click({ await this.get().locator('[data-testid="nc-check-all"]').nth(0).click({
button: 'right', button: 'right',
}); });
await this.rootPage.locator('text=Delete Selected Rows').click(); await this.rootPage.locator('text=Delete Selected Rows').click();
@ -211,7 +213,7 @@ export class GridPage extends BasePage {
} }
async waitLoading() { async waitLoading() {
await this.dashboard.get().locator('[data-nc="grid-load-spinner"]').waitFor({ state: 'hidden' }); await this.dashboard.get().locator('[data-testid="grid-load-spinner"]').waitFor({ state: 'hidden' });
} }
async verifyEditDisabled({ columnHeader = 'Title' }: { columnHeader?: string } = {}) { async verifyEditDisabled({ columnHeader = 'Title' }: { columnHeader?: string } = {}) {
@ -224,7 +226,7 @@ export class GridPage extends BasePage {
await expect(await cell.locator('input')).not.toBeVisible(); await expect(await cell.locator('input')).not.toBeVisible();
// right click menu // right click menu
await this.get().locator(`td[data-nc="cell-${columnHeader}-0"]`).click({ await this.get().locator(`td[data-testid="cell-${columnHeader}-0"]`).click({
button: 'right', button: 'right',
}); });
await expect(await this.rootPage.locator('text=Insert New Row')).not.toBeVisible(); await expect(await this.rootPage.locator('text=Insert New Row')).not.toBeVisible();
@ -252,7 +254,7 @@ export class GridPage extends BasePage {
await expect(await cell.locator('input')).toBeVisible(); await expect(await cell.locator('input')).toBeVisible();
// right click menu // right click menu
await this.get().locator(`td[data-nc="cell-${columnHeader}-0"]`).click({ await this.get().locator(`td[data-testid="cell-${columnHeader}-0"]`).click({
button: 'right', button: 'right',
}); });
await expect(await this.rootPage.locator('text=Insert New Row')).toBeVisible(); await expect(await this.rootPage.locator('text=Insert New Row')).toBeVisible();

0
packages/nc-gui/tests/playwright/pages/Dashboard/Import/Airtable.ts → tests/playwright/pages/Dashboard/Import/Airtable.ts

0
packages/nc-gui/tests/playwright/pages/Dashboard/Import/ImportTemplate.ts → tests/playwright/pages/Dashboard/Import/ImportTemplate.ts

4
packages/nc-gui/tests/playwright/pages/Dashboard/Kanban/index.ts → tests/playwright/pages/Dashboard/Kanban/index.ts

@ -14,7 +14,7 @@ export class KanbanPage extends BasePage {
} }
get() { get() {
return this.dashboard.get().locator('[data-nc="nc-kanban-wrapper"]'); return this.dashboard.get().locator('[data-testid="nc-kanban-wrapper"]');
} }
card(index: number) { card(index: number) {
@ -65,7 +65,7 @@ export class KanbanPage extends BasePage {
for (let i = 0; i < stacks; i++) { for (let i = 0; i < stacks; i++) {
const stack = await this.get().locator(`.nc-kanban-stack`).nth(i); const stack = await this.get().locator(`.nc-kanban-stack`).nth(i);
// Since otherwise stack title will be repeated as title is in two divs, with one having hidden class // Since otherwise stack title will be repeated as title is in two divs, with one having hidden class
const stackTitle = await stack.locator(`.nc-kanban-stack-head >> [data-nc="truncate-label"]`); const stackTitle = await stack.locator(`.nc-kanban-stack-head >> [data-testid="truncate-label"]`);
await expect(stackTitle).toHaveText(order[i], { ignoreCase: true }); await expect(stackTitle).toHaveText(order[i], { ignoreCase: true });
} }
} }

2
packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Acl.ts → tests/playwright/pages/Dashboard/Settings/Acl.ts

@ -11,7 +11,7 @@ export class AclPage extends BasePage {
} }
get() { get() {
return this.settings.get().locator(`[data-nc="nc-settings-subtab-UI Access Control"]`); return this.settings.get().locator(`[data-testid="nc-settings-subtab-UI Access Control"]`);
} }
async toggle({ table, role }: { table: string; role: string }) { async toggle({ table, role }: { table: string; role: string }) {

2
packages/nc-gui/tests/playwright/pages/Dashboard/Settings/AppStore.ts → tests/playwright/pages/Dashboard/Settings/AppStore.ts

@ -11,7 +11,7 @@ export class AppStoreSettingsPage extends BasePage {
} }
get() { get() {
return this.settings.get().locator(`[data-nc="nc-settings-subtab-appStore"]`); return this.settings.get().locator(`[data-testid="nc-settings-subtab-appStore"]`);
} }
async install({ name }: { name: string }) { async install({ name }: { name: string }) {

2
packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Audit.ts → tests/playwright/pages/Dashboard/Settings/Audit.ts

@ -11,7 +11,7 @@ export class AuditSettingsPage extends BasePage {
} }
get() { get() {
return this.settings.get().locator(`[data-nc="nc-settings-subtab-Audit"]`); return this.settings.get().locator(`[data-testid="nc-settings-subtab-Audit"]`);
} }
async verifyRow({ async verifyRow({

2
packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Erd.ts → tests/playwright/pages/Dashboard/Settings/Erd.ts

@ -10,6 +10,6 @@ export class SettingsErdPage extends ErdBasePage {
} }
get() { get() {
return this.rootPage.locator(`[data-nc="nc-settings-subtab-ERD View"]`); return this.rootPage.locator(`[data-testid="nc-settings-subtab-ERD View"]`);
} }
} }

2
packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Metadata.ts → tests/playwright/pages/Dashboard/Settings/Metadata.ts

@ -11,7 +11,7 @@ export class MetaDataPage extends BasePage {
} }
get() { get() {
return this.settings.get().locator(`[data-nc="nc-settings-subtab-Metadata"]`); return this.settings.get().locator(`[data-testid="nc-settings-subtab-Metadata"]`);
} }
async clickReload() { async clickReload() {

2
packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Miscellaneous.ts → tests/playwright/pages/Dashboard/Settings/Miscellaneous.ts

@ -11,7 +11,7 @@ export class MiscSettingsPage extends BasePage {
} }
get() { get() {
return this.settings.get().locator(`[data-nc="nc-settings-subtab-Miscellaneous"]`); return this.settings.get().locator(`[data-testid="nc-settings-subtab-Miscellaneous"]`);
} }
async clickShowM2MTables() { async clickShowM2MTables() {

4
packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Teams.ts → tests/playwright/pages/Dashboard/Settings/Teams.ts

@ -17,7 +17,7 @@ export class TeamsPage extends BasePage {
} }
get() { get() {
return this.settings.get().locator(`[data-nc="nc-settings-subtab-Users Management"]`); return this.settings.get().locator(`[data-testid="nc-settings-subtab-Users Management"]`);
} }
prefixEmail(email: string) { prefixEmail(email: string) {
@ -26,7 +26,7 @@ export class TeamsPage extends BasePage {
} }
getSharedBaseSubModal() { getSharedBaseSubModal() {
return this.rootPage.locator(`[data-nc="nc-share-base-sub-modal"]`); return this.rootPage.locator(`[data-testid="nc-share-base-sub-modal"]`);
} }
async invite({ email, role }: { email: string; role: string }) { async invite({ email, role }: { email: string; role: string }) {

2
packages/nc-gui/tests/playwright/pages/Dashboard/Settings/index.ts → tests/playwright/pages/Dashboard/Settings/index.ts

@ -57,7 +57,7 @@ export class SettingsPage extends BasePage {
} }
async close() { async close() {
await this.get().locator('[data-nc="settings-modal-close-button"]').click(); await this.get().locator('[data-testid="settings-modal-close-button"]').click();
await this.get().waitFor({ state: 'hidden' }); await this.get().waitFor({ state: 'hidden' });
} }
} }

26
packages/nc-gui/tests/playwright/pages/Dashboard/SurveyForm/index.ts → tests/playwright/pages/Dashboard/SurveyForm/index.ts

@ -13,14 +13,14 @@ export class SurveyFormPage extends BasePage {
constructor(rootPage: Page) { constructor(rootPage: Page) {
super(rootPage); super(rootPage);
this.formHeading = this.get().locator('[data-nc="nc-survey-form__heading"]'); this.formHeading = this.get().locator('[data-testid="nc-survey-form__heading"]');
this.formSubHeading = this.get().locator('[data-nc="nc-survey-form__sub-heading"]'); this.formSubHeading = this.get().locator('[data-testid="nc-survey-form__sub-heading"]');
this.submitButton = this.get().locator('[data-nc="nc-survey-form__btn-submit"]'); this.submitButton = this.get().locator('[data-testid="nc-survey-form__btn-submit"]');
this.nextButton = this.get().locator('[data-nc="nc-survey-form__btn-next"]'); this.nextButton = this.get().locator('[data-testid="nc-survey-form__btn-next"]');
this.nextSlideButton = this.get().locator('[data-nc="nc-survey-form__icon-next"]'); this.nextSlideButton = this.get().locator('[data-testid="nc-survey-form__icon-next"]');
this.prevSlideButton = this.get().locator('[data-nc="nc-survey-form__icon-prev"]'); this.prevSlideButton = this.get().locator('[data-testid="nc-survey-form__icon-prev"]');
this.darkModeButton = this.get().locator('[data-nc="nc-form-dark-mode"]'); this.darkModeButton = this.get().locator('[data-testid="nc-form-dark-mode"]');
this.formFooter = this.get().locator('[data-nc="nc-survey-form__footer"]'); this.formFooter = this.get().locator('[data-testid="nc-survey-form__footer"]');
} }
get() { get() {
@ -42,7 +42,7 @@ export class SurveyFormPage extends BasePage {
await expect(this.formHeading).toHaveText(heading); await expect(this.formHeading).toHaveText(heading);
await expect(this.formSubHeading).toHaveText(subHeading); await expect(this.formSubHeading).toHaveText(subHeading);
await expect(this.formFooter).toHaveText(footer); await expect(this.formFooter).toHaveText(footer);
await expect(this.get().locator(`[data-nc="nc-form-column-label"]`)).toHaveText(fieldLabel); await expect(this.get().locator(`[data-testid="nc-form-column-label"]`)).toHaveText(fieldLabel);
// parse footer text ("1 / 3") to identify if last slide // parse footer text ("1 / 3") to identify if last slide
let isLastSlide = false; let isLastSlide = false;
@ -60,11 +60,11 @@ export class SurveyFormPage extends BasePage {
} }
async fill(param: { fieldLabel: string; type?: string; value?: string }) { async fill(param: { fieldLabel: string; type?: string; value?: string }) {
await this.get().locator(`[data-nc="nc-survey-form__input-${param.fieldLabel}"]`).click(); await this.get().locator(`[data-testid="nc-survey-form__input-${param.fieldLabel}"]`).click();
if (param.type === 'SingleLineText') { if (param.type === 'SingleLineText') {
await this.get().locator(`[data-nc="nc-survey-form__input-${param.fieldLabel}"] >> input`).fill(param.value); await this.get().locator(`[data-testid="nc-survey-form__input-${param.fieldLabel}"] >> input`).fill(param.value);
// press enter key // press enter key
await this.get().locator(`[data-nc="nc-survey-form__input-${param.fieldLabel}"] >> input`).press('Enter'); await this.get().locator(`[data-testid="nc-survey-form__input-${param.fieldLabel}"] >> input`).press('Enter');
} else if (param.type === 'DateTime') { } else if (param.type === 'DateTime') {
const modal = await this.rootPage.locator('.nc-picker-datetime'); const modal = await this.rootPage.locator('.nc-picker-datetime');
await expect(modal).toBeVisible(); await expect(modal).toBeVisible();
@ -76,7 +76,7 @@ export class SurveyFormPage extends BasePage {
async validateSuccessMessage(param: { message: string; showAnotherForm?: boolean }) { async validateSuccessMessage(param: { message: string; showAnotherForm?: boolean }) {
await expect( await expect(
this.get().locator(`[data-nc="nc-survey-form__success-msg"]:has-text("${param.message}")`) this.get().locator(`[data-testid="nc-survey-form__success-msg"]:has-text("${param.message}")`)
).toBeVisible(); ).toBeVisible();
if (param.showAnotherForm) { if (param.showAnotherForm) {

6
packages/nc-gui/tests/playwright/pages/Dashboard/TreeView.ts → tests/playwright/pages/Dashboard/TreeView.ts

@ -48,7 +48,7 @@ export class TreeViewPage extends BasePage {
await this.dashboard.get().locator('.nc-modal-table-create').locator('.ant-modal-body').waitFor(); await this.dashboard.get().locator('.nc-modal-table-create').locator('.ant-modal-body').waitFor();
await this.dashboard.get().locator('[placeholder="Enter table name"]').fill(title); await this.dashboard.get().getByPlaceholder('Enter table name').fill(title);
await this.waitForResponse({ await this.waitForResponse({
uiAction: this.dashboard.get().locator('button:has-text("Submit")').click(), uiAction: this.dashboard.get().locator('button:has-text("Submit")').click(),
@ -107,8 +107,8 @@ export class TreeViewPage extends BasePage {
async reorderTables({ sourceTable, destinationTable }: { sourceTable: string; destinationTable: string }) { async reorderTables({ sourceTable, destinationTable }: { sourceTable: string; destinationTable: string }) {
await this.dashboard await this.dashboard
.get() .get()
.locator(`[data-nc="tree-view-table-draggable-handle-${sourceTable}"]`) .locator(`[data-testid="tree-view-table-draggable-handle-${sourceTable}"]`)
.dragTo(this.get().locator(`[data-nc="tree-view-table-${destinationTable}"]`)); .dragTo(this.get().locator(`[data-testid="tree-view-table-${destinationTable}"]`));
} }
async quickImport({ title }: { title: string }) { async quickImport({ title }: { title: string }) {

28
packages/nc-gui/tests/playwright/pages/Dashboard/ViewSidebar/index.ts → tests/playwright/pages/Dashboard/ViewSidebar/index.ts

@ -1,5 +1,5 @@
import { expect, Locator } from '@playwright/test'; import { expect, Locator } from '@playwright/test';
import { DashboardPage } from '../'; import { DashboardPage } from '..';
import BasePage from '../../Base'; import BasePage from '../../Base';
export class ViewSidebarPage extends BasePage { export class ViewSidebarPage extends BasePage {
@ -54,7 +54,7 @@ export class ViewSidebarPage extends BasePage {
} }
async openView({ title }: { title: string }) { async openView({ title }: { title: string }) {
await this.get().locator(`[data-nc="view-sidebar-view-${title}"]`).click(); await this.get().locator(`[data-testid="view-sidebar-view-${title}"]`).click();
} }
async createKanbanView({ title }: { title: string }) { async createKanbanView({ title }: { title: string }) {
@ -64,7 +64,7 @@ export class ViewSidebarPage extends BasePage {
// Todo: Make selection better // Todo: Make selection better
async verifyView({ title, index }: { title: string; index: number }) { async verifyView({ title, index }: { title: string; index: number }) {
await expect( await expect(
this.get().locator('[data-nc="view-item"]').nth(index).locator('[data-nc="truncate-label"]') this.get().locator('[data-testid="view-item"]').nth(index).locator('[data-testid="truncate-label"]')
).toHaveText(title, { ignoreCase: true }); ).toHaveText(title, { ignoreCase: true });
} }
@ -82,13 +82,16 @@ export class ViewSidebarPage extends BasePage {
async reorderViews({ sourceView, destinationView }: { sourceView: string; destinationView: string }) { async reorderViews({ sourceView, destinationView }: { sourceView: string; destinationView: string }) {
await this.dashboard await this.dashboard
.get() .get()
.locator(`[data-nc="view-sidebar-drag-handle-${sourceView}"]`) .locator(`[data-testid="view-sidebar-drag-handle-${sourceView}"]`)
.dragTo(this.get().locator(`[data-nc="view-sidebar-view-${destinationView}"]`)); .dragTo(this.get().locator(`[data-testid="view-sidebar-view-${destinationView}"]`));
} }
async deleteView({ title }: { title: string }) { async deleteView({ title }: { title: string }) {
await this.get().locator(`[data-nc="view-sidebar-view-${title}"]`).hover(); await this.get().locator(`[data-testid="view-sidebar-view-${title}"]`).hover();
await this.get().locator(`[data-nc="view-sidebar-view-actions-${title}"]`).locator('.nc-view-delete-icon').click(); await this.get()
.locator(`[data-testid="view-sidebar-view-actions-${title}"]`)
.locator('.nc-view-delete-icon')
.click();
await this.rootPage.locator('.nc-modal-view-delete').locator('button:has-text("Submit"):visible').click(); await this.rootPage.locator('.nc-modal-view-delete').locator('button:has-text("Submit"):visible').click();
@ -101,15 +104,18 @@ export class ViewSidebarPage extends BasePage {
} }
async renameView({ title, newTitle }: { title: string; newTitle: string }) { async renameView({ title, newTitle }: { title: string; newTitle: string }) {
await this.get().locator(`[data-nc="view-sidebar-view-${title}"]`).dblclick(); await this.get().locator(`[data-testid="view-sidebar-view-${title}"]`).dblclick();
await this.get().locator(`[data-nc="view-sidebar-view-${title}"]`).locator('input').fill(newTitle); await this.get().locator(`[data-testid="view-sidebar-view-${title}"]`).locator('input').fill(newTitle);
await this.get().press('Enter'); await this.get().press('Enter');
await this.verifyToast({ message: 'View renamed successfully' }); await this.verifyToast({ message: 'View renamed successfully' });
} }
async copyView({ title }: { title: string }) { async copyView({ title }: { title: string }) {
await this.get().locator(`[data-nc="view-sidebar-view-${title}"]`).hover(); await this.get().locator(`[data-testid="view-sidebar-view-${title}"]`).hover();
await this.get().locator(`[data-nc="view-sidebar-view-actions-${title}"]`).locator('.nc-view-copy-icon').click(); await this.get()
.locator(`[data-testid="view-sidebar-view-actions-${title}"]`)
.locator('.nc-view-copy-icon')
.click();
const submitAction = this.rootPage const submitAction = this.rootPage
.locator('.ant-modal-content') .locator('.ant-modal-content')
.locator('button:has-text("Submit"):visible') .locator('button:has-text("Submit"):visible')

0
packages/nc-gui/tests/playwright/pages/Dashboard/WebhookForm/index.ts → tests/playwright/pages/Dashboard/WebhookForm/index.ts

4
packages/nc-gui/tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts → tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts

@ -15,12 +15,12 @@ export class AttachmentCellPageObject extends BasePage {
} }
clickFilePicker({ index, columnHeader }: { index?: number; columnHeader: string }) { clickFilePicker({ index, columnHeader }: { index?: number; columnHeader: string }) {
return this.get({ index, columnHeader }).locator('[data-nc="attachment-cell-file-picker-button"]').click(); return this.get({ index, columnHeader }).locator('[data-testid="attachment-cell-file-picker-button"]').click();
} }
async addFile({ index, columnHeader, filePath }: { index?: number; columnHeader: string; filePath: string }) { async addFile({ index, columnHeader, filePath }: { index?: number; columnHeader: string; filePath: string }) {
const attachFileAction = this.get({ index, columnHeader }) const attachFileAction = this.get({ index, columnHeader })
.locator('[data-nc="attachment-cell-file-picker-button"]') .locator('[data-testid="attachment-cell-file-picker-button"]')
.click(); .click();
return await this.attachFile({ filePickUIAction: attachFileAction, filePath }); return await this.attachFile({ filePickUIAction: attachFileAction, filePath });
} }

0
packages/nc-gui/tests/playwright/pages/Dashboard/common/Cell/CheckboxCell.ts → tests/playwright/pages/Dashboard/common/Cell/CheckboxCell.ts

0
packages/nc-gui/tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts → tests/playwright/pages/Dashboard/common/Cell/RatingCell.ts

5
packages/nc-gui/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts → tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts

@ -27,12 +27,13 @@ export class SelectOptionCellPageObject extends BasePage {
}) { }) {
await this.get({ index, columnHeader }).click(); await this.get({ index, columnHeader }).click();
await this.rootPage.locator(`[data-nc="select-option-${columnHeader}-${index}"]`, { hasText: option }).click(); await this.rootPage.getByTestId(`select-option-${columnHeader}-${index}`).getByText(option).click();
if (multiSelect) await this.get({ index, columnHeader }).click(); if (multiSelect) await this.get({ index, columnHeader }).click();
await this.rootPage await this.rootPage
.locator(`[data-nc="select-option-${columnHeader}-${index}"]`, { hasText: option }) .getByTestId(`select-option-${columnHeader}-${index}`)
.getByText(option)
.waitFor({ state: 'hidden' }); .waitFor({ state: 'hidden' });
} }

4
packages/nc-gui/tests/playwright/pages/Dashboard/common/Cell/index.ts → tests/playwright/pages/Dashboard/common/Cell/index.ts

@ -24,9 +24,9 @@ export class CellPageObject extends BasePage {
get({ index, columnHeader }: { index?: number; columnHeader: string }): Locator { get({ index, columnHeader }: { index?: number; columnHeader: string }): Locator {
if (this.parent instanceof SharedFormPage) { if (this.parent instanceof SharedFormPage) {
return this.parent.get().locator(`[data-nc="nc-form-input-cell-${columnHeader}"]`); return this.parent.get().locator(`[data-testid="nc-form-input-cell-${columnHeader}"]`);
} else { } else {
return this.parent.get().locator(`td[data-nc="cell-${columnHeader}-${index}"]`); return this.parent.get().locator(`td[data-testid="cell-${columnHeader}-${index}"]`);
} }
} }

4
packages/nc-gui/tests/playwright/pages/Dashboard/common/ProjectMenu/index.ts → tests/playwright/pages/Dashboard/common/ProjectMenu/index.ts

@ -10,11 +10,11 @@ export class ProjectMenuObject extends BasePage {
} }
get() { get() {
return this.rootPage.locator(`[data-nc="nc-fields-menu"]`); return this.rootPage.locator(`[data-testid="nc-fields-menu"]`);
} }
async toggle() { async toggle() {
await this.rootPage.locator('[data-nc="nc-project-menu"]').click(); await this.rootPage.locator('[data-testid="nc-project-menu"]').click();
} }
async click({ menu, subMenu }: { menu: string; subMenu: string }) { async click({ menu, subMenu }: { menu: string; subMenu: string }) {

0
packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/Actions/Erd.ts → tests/playwright/pages/Dashboard/common/Toolbar/Actions/Erd.ts

2
packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/Actions/index.ts → tests/playwright/pages/Dashboard/common/Toolbar/Actions/index.ts

@ -13,7 +13,7 @@ export class ToolbarActionsPage extends BasePage {
} }
get() { get() {
return this.rootPage.locator(`[data-nc="toolbar-actions"]`); return this.rootPage.locator(`[data-testid="toolbar-actions"]`);
} }
// todo: use enum // todo: use enum

0
packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/AddEditKanbanStack.ts → tests/playwright/pages/Dashboard/common/Toolbar/AddEditKanbanStack.ts

8
packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/Fields.ts → tests/playwright/pages/Dashboard/common/Toolbar/Fields.ts

@ -11,14 +11,14 @@ export class ToolbarFieldsPage extends BasePage {
} }
get() { get() {
return this.rootPage.locator(`[data-nc="nc-fields-menu"]`); return this.rootPage.locator(`[data-testid="nc-fields-menu"]`);
} }
// todo: Click and toggle are similar method. Remove one of them // todo: Click and toggle are similar method. Remove one of them
async toggle({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) { async toggle({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) {
await this.toolbar.clickFields(); await this.toolbar.clickFields();
const toggleColumn = this.get() const toggleColumn = this.get()
.locator(`[data-nc="nc-fields-menu-${title}"]`) .locator(`[data-testid="nc-fields-menu-${title}"]`)
.locator('input[type="checkbox"]') .locator('input[type="checkbox"]')
.click(); .click();
@ -32,7 +32,7 @@ export class ToolbarFieldsPage extends BasePage {
} }
async verify({ title, checked }: { title: string; checked: boolean }) { async verify({ title, checked }: { title: string; checked: boolean }) {
const checkbox = this.get().locator(`[data-nc="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]'); const checkbox = this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]');
if (checked) { if (checked) {
await expect(checkbox).toBeChecked(); await expect(checkbox).toBeChecked();
@ -43,7 +43,7 @@ export class ToolbarFieldsPage extends BasePage {
async click({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) { async click({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) {
await this.waitForResponse({ await this.waitForResponse({
uiAction: this.get().locator(`[data-nc="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]').click(), uiAction: this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]').click(),
requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/', requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'], httpMethodsToMatch: ['GET'],
}); });

4
packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts → tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts

@ -11,7 +11,7 @@ export class ToolbarFilterPage extends BasePage {
} }
get() { get() {
return this.rootPage.locator(`[data-nc="nc-filter-menu"]`); return this.rootPage.locator(`[data-testid="nc-filter-menu"]`);
} }
async verify({ index, column, operator, value }: { index: number; column: string; operator: string; value: string }) { async verify({ index, column, operator, value }: { index: number; column: string; operator: string; value: string }) {
@ -24,7 +24,7 @@ export class ToolbarFilterPage extends BasePage {
async verifyFilter({ title }: { title: string }) { async verifyFilter({ title }: { title: string }) {
await expect( await expect(
this.get().locator(`[data-nc="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]') this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]')
).toBeChecked(); ).toBeChecked();
} }

14
packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/ShareView.ts → tests/playwright/pages/Dashboard/common/Toolbar/ShareView.ts

@ -14,22 +14,22 @@ export class ToolbarShareViewPage extends BasePage {
} }
async enablePassword(pwd: string) { async enablePassword(pwd: string) {
await this.get().locator(`[data-nc="nc-modal-share-view__with-password"]`).click(); await this.get().locator(`[data-testid="nc-modal-share-view__with-password"]`).click();
await this.get().locator(`[data-nc="nc-modal-share-view__password"]`).fill(pwd); await this.get().locator(`[data-testid="nc-modal-share-view__password"]`).fill(pwd);
await this.get().locator(`[data-nc="nc-modal-share-view__save-password"]`).click(); await this.get().locator(`[data-testid="nc-modal-share-view__save-password"]`).click();
await this.verifyToast({ message: 'Successfully updated' }); await this.verifyToast({ message: 'Successfully updated' });
} }
async disablePassword() { async disablePassword() {
await this.get().locator(`[data-nc="nc-modal-share-view__with-password"`).click(); await this.get().locator(`[data-testid="nc-modal-share-view__with-password"`).click();
} }
async toggleDownload() { async toggleDownload() {
await this.get().locator(`[data-nc="nc-modal-share-view__with-csv-download"]`).click(); await this.get().locator(`[data-testid="nc-modal-share-view__with-csv-download"]`).click();
} }
async getShareLink() { async getShareLink() {
return await this.get().locator(`[data-nc="nc-modal-share-view__link"]`).innerText(); return await this.get().locator(`[data-testid="nc-modal-share-view__link"]`).innerText();
} }
async close() { async close() {
@ -37,6 +37,6 @@ export class ToolbarShareViewPage extends BasePage {
} }
async toggleSurveyMode() { async toggleSurveyMode() {
await this.get().locator(`[data-nc="nc-modal-share-view__survey-mode"]`).click(); await this.get().locator(`[data-testid="nc-modal-share-view__survey-mode"]`).click();
} }
} }

4
packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts → tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts

@ -11,7 +11,7 @@ export class ToolbarSortPage extends BasePage {
} }
get() { get() {
return this.rootPage.locator(`[data-nc="nc-sorts-menu"]`); return this.rootPage.locator(`[data-testid="nc-sorts-menu"]`);
} }
async verify({ index, column, direction }: { index: number; column: string; direction: string }) { async verify({ index, column, direction }: { index: number; column: string; direction: string }) {
@ -78,6 +78,6 @@ export class ToolbarSortPage extends BasePage {
} }
click({ title }: { title: string }) { click({ title }: { title: string }) {
return this.get().locator(`[data-nc="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]').click(); return this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]').click();
} }
} }

0
packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/StackBy.ts → tests/playwright/pages/Dashboard/common/Toolbar/StackBy.ts

0
packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts → tests/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts

0
packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/index.ts → tests/playwright/pages/Dashboard/common/Toolbar/index.ts

0
packages/nc-gui/tests/playwright/pages/Dashboard/commonBase/Erd.ts → tests/playwright/pages/Dashboard/commonBase/Erd.ts

33
packages/nc-gui/tests/playwright/pages/Dashboard/index.ts → tests/playwright/pages/Dashboard/index.ts

@ -61,7 +61,7 @@ export class DashboardPage extends BasePage {
} }
async gotoSettings() { async gotoSettings() {
await this.rootPage.locator('[data-nc="nc-project-menu"]').click(); await this.rootPage.locator('[data-testid="nc-project-menu"]').click();
await this.rootPage.locator('div.nc-project-menu-item:has-text(" Team & Settings")').click(); await this.rootPage.locator('div.nc-project-menu-item:has-text(" Team & Settings")').click();
} }
@ -70,7 +70,7 @@ export class DashboardPage extends BasePage {
} }
async closeTab({ title }: { title: string }) { async closeTab({ title }: { title: string }) {
const tab = await this.tabBar.locator(`.ant-tabs-tab:has-text("${title}")`); const tab = this.tabBar.locator(`.ant-tabs-tab:has-text("${title}")`);
await tab.locator('button.ant-tabs-tab-remove').click(); await tab.locator('button.ant-tabs-tab-remove').click();
// fix me! // fix me!
@ -82,7 +82,7 @@ export class DashboardPage extends BasePage {
// todo: Fast page transition breaks the vue router // todo: Fast page transition breaks the vue router
await this.rootPage.waitForTimeout(2000); await this.rootPage.waitForTimeout(2000);
await this.rootPage.locator('[data-cy="nc-noco-brand-icon"]').click(); await this.rootPage.getByTestId('nc-noco-brand-icon').click();
const projectsPage = new ProjectsPage(this.rootPage); const projectsPage = new ProjectsPage(this.rootPage);
await projectsPage.waitToBeRendered(); await projectsPage.waitToBeRendered();
} }
@ -99,7 +99,7 @@ export class DashboardPage extends BasePage {
state: 'visible', state: 'visible',
}); });
} else { } else {
await this.get().locator('[data-nc="grid-id-column"]').waitFor({ await this.get().getByTestId('grid-id-column').waitFor({
state: 'visible', state: 'visible',
}); });
} }
@ -109,13 +109,13 @@ export class DashboardPage extends BasePage {
// wait active tab animation to finish // wait active tab animation to finish
await expect await expect
.poll(async () => { .poll(async () => {
return await this.tabBar.locator(`[data-nc="nc-root-tabs-${title}"]`).evaluate(el => { return await this.tabBar.getByTestId(`nc-root-tabs-${title}`).evaluate(el => {
return window.getComputedStyle(el).getPropertyValue('color'); return window.getComputedStyle(el).getPropertyValue('color');
}); });
}) })
.toBe('rgb(67, 81, 232)'); // active tab text color .toBe('rgb(67, 81, 232)'); // active tab text color
await this.get().locator('[data-nc="grid-load-spinner"]').waitFor({ state: 'hidden' }); await this.get().getByTestId('grid-load-spinner').waitFor({ state: 'hidden' });
if (mode === 'standard') { if (mode === 'standard') {
await expect(this.rootPage).toHaveURL( await expect(this.rootPage).toHaveURL(
@ -128,34 +128,35 @@ export class DashboardPage extends BasePage {
// open change password portal // open change password portal
await this.rootPage.locator('.nc-menu-accounts').click(); await this.rootPage.locator('.nc-menu-accounts').click();
await this.rootPage await this.rootPage
.locator(`.nc-dropdown-user-accounts-menu >> [data-cy="nc-menu-accounts__user-settings"]`) .locator('.nc-dropdown-user-accounts-menu')
.getByTestId('nc-menu-accounts__user-settings')
.click(); .click();
} }
// todo: Move this to a seperate page // todo: Move this to a seperate page
async changePassword({ oldPass, newPass, repeatPass }: { oldPass: string; newPass: string; repeatPass: string }) { async changePassword({ oldPass, newPass, repeatPass }: { oldPass: string; newPass: string; repeatPass: string }) {
// change password // change password
const currentPassword = await this.rootPage.locator('input[data-cy="nc-user-settings-form__current-password"]'); const currentPassword = this.rootPage.locator('input[data-testid="nc-user-settings-form__current-password"]');
const newPassword = await this.rootPage.locator('input[data-cy="nc-user-settings-form__new-password"]'); const newPassword = this.rootPage.locator('input[data-testid="nc-user-settings-form__new-password"]');
const confirmPassword = await this.rootPage.locator('input[data-cy="nc-user-settings-form__new-password-repeat"]'); const confirmPassword = this.rootPage.locator('input[data-testid="nc-user-settings-form__new-password-repeat"]');
await currentPassword.fill(oldPass); await currentPassword.fill(oldPass);
await newPassword.fill(newPass); await newPassword.fill(newPass);
await confirmPassword.fill(repeatPass); await confirmPassword.fill(repeatPass);
await this.rootPage.locator('button[data-cy="nc-user-settings-form__submit"]').click(); await this.rootPage.locator('button[data-testid="nc-user-settings-form__submit"]').click();
} }
async signOut() { async signOut() {
await this.rootPage.locator('[data-nc="nc-project-menu"]').click(); await this.rootPage.locator('[data-testid="nc-project-menu"]').click();
const projMenu = await this.rootPage.locator('.nc-dropdown-project-menu'); const projMenu = await this.rootPage.locator('.nc-dropdown-project-menu');
await projMenu.locator('[data-menu-id="account"]:visible').click(); await projMenu.locator('[data-menu-id="account"]:visible').click();
await this.rootPage.locator('div.nc-project-menu-item:has-text("Sign Out"):visible').click(); await this.rootPage.locator('div.nc-project-menu-item:has-text("Sign Out"):visible').click();
await this.rootPage.locator('[data-cy="nc-form-signin"]:visible').waitFor(); await this.rootPage.locator('[data-testid="nc-form-signin"]:visible').waitFor();
} }
async validateProjectMenu(param: { role: string; mode?: string }) { async validateProjectMenu(param: { role: string; mode?: string }) {
await this.rootPage.locator('[data-nc="nc-project-menu"]').click(); await this.rootPage.locator('[data-testid="nc-project-menu"]').click();
const pMenu = this.rootPage.locator(`.nc-dropdown-project-menu:visible`); const pMenu = this.rootPage.locator(`.nc-dropdown-project-menu:visible`);
// menu items // menu items
@ -189,11 +190,11 @@ export class DashboardPage extends BasePage {
for (const item of menuItems[param.role]) { for (const item of menuItems[param.role]) {
await expect(pMenu).toContainText(item); await expect(pMenu).toContainText(item);
} }
await this.rootPage.locator('[data-nc="nc-project-menu"]').click(); await this.rootPage.locator('[data-testid="nc-project-menu"]').click();
} }
// Wait for the loader i.e the loader than appears when rows are being fetched, saved etc on the top right of dashboard // Wait for the loader i.e the loader than appears when rows are being fetched, saved etc on the top right of dashboard
async waitForLoaderToDisappear() { async waitForLoaderToDisappear() {
await this.rootPage.locator('[data-nc="nc-loading"]').waitFor({ state: 'hidden' }); await this.rootPage.locator('[data-testid="nc-loading"]').waitFor({ state: 'hidden' });
} }
} }

8
packages/nc-gui/tests/playwright/pages/LoginPage/index.ts → tests/playwright/pages/LoginPage/index.ts

@ -22,16 +22,16 @@ export class LoginPage extends BasePage {
async fillEmail({ email, withoutPrefix }: { email: string; withoutPrefix?: boolean }) { async fillEmail({ email, withoutPrefix }: { email: string; withoutPrefix?: boolean }) {
if (!withoutPrefix) email = this.prefixEmail(email); if (!withoutPrefix) email = this.prefixEmail(email);
await this.get().locator(`[data-nc="nc-form-signin__email"]`).waitFor(); await this.get().locator(`[data-testid="nc-form-signin__email"]`).waitFor();
await this.get().locator(`[data-nc="nc-form-signin__email"]`).fill(email); await this.get().locator(`[data-testid="nc-form-signin__email"]`).fill(email);
} }
async fillPassword(password: string) { async fillPassword(password: string) {
await this.get().locator(`[data-nc="nc-form-signin__password"]`).fill(password); await this.get().locator(`[data-testid="nc-form-signin__password"]`).fill(password);
} }
async submit() { async submit() {
await this.get().locator(`[data-nc="nc-form-signin__submit"]`).click(); await this.get().locator(`[data-testid="nc-form-signin__submit"]`).click();
// todo: Login api can take some time to respond if server is under load // todo: Login api can take some time to respond if server is under load
await expect(this.rootPage).toHaveURL('http://localhost:3000/#/', { await expect(this.rootPage).toHaveURL('http://localhost:3000/#/', {

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

Loading…
Cancel
Save