Browse Source

fix(test): Changed tpo data-testid from data-nc

pull/4376/head
Muhammed Mustafa 2 years ago
parent
commit
25d420a331
  1. 2
      packages/nc-gui/components/cell/MultiSelect.vue
  2. 2
      packages/nc-gui/components/cell/SingleSelect.vue
  3. 2
      packages/nc-gui/components/cell/attachment/index.vue
  4. 8
      packages/nc-gui/components/dashboard/TreeView.vue
  5. 2
      packages/nc-gui/components/dashboard/settings/AuditTab.vue
  6. 4
      packages/nc-gui/components/dashboard/settings/Modal.vue
  7. 2
      packages/nc-gui/components/dlg/TableCreate.vue
  8. 2
      packages/nc-gui/components/general/TruncateText.vue
  9. 46
      packages/nc-gui/components/smartsheet/Form.vue
  10. 4
      packages/nc-gui/components/smartsheet/Gallery.vue
  11. 22
      packages/nc-gui/components/smartsheet/Grid.vue
  12. 2
      packages/nc-gui/components/smartsheet/Kanban.vue
  13. 2
      packages/nc-gui/components/smartsheet/Pagination.vue
  14. 2
      packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
  15. 6
      packages/nc-gui/components/smartsheet/column/SelectOptions.vue
  16. 2
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  17. 8
      packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue
  18. 2
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilterMenu.vue
  19. 4
      packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue
  20. 26
      packages/nc-gui/components/smartsheet/toolbar/ShareView.vue
  21. 2
      packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue
  22. 2
      packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue
  23. 2
      packages/nc-gui/components/tabs/auth/user-management/ShareBase.vue
  24. 13
      packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
  25. 8
      packages/nc-gui/layouts/base.vue
  26. 2
      packages/nc-gui/layouts/shared-view.vue
  27. 4
      packages/nc-gui/pages/[projectType]/[projectId]/index.vue
  28. 6
      packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue
  29. 6
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/index.vue
  30. 43
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue
  31. 6
      packages/nc-gui/pages/index/index/index.vue
  32. 12
      packages/nc-gui/pages/index/index/user.vue
  33. 15
      packages/nc-gui/pages/signin.vue
  34. 1
      packages/nc-gui/tests/playwright/package.json
  35. 8
      packages/nc-gui/tests/playwright/pages/Dashboard/ExpandedForm/index.ts
  36. 72
      packages/nc-gui/tests/playwright/pages/Dashboard/Form/index.ts
  37. 2
      packages/nc-gui/tests/playwright/pages/Dashboard/Gallery/index.ts
  38. 10
      packages/nc-gui/tests/playwright/pages/Dashboard/Grid/Column/LTAR/ChildList.ts
  39. 14
      packages/nc-gui/tests/playwright/pages/Dashboard/Grid/Column/SelectOptionColumn.ts
  40. 2
      packages/nc-gui/tests/playwright/pages/Dashboard/Grid/Column/index.ts
  41. 36
      packages/nc-gui/tests/playwright/pages/Dashboard/Grid/index.ts
  42. 4
      packages/nc-gui/tests/playwright/pages/Dashboard/Kanban/index.ts
  43. 2
      packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Acl.ts
  44. 2
      packages/nc-gui/tests/playwright/pages/Dashboard/Settings/AppStore.ts
  45. 2
      packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Audit.ts
  46. 2
      packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Erd.ts
  47. 2
      packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Metadata.ts
  48. 2
      packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Miscellaneous.ts
  49. 4
      packages/nc-gui/tests/playwright/pages/Dashboard/Settings/Teams.ts
  50. 2
      packages/nc-gui/tests/playwright/pages/Dashboard/Settings/index.ts
  51. 26
      packages/nc-gui/tests/playwright/pages/Dashboard/SurveyForm/index.ts
  52. 6
      packages/nc-gui/tests/playwright/pages/Dashboard/TreeView.ts
  53. 26
      packages/nc-gui/tests/playwright/pages/Dashboard/ViewSidebar/index.ts
  54. 4
      packages/nc-gui/tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts
  55. 5
      packages/nc-gui/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts
  56. 4
      packages/nc-gui/tests/playwright/pages/Dashboard/common/Cell/index.ts
  57. 4
      packages/nc-gui/tests/playwright/pages/Dashboard/common/ProjectMenu/index.ts
  58. 2
      packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/Actions/index.ts
  59. 8
      packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/Fields.ts
  60. 4
      packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/Filter.ts
  61. 14
      packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/ShareView.ts
  62. 4
      packages/nc-gui/tests/playwright/pages/Dashboard/common/Toolbar/Sort.ts
  63. 33
      packages/nc-gui/tests/playwright/pages/Dashboard/index.ts
  64. 8
      packages/nc-gui/tests/playwright/pages/LoginPage/index.ts
  65. 6
      packages/nc-gui/tests/playwright/pages/ProjectsPage/index.ts
  66. 2
      packages/nc-gui/tests/playwright/pages/SharedForm/index.ts
  67. 4
      packages/nc-gui/tests/playwright/setup/index.ts
  68. 2
      packages/nc-gui/tests/playwright/tests/authChangePassword.spec.ts

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

