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
if: ${{ inputs.db == 'mysql' }}
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
if: ${{ inputs.db == 'pg' }}
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
if: ${{ inputs.db == 'sqlite' && inputs.shard == '1' }}
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
working-directory: ./packages/nc-gui
run: npm run ci:run
@ -78,14 +78,14 @@ jobs:
id: playwright-cache
with:
path: |
**/packages/nc-gui/tests/playwright/node_modules
key: cache-nc-playwright-${{ hashFiles('**/packages/nc-gui/tests/playwright/package-lock.json') }}
**/tests/playwright/node_modules
key: cache-nc-playwright-${{ hashFiles('**/tests/playwright/package-lock.json') }}
- name: Install dependencies
if: steps.playwright-cache.outputs.cache-hit != 'true'
working-directory: ./packages/nc-gui/tests/playwright
working-directory: ./tests/playwright
run: npm install
- name: Install Playwright Browsers
working-directory: ./packages/nc-gui/tests/playwright
working-directory: ./tests/playwright
run: npx playwright install chromium --with-deps
- name: Wait for backend
run: |
@ -95,7 +95,7 @@ jobs:
done
- 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 }}
# 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 &
- name: Wait for backend & run quick tests
if: ${{ inputs.db == 'sqlite' }}
working-directory: ./packages/nc-gui/tests/playwright
working-directory: ./tests/playwright
run: |
while ! curl --output /dev/null --silent --head --fail http://localhost:8080; do
printf '.'
@ -130,14 +130,14 @@ jobs:
if: ${{ inputs.db == 'sqlite' }}
with:
name: playwright-report-quick-${{ inputs.shard }}
path: ./packages/nc-gui/tests/playwright/playwright-report-quick/
path: ./tests/playwright/playwright-report-quick/
retention-days: 2
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report-${{ inputs.db }}-${{ inputs.shard }}
path: ./packages/nc-gui/tests/playwright/playwright-report/
path: ./tests/playwright/playwright-report/
retention-days: 2
- uses: actions/upload-artifact@v3
if: always()

2
package.json

@ -36,7 +36,7 @@
]
},
"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",
"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",

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

@ -155,7 +155,7 @@ watch(isOpen, (n, _o) => {
v-for="op of options"
:key="op.id"
:value="op.title"
:data-nc="`select-option-${column.title}-${rowIndex}`"
:data-testid="`select-option-${column.title}-${rowIndex}`"
@click.stop
>
<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"
:key="op.title"
:value="op.title"
:data-nc="`select-option-${column.title}-${rowIndex}`"
:data-testid="`select-option-${column.title}-${rowIndex}`"
@click.stop
>
<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"
: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)"
data-nc="attachment-cell-file-picker-button"
data-testid="attachment-cell-file-picker-button"
@click.stop="open"
>
<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"
:data-order="table.order"
:data-id="table.id"
:data-nc="`tree-view-table-${table.title}`"
:data-testid="`tree-view-table-${table.title}`"
@click="addTableTab(table)"
>
<GeneralTooltip class="pl-5 pr-3 py-2" modifier-key="Alt">
<template #title>{{ table.table_name }}</template>
<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
v-if="isUIAllowed('treeview-drag-n-drop')"
:class="`nc-child-draggable-icon-${table.title}`"
@ -366,14 +366,14 @@ const onSearchCloseIconClick = () => {
<template #overlay>
<a-menu class="!py-0 rounded text-sm">
<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') }}
</div>
</a-menu-item>
<a-menu-item
v-if="isUIAllowed('table-delete')"
:data-nc="`sidebar-table-delete-${table.title}`"
:data-testid="`sidebar-table-delete-${table.title}`"
@click="deleteTable(table)"
>
<div class="nc-project-menu-item">

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

@ -112,7 +112,7 @@ const columns = [
:columns="columns"
:pagination="false"
:loading="isLoading"
data-nc="audit-tab-table"
data-testid="audit-tab-table"
>
<template #emptyText>
<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
type="text"
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"
>
<template #icon>
@ -220,7 +220,7 @@ watch(
</a-menu-item>
</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>
</a-modal>

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

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

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

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

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

@ -387,7 +387,7 @@ watch(view, (nextView) => {
</script>
<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">
<div v-if="formViewData" class="items-center justify-center text-center mt-2">
<a-alert type="success">
@ -407,7 +407,7 @@ watch(view, (nextView) => {
</a-col>
</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">
<div class="flex flex-wrap gap-2">
<div class="flex-1 text-lg">
@ -419,7 +419,7 @@ watch(view, (nextView) => {
v-if="hiddenColumns.length"
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"
data-nc="nc-form-add-all"
data-testid="nc-form-add-all"
@click="addAllColumns"
>
<!-- Add all -->
@ -430,7 +430,7 @@ watch(view, (nextView) => {
v-if="localColumns.length"
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"
data-nc="nc-form-remove-all"
data-testid="nc-form-remove-all"
@click="removeAllColumns"
>
<!-- Remove all -->
@ -452,7 +452,7 @@ watch(view, (nextView) => {
<a-card
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"
:data-nc="`nc-form-hidden-column-${element.label || element.title}`"
:data-testid="`nc-form-hidden-column-${element.label || element.title}`"
@mousedown="moved = false"
@mousemove="moved = false"
@mouseup="handleMouseUp(element, index)"
@ -480,7 +480,7 @@ watch(view, (nextView) => {
<template #footer>
<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"
data-nc="nc-drag-n-drop-to-hide"
data-testid="nc-drag-n-drop-to-hide"
>
<!-- Drag and drop fields here to hide -->
{{ $t('msg.info.dragDropHide') }}
@ -538,7 +538,7 @@ watch(view, (nextView) => {
hide-details
placeholder="Form Title"
:bordered="false"
data-nc="nc-form-heading"
data-testid="nc-form-heading"
@blur="updateView"
@keydown.enter="updateView"
/>
@ -558,7 +558,7 @@ watch(view, (nextView) => {
:placeholder="$t('msg.info.formDesc')"
:bordered="false"
:disabled="!isEditable"
data-nc="nc-form-sub-heading"
data-testid="nc-form-sub-heading"
@blur="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,
},
]"
data-nc="nc-form-fields"
data-testid="nc-form-fields"
@click="activeRow = element.title"
>
<div
@ -597,7 +597,7 @@ watch(view, (nextView) => {
>
<MdiEyeOffOutline
class="opacity-0 nc-field-remove-icon"
data-nc="nc-field-remove-icon"
data-testid="nc-field-remove-icon"
@click.stop="hideColumn(index)"
/>
</div>
@ -606,7 +606,7 @@ watch(view, (nextView) => {
<div class="flex gap-2 items-center">
<span
class="text-gray-500 mr-2 nc-form-input-required"
data-nc="nc-form-input-required"
data-testid="nc-form-input-required"
@click="
() => {
element.required = !element.required
@ -630,7 +630,7 @@ watch(view, (nextView) => {
v-model:value="element.label"
type="text"
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')"
@change="updateColMeta(element)"
>
@ -642,7 +642,7 @@ watch(view, (nextView) => {
v-model:value="element.description"
type="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')"
@change="updateColMeta(element)"
/>
@ -655,7 +655,7 @@ watch(view, (nextView) => {
:column="{ ...element, title: element.label || element.title }"
:required="isRequired(element, element.required)"
:hide-menu="true"
data-nc="nc-form-input-label"
data-testid="nc-form-input-label"
/>
<LazySmartsheetHeaderCell
@ -663,7 +663,7 @@ watch(view, (nextView) => {
:column="{ ...element, title: element.label || element.title }"
:required="isRequired(element, element.required)"
:hide-menu="true"
data-nc="nc-form-input-label"
data-testid="nc-form-input-label"
/>
</div>
@ -678,7 +678,7 @@ watch(view, (nextView) => {
:row="row"
class="nc-input"
: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"
@click.stop.prevent
/>
@ -694,14 +694,14 @@ watch(view, (nextView) => {
v-model="formState[element.title]"
class="nc-input"
: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"
:edit-enabled="true"
@click.stop.prevent
/>
</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>
</template>
@ -716,7 +716,7 @@ watch(view, (nextView) => {
</Draggable>
<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') }}
</button>
</div>
@ -738,7 +738,7 @@ watch(view, (nextView) => {
:rows="3"
hide-details
class="nc-form-after-submit-msg"
data-nc="nc-form-after-submit-msg"
data-testid="nc-form-after-submit-msg"
@change="updateView"
/>
@ -751,7 +751,7 @@ watch(view, (nextView) => {
v-e="[`a:form-view:submit-another-form`]"
size="small"
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"
/>
<span class="ml-4">{{ $t('msg.info.submitAnotherForm') }}</span>
@ -764,7 +764,7 @@ watch(view, (nextView) => {
v-e="[`a:form-view:show-blank-form`]"
size="small"
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"
/>
@ -777,7 +777,7 @@ watch(view, (nextView) => {
v-e="[`a:form-view:email-me`]"
size="small"
class="nc-form-checkbox-send-email"
data-nc="nc-form-checkbox-send-email"
data-testid="nc-form-checkbox-send-email"
@change="onEmailChange"
/>

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

@ -163,14 +163,14 @@ watch(view, async (nextView) => {
</script>
<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 v-for="record in data" :key="`record-${record.row.id}`">
<LazySmartsheetRow :row="record">
<a-card
hoverable
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)"
>
<template v-if="galleryData?.fk_cover_image_col_id" #cover>

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

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

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

@ -309,7 +309,7 @@ watch(view, async (nextView) => {
</script>
<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">
<a-dropdown v-model:visible="contextMenu" :trigger="['contextmenu']" overlay-class-name="nc-dropdown-kanban-context-menu">
<!-- Draggable Stack -->

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

@ -19,7 +19,7 @@ const page = computed({
<template>
<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') }}
</span>

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

@ -124,7 +124,7 @@ onMounted(() => {
:class="{ '!w-[600px]': formState.uidt === UITypes.Formula }"
@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">
<a-form-item :label="$t('labels.columnName')" v-bind="validateInfos.title">
<a-input

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

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

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

@ -150,7 +150,7 @@ export default {
:key="col.title"
class="mt-2 py-2"
: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" />

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

@ -167,12 +167,12 @@ function onStopEdit() {
<template>
<a-menu-item
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"
@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 class="flex w-auto" :data-nc="`view-sidebar-drag-handle-${vModel.alias || vModel.title}`">
<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-testid="`view-sidebar-drag-handle-${vModel.alias || vModel.title}`">
<MdiDrag
class="nc-drag-icon hidden group-hover:block transition-opacity opacity-0 group-hover:opacity-100 text-gray-500 !cursor-move"
@click.stop.prevent
@ -194,7 +194,7 @@ function onStopEdit() {
<div class="flex-1" />
<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">
<template #title>
{{ $t('activity.copyView') }}

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

@ -82,7 +82,7 @@ const filterAutoSaveLoc = computed({
ref="filterComp"
class="nc-table-toolbar-menu shadow-lg"
:auto-save="filterAutoSave"
data-nc="nc-filter-menu"
data-testid="nc-filter-menu"
@update:filters-length="filtersLength = $event"
>
<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>
<div
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
>
<a-card
@ -167,7 +167,7 @@ const getIcon = (c: ColumnType) =>
v-show="filteredFieldList.includes(field)"
:key="field.id"
class="px-2 py-1 flex items-center"
:data-nc="`nc-fields-menu-${field.title}`"
:data-testid="`nc-fields-menu-${field.title}`"
@click.stop
>
<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"
>
<div
data-cy="nc-modal-share-view__link"
data-nc="nc-modal-share-view__link"
data-testid="nc-modal-share-view__link"
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>
@ -235,8 +234,7 @@ watch(passwordProtected, (value) => {
<a-checkbox
v-if="shared.type === ViewTypes.FORM"
v-model:checked="surveyMode"
data-cy="nc-modal-share-view__survey-mode"
data-nc="nc-modal-share-view__survey-mode"
data-testid="nc-modal-share-view__survey-mode"
class="!text-sm"
>
Use Survey Mode
@ -250,7 +248,7 @@ watch(passwordProtected, (value) => {
</template>
<a-input
v-model:value="transitionDuration"
data-cy="nc-form-signin__email"
data-testid="nc-form-signin__email"
size="small"
class="!w-32"
type="number"
@ -266,8 +264,7 @@ watch(passwordProtected, (value) => {
<a-checkbox
v-if="shared.type === ViewTypes.FORM"
v-model:checked="viewTheme"
data-cy="nc-modal-share-view__with-theme"
data-nc="nc-modal-share-view__with-theme"
data-testid="nc-modal-share-view__with-theme"
class="!text-sm"
>
Use Theme
@ -276,13 +273,12 @@ watch(passwordProtected, (value) => {
<Transition name="layout" mode="out-in">
<div v-if="viewTheme" class="flex pl-6">
<LazyGeneralColorPicker
data-cy="nc-modal-share-view__theme-picker"
data-testid="nc-modal-share-view__theme-picker"
class="!p-0"
:model-value="shared.meta.theme?.primaryColor"
:colors="projectThemeColors"
:row-size="9"
:advanced="false"
data-nc="nc-modal-share-view__theme-picker"
@input="onChangeTheme"
/>
</div>
@ -293,9 +289,8 @@ watch(passwordProtected, (value) => {
<!-- Password Protection -->
<a-checkbox
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"
data-nc="nc-modal-share-view__with-password"
>
{{ $t('msg.info.beforeEnablePwd') }}
</a-checkbox>
@ -304,8 +299,7 @@ watch(passwordProtected, (value) => {
<div v-if="passwordProtected" class="pl-6 flex gap-2 mt-2 mb-4">
<a-input
v-model:value="shared.password"
data-cy="nc-modal-share-view__password"
data-nc="nc-modal-share-view__password"
data-testid="nc-modal-share-view__password"
size="small"
class="!text-xs max-w-[250px]"
type="password"
@ -313,8 +307,7 @@ watch(passwordProtected, (value) => {
/>
<a-button
data-cy="nc-modal-share-view__save-password"
data-nc="nc-modal-share-view__save-password"
data-testid="nc-modal-share-view__save-password"
size="small"
class="!text-xs"
@click="saveShareLinkPassword"
@ -333,8 +326,7 @@ watch(passwordProtected, (value) => {
(shared.type === ViewTypes.GRID || shared.type === ViewTypes.KANBAN || shared.type === ViewTypes.GALLERY)
"
v-model:checked="allowCSVDownload"
data-cy="nc-modal-share-view__with-csv-download"
data-nc="nc-modal-share-view__with-csv-download"
data-testid="nc-modal-share-view__with-csv-download"
class="!text-sm"
>
{{ $t('labels.downloadAllowed') }}

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

@ -55,7 +55,7 @@ watch(
<template #overlay>
<div
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>
<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>
<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-sub-menu
v-if="isUIAllowed('view-type')"

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

@ -136,7 +136,7 @@ onMounted(() => {
</script>
<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]">
<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="flex mb-4 items-center gap-2 px-12">
<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
v-if="!readonly"
type="primary"
ghost
class="!text-xs"
data-cy="nc-child-list-button-link-to"
data-testid="nc-child-list-button-link-to"
size="small"
@click="emit('attachRecord')"
>
@ -143,13 +148,13 @@ watch(
<div v-if="!readonly" class="flex gap-2">
<MdiLinkVariantRemove
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)"
/>
<MdiDeleteOutline
v-if="!readonly && !isPublic"
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)"
/>
</div>

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

@ -44,7 +44,7 @@ hooks.hook('page:finish', () => {
<div
v-if="!route.params.projectType"
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"
@click="navigateTo('/')"
>
@ -57,7 +57,7 @@ hooks.hook('page:finish', () => {
</div>
<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') }}
<MdiReload :class="{ 'animate-infinite animate-spin': isLoading }" />
@ -79,14 +79,14 @@ hooks.hook('page:finish', () => {
<template v-if="signedIn">
<a-dropdown :trigger="['click']" overlay-class-name="nc-dropdown-user-accounts-menu">
<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"
@click.prevent
/>
<template #overlay>
<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">
<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 items-center gap-2 ml-3 text-white">
<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 }" />
</template>

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

@ -194,7 +194,7 @@ onBeforeUnmount(reset)
<div
v-if="isOpen && !isSharedBase"
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"
@click="navigateTo('/')"
>
@ -229,7 +229,7 @@ onBeforeUnmount(reset)
<div
:style="{ width: isOpen ? 'calc(100% - 40px) pr-2' : '100%' }"
: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"
>
<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>
<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>
<div>{{ tab.title }}</div>
</template>
</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>
</template>
</a-tab-pane>
@ -68,7 +68,7 @@ function onEdit(targetKey: number, action: 'add' | 'remove' | string) {
<span class="flex-1" />
<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') }}
<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
v-if="isVirtualCol(field)"
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(' ', '')}`"
:column="field"
/>
@ -94,7 +94,7 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
v-else
v-model="formState[field.title]"
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(' ', '')}`"
:column="field"
:edit-enabled="true"
@ -115,7 +115,7 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
<button
type="submit"
class="uppercase scaling-btn prose-sm"
data-nc="shared-form-submit-button"
data-testid="shared-form-submit-button"
@click="submitForm"
>
{{ $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"
>
<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 }}
</h1>
<h2
v-if="sharedFormView.subheading && sharedFormView.subheading !== ''"
class="prose-lg text-slate-500 dark:text-slate-300 self-center mb-4 leading-6"
data-cy="nc-survey-form__sub-heading"
data-nc="nc-survey-form__sub-heading"
data-testid="nc-survey-form__sub-heading"
>
{{ sharedFormView?.subheading }}
</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"
>
<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
v-if="isVirtualCol(field)"
:column="{ ...field, title: field.label || field.title }"
@ -254,8 +253,7 @@ onMounted(() => {
v-model="formState[field.title]"
class="mt-0 nc-input"
:row="{ row: {}, oldRow: {}, rowMeta: {} }"
:data-cy="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`"
:data-nc="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`"
:data-testid="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`"
:column="field"
/>
@ -263,8 +261,7 @@ onMounted(() => {
v-else
v-model="formState[field.title]"
class="nc-input"
:data-cy="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`"
:data-nc="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`"
:data-testid="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`"
:column="field"
:edit-enabled="true"
/>
@ -277,8 +274,7 @@ onMounted(() => {
<div
class="block text-[14px]"
:class="field.uidt === UITypes.Checkbox ? 'text-center' : ''"
data-nc="nc-survey-form__field-description"
data-cy="nc-survey-form__field-description"
data-testid="nc-survey-form__field-description"
>
{{ field.description }}
</div>
@ -302,8 +298,7 @@ onMounted(() => {
"
type="submit"
class="uppercase scaling-btn prose-sm"
data-cy="nc-survey-form__btn-submit"
data-nc="nc-survey-form__btn-submit"
data-testid="nc-survey-form__btn-submit"
@click="submit"
>
{{ $t('general.submit') }}
@ -318,8 +313,7 @@ onMounted(() => {
>
<button
class="bg-opacity-100 scaling-btn flex items-center gap-1"
data-cy="nc-survey-form__btn-next"
data-nc="nc-survey-form__btn-next"
data-testid="nc-survey-form__btn-next"
:class="[
v$.localState[field.title]?.$error ? 'after:!bg-gray-100 after:!ring-red-500' : '',
animationTarget === AnimationTarget.OkButton && isAnimating
@ -349,11 +343,7 @@ onMounted(() => {
<Transition name="slide-left">
<div v-if="submitted" class="flex flex-col justify-center items-center text-center">
<div
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"
>
<div class="text-lg px-6 py-3 bg-green-300 text-gray-700 rounded" data-testid="nc-survey-form__success-msg">
<template v-if="sharedFormView?.success_msg">
{{ sharedFormView?.success_msg }}
</template>
@ -376,8 +366,7 @@ onMounted(() => {
<button
type="button"
class="scaling-btn bg-opacity-100"
data-cy="nc-survey-form__btn-submit-another-form"
data-nc="nc-survey-form__btn-submit-another-form"
data-testid="nc-survey-form__btn-submit-another-form"
@click="resetForm"
>
Submit Another Form
@ -391,11 +380,7 @@ onMounted(() => {
</div>
<template v-if="!submitted">
<div
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"
>
<div class="mb-24 md:my-4 select-none text-center text-gray-500 dark:text-slate-200" data-testid="nc-survey-form__footer">
{{ index + 1 }} / {{ formColumns?.length }}
</div>
</template>
@ -414,8 +399,7 @@ onMounted(() => {
: ''
"
class="p-0.5 flex items-center group color-transition"
data-cy="nc-survey-form__icon-prev"
data-nc="nc-survey-form__icon-prev"
data-testid="nc-survey-form__icon-prev"
@click="goPrevious()"
>
<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"
data-cy="nc-survey-form__icon-next"
data-nc="nc-survey-form__icon-next"
data-testid="nc-survey-form__icon-next"
@click="goNext()"
>
<MdiChevronRight

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

@ -144,7 +144,7 @@ const copyProjectMeta = async () => {
<template>
<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)"
data-nc="projects-container"
data-testid="projects-container"
>
<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>
@ -166,7 +166,7 @@ const copyProjectMeta = async () => {
v-e="['a:project:refresh']"
class="text-xl text-gray-500 group-hover:text-accent cursor-pointer"
:class="isLoading ? '!text-primary' : ''"
data-nc="projects-reload-button"
data-testid="projects-reload-button"
@click="loadProjects"
/>
</div>
@ -290,7 +290,7 @@ const copyProjectMeta = async () => {
<MdiDeleteOutline
class="nc-action-btn"
:data-nc="`delete-project-${record.title}`"
:data-testid="`delete-project-${record.title}`"
@click.stop="deleteProject(record)"
/>
</div>

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

@ -82,7 +82,7 @@ const resetError = () => {
<a-form
ref="formValidator"
data-cy="nc-user-settings-form"
data-testid="nc-user-settings-form"
layout="vertical"
class="change-password lg:max-w-3/4 w-full !mx-auto"
no-style
@ -91,7 +91,7 @@ const resetError = () => {
>
<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 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 />
{{ error }}
</div>
@ -101,7 +101,7 @@ const resetError = () => {
<a-form-item :label="$t('placeholder.password.current')" name="currentPassword" :rules="formRules.currentPassword">
<a-input-password
v-model:value="form.currentPassword"
data-cy="nc-user-settings-form__current-password"
data-testid="nc-user-settings-form__current-password"
size="large"
class="password"
: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-input-password
v-model:value="form.password"
data-cy="nc-user-settings-form__new-password"
data-testid="nc-user-settings-form__new-password"
size="large"
class="password"
: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-input-password
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"
class="password"
:placeholder="$t('placeholder.password.confirm')"
@ -132,7 +132,7 @@ const resetError = () => {
</a-form-item>
<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">
<MdiKeyChange />
{{ $t('activity.changePwd') }}

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

@ -64,7 +64,7 @@ function resetError() {
<template>
<NuxtLayout>
<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"
>
<div
@ -87,8 +87,7 @@ function resetError() {
<a-form-item :label="$t('labels.email')" name="email" :rules="formRules.email">
<a-input
v-model:value="form.email"
data-cy="nc-form-signin__email"
data-nc="nc-form-signin__email"
data-testid="nc-form-signin__email"
size="large"
:placeholder="$t('msg.info.signUp.workEmail')"
@focus="resetError"
@ -98,8 +97,7 @@ function resetError() {
<a-form-item :label="$t('labels.password')" name="password" :rules="formRules.password">
<a-input-password
v-model:value="form.password"
data-cy="nc-form-signin__password"
data-nc="nc-form-signin__password"
data-testid="nc-form-signin__password"
size="large"
class="password"
:placeholder="$t('msg.info.signUp.enterPassword')"
@ -114,12 +112,7 @@ function resetError() {
</div>
<div class="self-center flex flex-col flex-wrap gap-4 items-center mt-4 justify-center">
<button
data-cy="nc-form-signin__submit"
data-nc="nc-form-signin__submit"
class="scaling-btn bg-opacity-100"
type="submit"
>
<button data-testid="nc-form-signin__submit" class="scaling-btn bg-opacity-100" type="submit">
<span class="flex items-center gap-2">
<MdiLogin />
{{ $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
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
npm install
@ -47,13 +47,13 @@ npm run test:debug
For setting up mysql:
```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:
```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
@ -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.
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

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",
"scripts": {
"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:2": "TRACE=true npx playwright test --workers=4 --shard=2/2",
"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 }) {
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();
switch (type) {
case 'text':
@ -40,7 +40,7 @@ export class ExpandedFormPage extends BasePage {
break;
case 'hasMany':
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);
break;
}
@ -70,7 +70,7 @@ export class ExpandedFormPage extends BasePage {
await this.get().press('Escape');
await this.get().waitFor({ state: 'hidden' });
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 }) {
@ -87,7 +87,7 @@ export class ExpandedFormPage extends BasePage {
}
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();
}

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.toolbar = new ToolbarPage(this);
this.addAllButton = dashboard.get().locator('[data-nc="nc-form-add-all"]');
this.removeAllButton = dashboard.get().locator('[data-nc="nc-form-remove-all"]');
this.submitButton = dashboard.get().locator('[data-nc="nc-form-submit"]');
this.showAnotherFormRadioButton = dashboard.get().locator('[data-nc="nc-form-checkbox-submit-another-form"]');
this.showAnotherFormAfter5SecRadioButton = dashboard.get().locator('[data-nc="nc-form-checkbox-show-blank-form"]');
this.emailMeRadioButton = dashboard.get().locator('[data-nc="nc-form-checkbox-send-email"]');
this.formHeading = dashboard.get().locator('[data-nc="nc-form-heading"]');
this.formSubHeading = dashboard.get().locator('[data-nc="nc-form-sub-heading"]');
this.afterSubmitMsg = dashboard.get().locator('[data-nc="nc-form-after-submit-msg"]');
this.addAllButton = dashboard.get().locator('[data-testid="nc-form-add-all"]');
this.removeAllButton = dashboard.get().locator('[data-testid="nc-form-remove-all"]');
this.submitButton = dashboard.get().locator('[data-testid="nc-form-submit"]');
this.showAnotherFormRadioButton = dashboard.get().locator('[data-testid="nc-form-checkbox-submit-another-form"]');
this.showAnotherFormAfter5SecRadioButton = dashboard
.get()
.locator('[data-testid="nc-form-checkbox-show-blank-form"]');
this.emailMeRadioButton = dashboard.get().locator('[data-testid="nc-form-checkbox-send-email"]');
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() {
return this.dashboard.get().locator('[data-nc="nc-form-wrapper"]');
return this.dashboard.get().locator('[data-testid="nc-form-wrapper"]');
}
getFormAfterSubmit() {
return this.dashboard.get().locator('[data-nc="nc-form-wrapper-submit"]');
return this.dashboard.get().locator('[data-testid="nc-form-wrapper-submit"]');
}
getFormHiddenColumn() {
return this.get().locator('[data-nc="nc-form-hidden-column"]');
return this.get().locator('[data-testid="nc-form-hidden-column"]');
}
getFormFields() {
return this.get().locator('[data-nc="nc-form-fields"]');
return this.get().locator('[data-testid="nc-form-fields"]');
}
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() {
return this.get().locator('[data-nc="nc-field-remove-icon"]');
return this.get().locator('[data-testid="nc-field-remove-icon"]');
}
getFormFieldsRequired() {
return this.get().locator('[data-nc="nc-form-input-required"]');
return this.get().locator('[data-testid="nc-form-input-required"]');
}
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() {
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 }) {
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 }) {
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);
}
@ -91,7 +95,7 @@ export class FormPage extends BasePage {
}
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);
for (let i = 0; i < fields.length; 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 }) {
if (mode === 'dragDrop') {
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);
} else if (mode === 'hideField') {
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 }) {
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`);
await src.dragTo(dst);
} 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();
}
}
@ -149,7 +153,7 @@ export class FormPage extends BasePage {
async fillForm(param: { field: string; value: string }[]) {
for (let i = 0; i < param.length; i++) {
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);
}
}
@ -167,7 +171,7 @@ export class FormPage extends BasePage {
}) {
await this.get()
.locator(`.nc-form-drag-${field.replace(' ', '')}`)
.locator('div[data-nc="nc-form-input-label"]')
.locator('div[data-testid="nc-form-input-label"]')
.click();
await this.getFormFieldsInputLabel().fill(label);
await this.getFormFieldsInputHelpText().fill(helpText);
@ -196,12 +200,12 @@ export class FormPage extends BasePage {
const fieldLabel = await this.get()
.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);
const fieldHelpText = await this.get()
.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);
}
@ -237,16 +241,18 @@ export class FormPage extends BasePage {
async verifyAfterSubmitMenuState(param: { showBlankForm?: boolean; submitAnotherForm?: boolean; emailMe?: boolean }) {
if (true === param.showBlankForm) {
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();
}
if (true === param.submitAnotherForm) {
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();
}
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() {
return this.dashboard.get().locator('[data-nc="nc-gallery-wrapper"]');
return this.dashboard.get().locator('[data-testid="nc-gallery-wrapper"]');
}
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
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(`[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)
const cardCount = cardTitle.length;
@ -34,8 +34,12 @@ export class ChildList extends BasePage {
await expect(await childList.nth(i).textContent()).toContain(cardTitle[i]);
// icon: unlink
// icon: delete
await expect(await childList.nth(i).locator(`[data-cy="nc-child-list-icon-unlink"]`).isVisible()).toBeTruthy();
await expect(await childList.nth(i).locator(`[data-cy="nc-child-list-icon-delete"]`).isVisible()).toBeTruthy();
await expect(
await childList.nth(i).locator(`[data-testid="nc-child-list-icon-unlink"]`).isVisible()
).toBeTruthy();
await expect(
await childList.nth(i).locator(`[data-testid="nc-child-list-icon-delete"]`).isVisible()
).toBeTruthy();
}
}
}

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();
// 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-nc="select-column-option-input-${index}"]`).fill(option);
await this.column.get().locator(`[data-testid="select-column-option-input-${index}"]`).click();
await this.column.get().locator(`[data-testid="select-column-option-input-${index}"]`).fill(option);
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 }) {
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-nc="select-column-option-input-${index}"]`).fill(newOption);
await this.column.get().locator(`[data-testid="select-column-option-input-${index}"]`).click();
await this.column.get().locator(`[data-testid="select-column-option-input-${index}"]`).fill(newOption);
await this.column.save({ isUpdated: true });
}
@ -47,7 +47,7 @@ export class SelectOptionColumnPageObject extends BasePage {
async deleteOption({ columnTitle, index }: { index: number; columnTitle: string }) {
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 });
}
@ -66,8 +66,8 @@ export class SelectOptionColumnPageObject extends BasePage {
await this.column.rootPage.waitForTimeout(150);
await this.column.rootPage.dragAndDrop(
`svg[data-nc="select-option-column-handle-icon-${sourceOption}"]`,
`svg[data-nc="select-option-column-handle-icon-${destinationOption}"]`,
`svg[data-testid="select-option-column-handle-icon-${sourceOption}"]`,
`svg[data-testid="select-option-column-handle-icon-${destinationOption}"]`,
{
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() {
return this.rootPage.locator('form[data-nc="add-or-edit-column"]');
return this.rootPage.locator('form[data-testid="add-or-edit-column"]');
}
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() {
return this.dashboard.get().locator('[data-nc="nc-grid-wrapper"]');
return this.dashboard.get().locator('[data-testid="nc-grid-wrapper"]');
}
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() {
@ -122,17 +122,17 @@ export class GridPage extends BasePage {
}
async verifyRow({ index }: { index: number }) {
await this.get().locator(`td[data-nc="cell-Title-${index}"]`).waitFor({ state: 'visible' });
await expect(this.get().locator(`td[data-nc="cell-Title-${index}"]`)).toHaveCount(1);
await this.get().locator(`td[data-testid="cell-Title-${index}"]`).waitFor({ state: 'visible' });
await expect(this.get().locator(`td[data-testid="cell-Title-${index}"]`)).toHaveCount(1);
}
async verifyRowDoesNotExist({ index }: { index: number }) {
await this.get().locator(`td[data-nc="cell-Title-${index}"]`).waitFor({ state: 'hidden' });
return await expect(this.get().locator(`td[data-nc="cell-Title-${index}"]`)).toHaveCount(0);
await this.get().locator(`td[data-testid="cell-Title-${index}"]`).waitFor({ state: 'hidden' });
return await expect(this.get().locator(`td[data-testid="cell-Title-${index}"]`)).toHaveCount(0);
}
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',
});
@ -149,7 +149,7 @@ export class GridPage extends BasePage {
async addRowRightClickMenu(index: number) {
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',
});
// Click text=Insert New Row
@ -158,28 +158,30 @@ export class GridPage extends BasePage {
}
async openExpandedRow({ index }: { index: number }) {
await this.row(index).locator(`td[data-nc="cell-Id-${index}"]`).hover();
await this.row(index).locator(`div[data-nc="nc-expand-${index}"]`).click();
await this.row(index).locator(`td[data-testid="cell-Id-${index}"]`).hover();
await this.row(index).locator(`div[data-testid="nc-expand-${index}"]`).click();
await (await this.rootPage.locator('.ant-drawer-body').elementHandle())?.waitForElementState('stable');
}
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,
});
const rowCount = await this.rowCount();
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);
}
async deleteAll() {
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',
});
await this.rootPage.locator('text=Delete Selected Rows').click();
@ -211,7 +213,7 @@ export class GridPage extends BasePage {
}
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 } = {}) {
@ -224,7 +226,7 @@ export class GridPage extends BasePage {
await expect(await cell.locator('input')).not.toBeVisible();
// 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',
});
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();
// 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',
});
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() {
return this.dashboard.get().locator('[data-nc="nc-kanban-wrapper"]');
return this.dashboard.get().locator('[data-testid="nc-kanban-wrapper"]');
}
card(index: number) {
@ -65,7 +65,7 @@ export class KanbanPage extends BasePage {
for (let i = 0; i < stacks; 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
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 });
}
}

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() {
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 }) {

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() {
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 }) {

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() {
return this.settings.get().locator(`[data-nc="nc-settings-subtab-Audit"]`);
return this.settings.get().locator(`[data-testid="nc-settings-subtab-Audit"]`);
}
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() {
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() {
return this.settings.get().locator(`[data-nc="nc-settings-subtab-Metadata"]`);
return this.settings.get().locator(`[data-testid="nc-settings-subtab-Metadata"]`);
}
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() {
return this.settings.get().locator(`[data-nc="nc-settings-subtab-Miscellaneous"]`);
return this.settings.get().locator(`[data-testid="nc-settings-subtab-Miscellaneous"]`);
}
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() {
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) {
@ -26,7 +26,7 @@ export class TeamsPage extends BasePage {
}
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 }) {

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() {
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' });
}
}

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) {
super(rootPage);
this.formHeading = this.get().locator('[data-nc="nc-survey-form__heading"]');
this.formSubHeading = this.get().locator('[data-nc="nc-survey-form__sub-heading"]');
this.submitButton = this.get().locator('[data-nc="nc-survey-form__btn-submit"]');
this.nextButton = this.get().locator('[data-nc="nc-survey-form__btn-next"]');
this.nextSlideButton = this.get().locator('[data-nc="nc-survey-form__icon-next"]');
this.prevSlideButton = this.get().locator('[data-nc="nc-survey-form__icon-prev"]');
this.darkModeButton = this.get().locator('[data-nc="nc-form-dark-mode"]');
this.formFooter = this.get().locator('[data-nc="nc-survey-form__footer"]');
this.formHeading = this.get().locator('[data-testid="nc-survey-form__heading"]');
this.formSubHeading = this.get().locator('[data-testid="nc-survey-form__sub-heading"]');
this.submitButton = this.get().locator('[data-testid="nc-survey-form__btn-submit"]');
this.nextButton = this.get().locator('[data-testid="nc-survey-form__btn-next"]');
this.nextSlideButton = this.get().locator('[data-testid="nc-survey-form__icon-next"]');
this.prevSlideButton = this.get().locator('[data-testid="nc-survey-form__icon-prev"]');
this.darkModeButton = this.get().locator('[data-testid="nc-form-dark-mode"]');
this.formFooter = this.get().locator('[data-testid="nc-survey-form__footer"]');
}
get() {
@ -42,7 +42,7 @@ export class SurveyFormPage extends BasePage {
await expect(this.formHeading).toHaveText(heading);
await expect(this.formSubHeading).toHaveText(subHeading);
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
let isLastSlide = false;
@ -60,11 +60,11 @@ export class SurveyFormPage extends BasePage {
}
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') {
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
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') {
const modal = await this.rootPage.locator('.nc-picker-datetime');
await expect(modal).toBeVisible();
@ -76,7 +76,7 @@ export class SurveyFormPage extends BasePage {
async validateSuccessMessage(param: { message: string; showAnotherForm?: boolean }) {
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();
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('[placeholder="Enter table name"]').fill(title);
await this.dashboard.get().getByPlaceholder('Enter table name').fill(title);
await this.waitForResponse({
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 }) {
await this.dashboard
.get()
.locator(`[data-nc="tree-view-table-draggable-handle-${sourceTable}"]`)
.dragTo(this.get().locator(`[data-nc="tree-view-table-${destinationTable}"]`));
.locator(`[data-testid="tree-view-table-draggable-handle-${sourceTable}"]`)
.dragTo(this.get().locator(`[data-testid="tree-view-table-${destinationTable}"]`));
}
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 { DashboardPage } from '../';
import { DashboardPage } from '..';
import BasePage from '../../Base';
export class ViewSidebarPage extends BasePage {
@ -54,7 +54,7 @@ export class ViewSidebarPage extends BasePage {
}
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 }) {
@ -64,7 +64,7 @@ export class ViewSidebarPage extends BasePage {
// Todo: Make selection better
async verifyView({ title, index }: { title: string; index: number }) {
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 });
}
@ -82,13 +82,16 @@ export class ViewSidebarPage extends BasePage {
async reorderViews({ sourceView, destinationView }: { sourceView: string; destinationView: string }) {
await this.dashboard
.get()
.locator(`[data-nc="view-sidebar-drag-handle-${sourceView}"]`)
.dragTo(this.get().locator(`[data-nc="view-sidebar-view-${destinationView}"]`));
.locator(`[data-testid="view-sidebar-drag-handle-${sourceView}"]`)
.dragTo(this.get().locator(`[data-testid="view-sidebar-view-${destinationView}"]`));
}
async deleteView({ title }: { title: string }) {
await this.get().locator(`[data-nc="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-${title}"]`).hover();
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();
@ -101,15 +104,18 @@ export class ViewSidebarPage extends BasePage {
}
async renameView({ title, newTitle }: { title: string; newTitle: string }) {
await this.get().locator(`[data-nc="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}"]`).dblclick();
await this.get().locator(`[data-testid="view-sidebar-view-${title}"]`).locator('input').fill(newTitle);
await this.get().press('Enter');
await this.verifyToast({ message: 'View renamed successfully' });
}
async copyView({ title }: { title: string }) {
await this.get().locator(`[data-nc="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-${title}"]`).hover();
await this.get()
.locator(`[data-testid="view-sidebar-view-actions-${title}"]`)
.locator('.nc-view-copy-icon')
.click();
const submitAction = this.rootPage
.locator('.ant-modal-content')
.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 }) {
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 }) {
const attachFileAction = this.get({ index, columnHeader })
.locator('[data-nc="attachment-cell-file-picker-button"]')
.locator('[data-testid="attachment-cell-file-picker-button"]')
.click();
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.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();
await this.rootPage
.locator(`[data-nc="select-option-${columnHeader}-${index}"]`, { hasText: option })
.getByTestId(`select-option-${columnHeader}-${index}`)
.getByText(option)
.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 {
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 {
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() {
return this.rootPage.locator(`[data-nc="nc-fields-menu"]`);
return this.rootPage.locator(`[data-testid="nc-fields-menu"]`);
}
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 }) {

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() {
return this.rootPage.locator(`[data-nc="toolbar-actions"]`);
return this.rootPage.locator(`[data-testid="toolbar-actions"]`);
}
// 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() {
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
async toggle({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) {
await this.toolbar.clickFields();
const toggleColumn = this.get()
.locator(`[data-nc="nc-fields-menu-${title}"]`)
.locator(`[data-testid="nc-fields-menu-${title}"]`)
.locator('input[type="checkbox"]')
.click();
@ -32,7 +32,7 @@ export class ToolbarFieldsPage extends BasePage {
}
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) {
await expect(checkbox).toBeChecked();
@ -43,7 +43,7 @@ export class ToolbarFieldsPage extends BasePage {
async click({ title, isLocallySaved }: { title: string; isLocallySaved?: boolean }) {
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/',
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() {
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 }) {
@ -24,7 +24,7 @@ export class ToolbarFilterPage extends BasePage {
async verifyFilter({ title }: { title: string }) {
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();
}

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) {
await this.get().locator(`[data-nc="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-nc="nc-modal-share-view__save-password"]`).click();
await this.get().locator(`[data-testid="nc-modal-share-view__with-password"]`).click();
await this.get().locator(`[data-testid="nc-modal-share-view__password"]`).fill(pwd);
await this.get().locator(`[data-testid="nc-modal-share-view__save-password"]`).click();
await this.verifyToast({ message: 'Successfully updated' });
}
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() {
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() {
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() {
@ -37,6 +37,6 @@ export class ToolbarShareViewPage extends BasePage {
}
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() {
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 }) {
@ -78,6 +78,6 @@ export class ToolbarSortPage extends BasePage {
}
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() {
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();
}
@ -70,7 +70,7 @@ export class DashboardPage extends BasePage {
}
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();
// fix me!
@ -82,7 +82,7 @@ export class DashboardPage extends BasePage {
// todo: Fast page transition breaks the vue router
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);
await projectsPage.waitToBeRendered();
}
@ -99,7 +99,7 @@ export class DashboardPage extends BasePage {
state: 'visible',
});
} else {
await this.get().locator('[data-nc="grid-id-column"]').waitFor({
await this.get().getByTestId('grid-id-column').waitFor({
state: 'visible',
});
}
@ -109,13 +109,13 @@ export class DashboardPage extends BasePage {
// wait active tab animation to finish
await expect
.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');
});
})
.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') {
await expect(this.rootPage).toHaveURL(
@ -128,34 +128,35 @@ export class DashboardPage extends BasePage {
// open change password portal
await this.rootPage.locator('.nc-menu-accounts').click();
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();
}
// todo: Move this to a seperate page
async changePassword({ oldPass, newPass, repeatPass }: { oldPass: string; newPass: string; repeatPass: string }) {
// change password
const currentPassword = await this.rootPage.locator('input[data-cy="nc-user-settings-form__current-password"]');
const newPassword = await this.rootPage.locator('input[data-cy="nc-user-settings-form__new-password"]');
const confirmPassword = await this.rootPage.locator('input[data-cy="nc-user-settings-form__new-password-repeat"]');
const currentPassword = this.rootPage.locator('input[data-testid="nc-user-settings-form__current-password"]');
const newPassword = this.rootPage.locator('input[data-testid="nc-user-settings-form__new-password"]');
const confirmPassword = this.rootPage.locator('input[data-testid="nc-user-settings-form__new-password-repeat"]');
await currentPassword.fill(oldPass);
await newPassword.fill(newPass);
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() {
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');
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('[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 }) {
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`);
// menu items
@ -189,11 +190,11 @@ export class DashboardPage extends BasePage {
for (const item of menuItems[param.role]) {
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
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 }) {
if (!withoutPrefix) email = this.prefixEmail(email);
await this.get().locator(`[data-nc="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"]`).waitFor();
await this.get().locator(`[data-testid="nc-form-signin__email"]`).fill(email);
}
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() {
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
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