6
packages/nc-gui/tests/playwright/pages/ProjectsPage/index.ts

@ -13,7 +13,7 @@ export class ProjectsPage extends BasePage {
} }
get() { get() {
return this.rootPage.locator('[data-nc="projects-container"]'); return this.rootPage.locator('[data-testid="projects-container"]');
} }
// create project // create project
@ -55,7 +55,7 @@ export class ProjectsPage extends BasePage {
} }
async reloadProjects() { async reloadProjects() {
const reloadUiAction = this.get().locator('[data-nc="projects-reload-button"]').click(); const reloadUiAction = this.get().locator('[data-testid="projects-reload-button"]').click();
await this.waitForResponse({ await this.waitForResponse({
uiAction: reloadUiAction, uiAction: reloadUiAction,
requestUrlPathToMatch: '/api/v1/db/meta/projects', requestUrlPathToMatch: '/api/v1/db/meta/projects',
@ -133,7 +133,7 @@ export class ProjectsPage extends BasePage {
async deleteProject({ title, withoutPrefix }: { title: string; withoutPrefix?: boolean }) { async deleteProject({ title, withoutPrefix }: { title: string; withoutPrefix?: boolean }) {
if (!withoutPrefix) title = this.prefixTitle(title); if (!withoutPrefix) title = this.prefixTitle(title);
await this.get().locator(`[data-nc="delete-project-${title}"]`).click(); await this.get().locator(`[data-testid="delete-project-${title}"]`).click();
await this.rootPage.locator(`button:has-text("Yes")`).click(); await this.rootPage.locator(`button:has-text("Yes")`).click();
await this.get().locator('.ant-table-row', { hasText: title }).waitFor({ state: 'hidden' }); await this.get().locator('.ant-table-row', { hasText: title }).waitFor({ state: 'hidden' });

2
packages/nc-gui/tests/playwright/pages/SharedForm/index.ts

@ -16,7 +16,7 @@ export class SharedFormPage extends BasePage {
async submit() { async submit() {
await this.waitForResponse({ await this.waitForResponse({
uiAction: this.get().locator('[data-nc="shared-form-submit-button"]').click(), uiAction: this.get().locator('[data-testid="shared-form-submit-button"]').click(),
httpMethodsToMatch: ['POST'], httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: '/rows', requestUrlPathToMatch: '/rows',
}); });

4
packages/nc-gui/tests/playwright/setup/index.ts

@ -1,4 +1,4 @@
import { Page } from '@playwright/test'; import { Page, selectors } from '@playwright/test';
import axios from 'axios'; import axios from 'axios';
export interface NcContext { export interface NcContext {
@ -7,6 +7,8 @@ export interface NcContext {
dbType?: string; dbType?: string;
} }
selectors.setTestIdAttribute('data-testid');
const setup = async ({ page, isEmptyProject }: { page: Page; isEmptyProject?: boolean }): Promise<NcContext> => { const setup = async ({ page, isEmptyProject }: { page: Page; isEmptyProject?: boolean }): Promise<NcContext> => {
let dbType = process.env.CI ? process.env.E2E_DB_TYPE : process.env.E2E_DEV_DB_TYPE; let dbType = process.env.CI ? process.env.E2E_DB_TYPE : process.env.E2E_DEV_DB_TYPE;
dbType = dbType || 'sqlite'; dbType = dbType || 'sqlite';

2
packages/nc-gui/tests/playwright/tests/authChangePassword.spec.ts

@ -46,7 +46,7 @@ test.describe('Auth', () => {
repeatPass: '123456789', repeatPass: '123456789',
}); });
await dashboard.rootPage await dashboard.rootPage
.locator('[data-cy="nc-user-settings-form__error"]:has-text("Current password is wrong")') .locator('[data-testid="nc-user-settings-form__error"]:has-text("Current password is wrong")')
.waitFor(); .waitFor();
// New pass and repeat pass mismatch // New pass and repeat pass mismatch

Loading…
Cancel
Save