Browse Source

fix: Integrated view sidebar removal to tests

pull/6444/head
Muhammed Mustafa 1 year ago
parent
commit
01c866758f
  1. 8
      packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue
  2. 6
      packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue
  3. 1
      packages/nc-gui/components/dashboard/TreeView/TableList.vue
  4. 9
      packages/nc-gui/components/dashboard/TreeView/TableNode.vue
  5. 5
      packages/nc-gui/components/dashboard/TreeView/ViewsList.vue
  6. 8
      packages/nc-gui/components/dashboard/TreeView/ViewsNode.vue
  7. 4
      packages/nc-gui/components/nc/Select.vue
  8. 1
      packages/nc-gui/lib/acl.ts
  9. 46
      tests/playwright/pages/Dashboard/Sidebar/index.ts
  10. 51
      tests/playwright/pages/Dashboard/ViewSidebar/index.ts
  11. 4
      tests/playwright/pages/Dashboard/index.ts
  12. 2
      tests/playwright/tests/db/columns/columnAttachments.spec.ts
  13. 4
      tests/playwright/tests/db/features/undo-redo.spec.ts
  14. 16
      tests/playwright/tests/db/general/views.spec.ts
  15. 4
      tests/playwright/tests/db/views/viewForm.spec.ts
  16. 8
      tests/playwright/tests/db/views/viewKanban.spec.ts

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

@ -71,7 +71,7 @@ function onOpenModal({
<template #overlay>
<NcMenu class="max-w-48">
<NcMenuItem @click="onOpenModal({ type: ViewTypes.GRID })">
<div class="item">
<div class="item" data-testid="sidebar-view-create-grid">
<div class="item-inner">
<GeneralViewIcon :meta="{ type: ViewTypes.GRID }" />
<div>Grid</div>
@ -82,7 +82,7 @@ function onOpenModal({
</NcMenuItem>
<NcMenuItem @click="onOpenModal({ type: ViewTypes.FORM })">
<div class="item">
<div class="item" data-testid="sidebar-view-create-form">
<div class="item-inner">
<GeneralViewIcon :meta="{ type: ViewTypes.FORM }" />
<div>Form</div>
@ -92,7 +92,7 @@ function onOpenModal({
</div>
</NcMenuItem>
<NcMenuItem @click="onOpenModal({ type: ViewTypes.GALLERY })">
<div class="item">
<div class="item" data-testid="sidebar-view-create-gallery">
<div class="item-inner">
<GeneralViewIcon :meta="{ type: ViewTypes.GALLERY }" />
<div>Gallery</div>
@ -101,7 +101,7 @@ function onOpenModal({
<GeneralIcon class="plus" icon="plus" />
</div>
</NcMenuItem>
<NcMenuItem @click="onOpenModal({ type: ViewTypes.KANBAN })">
<NcMenuItem data-testid="sidebar-view-create-kanban" @click="onOpenModal({ type: ViewTypes.KANBAN })">
<div class="item">
<div class="item-inner">
<GeneralViewIcon :meta="{ type: ViewTypes.KANBAN }" />

6
packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue

@ -440,7 +440,11 @@ const DlgProjectDuplicateOnOk = async (jobData: { id: string; project_id: string
</span>
<div :class="{ 'flex flex-grow h-full': !editMode }" @click="onProjectClick(project)"></div>
<NcDropdown v-model:visible="isOptionsOpen" :trigger="['click']">
<NcDropdown
v-if="isUIAllowed('tableCreate', { roles: projectRole })"
v-model:visible="isOptionsOpen"
:trigger="['click']"
>
<NcButton
class="nc-sidebar-node-btn"
:class="{ '!text-black !opacity-100': isOptionsOpen }"

1
packages/nc-gui/components/dashboard/TreeView/TableList.vue

@ -152,7 +152,6 @@ const availableTables = computed(() => {
class="nc-tree-item text-sm"
:data-order="table.order"
:data-id="table.id"
:data-testid="`tree-view-table-${table.title}`"
:table="table"
:project="project"
:base-index="baseIndex"

9
packages/nc-gui/components/dashboard/TreeView/TableNode.vue

@ -123,12 +123,12 @@ const isTableOpened = computed(() => {
<template>
<div
class="nc-tree-item text-sm select-none w-full"
class="nc-tree-item nc-table-node-wrapper text-sm select-none w-full"
:data-order="table.order"
:data-id="table.id"
:data-testid="`tree-view-table-${table.title}`"
:data-table-id="table.id"
:class="[`nc-project-tree-tbl nc-project-tree-tbl-${table.title}`]"
:data-active="openedTableId === table.id"
>
<GeneralTooltip
class="nc-tree-item-inner pl-11 pr-0.75 mb-0.25 rounded-md h-7.1 w-full group cursor-pointer hover:bg-gray-200"
@ -143,6 +143,7 @@ const isTableOpened = computed(() => {
<template #title>{{ table.table_name }}</template>
<div
class="table-context flex items-center gap-1 h-full"
:data-testid="`tree-view-table-${table.title}`"
@contextmenu="setMenuContext('table', table)"
@click="openTable(table)"
>
@ -260,8 +261,8 @@ const isTableOpened = computed(() => {
</NcMenu>
</template>
</NcDropdown>
<DashboardTreeViewCreateViewBtn>
<NcButton type="text" size="xxsmall" class="nc-sidebar-node-btn">
<DashboardTreeViewCreateViewBtn v-if="isUIAllowed('viewCreateOrEdit')">
<NcButton type="text" size="xxsmall" class="nc-create-view-btn nc-sidebar-node-btn">
<GeneralIcon icon="plus" class="text-xl leading-5" style="-webkit-text-stroke: 0.15px" />
</NcButton>
</DashboardTreeViewCreateViewBtn>

5
packages/nc-gui/components/dashboard/TreeView/ViewsList.vue

@ -346,7 +346,10 @@ function onOpenModal({
</script>
<template>
<DashboardTreeViewCreateViewBtn :overlay-class-name="isDefaultBase ? '!left-18 !min-w-42' : '!left-25 !min-w-42'">
<DashboardTreeViewCreateViewBtn
v-if="isUIAllowed('viewCreateOrEdit')"
:overlay-class-name="isDefaultBase ? '!left-18 !min-w-42' : '!left-25 !min-w-42'"
>
<NcButton
type="text"
size="xsmall"

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

@ -270,7 +270,7 @@ function onRef(el: HTMLElement) {
<NcButton
type="text"
size="xxsmall"
class="nc-sidebar-node-btn invisible !group-hover:visible"
class="nc-sidebar-node-btn invisible !group-hover:visible nc-sidebar-view-node-context-btn"
:class="{
'!visible': isDropdownOpen,
}"
@ -286,15 +286,15 @@ function onRef(el: HTMLElement) {
<div class="-ml-0.25">Rename</div>
</NcMenuItem>
<NcMenuItem @click.stop="onDuplicate">
<GeneralIcon icon="duplicate" />
<GeneralIcon icon="duplicate" class="nc-view-copy-icon" />
Duplicate
</NcMenuItem>
<NcDivider />
<template v-if="!vModel.is_default">
<NcMenuItem class="!text-red-500" @click.stop="onDelete">
<GeneralIcon icon="delete" class="text-sm" />
<NcMenuItem class="!text-red-500" l @click.stop="onDelete">
<GeneralIcon icon="delete" class="text-sm nc-view-delete-icon" />
<div class="-ml-0.25">Delete</div>
</NcMenuItem>
</template>

4
packages/nc-gui/components/nc/Select.vue

@ -42,8 +42,8 @@ const onChange = (value: string) => {
:filter-option="filterOption"
:dropdown-match-select-width="dropdownMatchSelectWidth"
:allow-clear="allowClear"
:loading="true"
:disabled="true"
:loading="loading"
:disabled="loading"
@change="onChange"
>
<template #suffixIcon>

1
packages/nc-gui/lib/acl.ts

@ -25,6 +25,7 @@ const rolePermissions = {
projectDelete: true,
projectDuplicate: true,
newUser: true,
viewCreateOrEdit: true,
},
},
[OrgUserRoles.VIEWER]: {

46
tests/playwright/pages/Dashboard/Sidebar/index.ts

@ -1,5 +1,5 @@
import { expect, Locator } from '@playwright/test';
import { ProjectTypes } from 'nocodb-sdk';
import { ProjectTypes, ViewTypes } from 'nocodb-sdk';
import { DashboardPage } from '..';
import BasePage from '../../Base';
import { DocsSidebarPage } from './DocsSidebar';
@ -59,4 +59,48 @@ export class SidebarPage extends BasePage {
});
await this.dashboard.docs.pagesList.waitForOpen({ title });
}
async createView({ title, type }: { title: string; type: ViewTypes }) {
const createViewButtonOfActiveProject = this.dashboard
.get()
.locator('.nc-table-node-wrapper[data-active="true"] .nc-create-view-btn');
await createViewButtonOfActiveProject.waitFor({ state: 'visible' });
await createViewButtonOfActiveProject.scrollIntoViewIfNeeded();
await createViewButtonOfActiveProject.click();
if (type === ViewTypes.GRID) {
await this.rootPage.getByTestId('sidebar-view-create-grid').last().click({ force: true });
} else if (type === ViewTypes.FORM) {
await this.rootPage.getByTestId('sidebar-view-create-form').last().click({ force: true });
} else if (type === ViewTypes.KANBAN) {
await this.rootPage.getByTestId('sidebar-view-create-kanban').last().click({ force: true });
} else if (type === ViewTypes.GALLERY) {
await this.rootPage.getByTestId('sidebar-view-create-gallery').last().click({ force: true });
}
await this.rootPage.locator('input[id="form_item_title"]:visible').waitFor({ state: 'visible' });
await this.rootPage.locator('input[id="form_item_title"]:visible').fill(title);
const submitAction = () =>
this.rootPage.locator('.ant-modal-content').locator('button.ant-btn.ant-btn-primary').click();
await this.waitForResponse({
httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: '/api/v1/db/meta/tables/',
uiAction: submitAction,
responseJsonMatcher: json => json.title === title,
});
// Todo: Wait for view to be rendered
await this.rootPage.waitForTimeout(1000);
}
async verifyCreateViewButtonVisibility({ isVisible }: { isVisible: boolean }) {
const createViewButtonOfActiveProject = this.dashboard
.get()
.locator('.nc-table-node-wrapper[data-active="true"] .nc-create-view-btn');
if (isVisible) {
await expect(createViewButtonOfActiveProject).toBeVisible();
} else {
await expect(createViewButtonOfActiveProject).toHaveCount(0);
}
}
}

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

@ -1,6 +1,7 @@
import { expect, Locator } from '@playwright/test';
import { DashboardPage } from '..';
import BasePage from '../../Base';
import { ViewTypes } from 'nocodb-sdk';
export class ViewSidebarPage extends BasePage {
readonly project: any;
@ -31,7 +32,7 @@ export class ViewSidebarPage extends BasePage {
}
get() {
return this.dashboard.get().locator('.nc-view-sidebar');
return this.dashboard.get().locator('.nc-table-node-wrapper[data-active="true"]');
}
async isVisible() {
@ -53,34 +54,22 @@ export class ViewSidebarPage extends BasePage {
await this.rootPage.goto(this.rootPage.url());
}
private async createView({ title, locator }: { title: string; locator: Locator }) {
await this.rootPage.waitForTimeout(1000);
await locator.click();
await this.rootPage.locator('input[id="form_item_title"]:visible').waitFor({ state: 'visible' });
await this.rootPage.locator('input[id="form_item_title"]:visible').fill(title);
const submitAction = () =>
this.rootPage.locator('.ant-modal-content').locator('button.ant-btn.ant-btn-primary').click({ force: true });
await this.waitForResponse({
httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: '/api/v1/db/meta/tables/',
uiAction: submitAction,
responseJsonMatcher: json => json.title === title,
});
await this.verifyToast({ message: 'View created successfully' });
// Todo: Wait for view to be rendered
private async createView({ title, type }: { title: string; type: ViewTypes }) {
await this.rootPage.waitForTimeout(1000);
await this.dashboard.sidebar.createView({ title, type });
}
async createGalleryView({ title }: { title: string }) {
await this.createView({ title, locator: this.createGalleryButton });
await this.createView({ title, type: ViewTypes.GALLERY });
}
async createGridView({ title }: { title: string }) {
await this.createView({ title, locator: this.createGridButton });
await this.createView({ title, type: ViewTypes.GRID });
}
async createFormView({ title }: { title: string }) {
await this.createView({ title, locator: this.createFormButton });
await this.createView({ title, type: ViewTypes.FORM });
}
async openView({ title }: { title: string }) {
@ -90,13 +79,13 @@ export class ViewSidebarPage extends BasePage {
}
async createKanbanView({ title }: { title: string }) {
await this.createView({ title, locator: this.createKanbanButton });
await this.createView({ title, type: ViewTypes.KANBAN });
await this.rootPage.waitForTimeout(1500);
}
async createMapView({ title }: { title: string }) {
await this.createView({ title, locator: this.createMapButton });
await this.createView({ title, type: ViewTypes.MAP });
}
// Todo: Make selection better
@ -135,13 +124,15 @@ export class ViewSidebarPage extends BasePage {
await this.get().locator(`[data-testid="view-sidebar-view-${title}"]`).hover();
await this.get()
.locator(`[data-testid="view-sidebar-view-${title}"]`)
.locator('.nc-view-sidebar-node-context-btn')
.locator('.nc-sidebar-view-node-context-btn')
.click();
await this.rootPage
.locator(`[data-testid="view-sidebar-view-actions-${title}"]`)
.locator('.nc-view-delete-icon')
.click();
.click({
force: true,
});
await this.rootPage.locator('button:has-text("Delete View"):visible').click();
}
@ -157,13 +148,15 @@ export class ViewSidebarPage extends BasePage {
await this.get().locator(`[data-testid="view-sidebar-view-${title}"]`).hover();
await this.get()
.locator(`[data-testid="view-sidebar-view-${title}"]`)
.locator('.nc-view-sidebar-node-context-btn')
.locator('.nc-sidebar-view-node-context-btn')
.click();
await this.rootPage
.locator(`[data-testid="view-sidebar-view-actions-${title}"]`)
.locator('.nc-view-copy-icon')
.click();
.click({
force: true,
});
const submitAction = () =>
this.rootPage.locator('.ant-modal-content').locator('button:has-text("Create View"):visible').click();
await this.waitForResponse({
@ -198,11 +191,9 @@ export class ViewSidebarPage extends BasePage {
}
async validateRoleAccess(param: { role: string }) {
const count = param.role.toLowerCase() === 'creator' ? 1 : 0;
await expect(this.createGridButton).toHaveCount(count);
await expect(this.createGalleryButton).toHaveCount(count);
await expect(this.createFormButton).toHaveCount(count);
await expect(this.createKanbanButton).toHaveCount(count);
await this.dashboard.sidebar.verifyCreateViewButtonVisibility({
isVisible: param.role.toLowerCase() === 'creator',
});
// await this.openDeveloperTab({});
// await expect(this.erdButton).toHaveCount(1);

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

@ -191,6 +191,10 @@ export class DashboardPage extends BasePage {
await this.sidebar.userMenu.click();
await this.rootPage.getByTestId('nc-sidebar-user-logout').waitFor({ state: 'visible' });
await this.sidebar.userMenu.clickLogout();
// TODO: Remove this
await this.rootPage.reload();
await this.rootPage.locator('[data-testid="nc-form-signin"]:visible').waitFor();
await new Promise(resolve => setTimeout(resolve, 150));
}

2
tests/playwright/tests/db/columns/columnAttachments.spec.ts

@ -51,7 +51,7 @@ test.describe('Attachment column', () => {
});
await dashboard.rootPage.waitForTimeout(500);
const sharedFormUrl = await dashboard.form.topbar.getSharedViewUrl();
await dashboard.viewSidebar.openView({ title: 'Country' });
await dashboard.treeView.openTable({ title: 'Country' });
// Verify attachment in shared form
const newPage = await context.newPage();

4
tests/playwright/tests/db/features/undo-redo.spec.ts

@ -457,10 +457,10 @@ test.describe('Undo Redo - Table & view rename operations', () => {
break;
}
await dashboard.viewSidebar.renameView({ title: viewTypes[i], newTitle: 'newNameForTest' });
await dashboard.viewSidebar.verifyView({ title: 'newNameForTest', index: 1 });
await dashboard.viewSidebar.verifyView({ title: 'newNameForTest', index: 0 });
await new Promise(resolve => setTimeout(resolve, 100));
await undo({ page, dashboard });
await dashboard.viewSidebar.verifyView({ title: viewTypes[i], index: 1 });
await dashboard.viewSidebar.verifyView({ title: viewTypes[i], index: 0 });
await dashboard.viewSidebar.deleteView({ title: viewTypes[i] });
}
});

16
tests/playwright/tests/db/general/views.spec.ts

@ -21,29 +21,29 @@ test.describe('Views CRUD Operations', () => {
test('Create views, reorder and delete', async () => {
await dashboard.treeView.openTable({ title: 'City' });
await dashboard.viewSidebar.createGridView({ title: 'CityGrid' });
await dashboard.viewSidebar.verifyView({ title: 'CityGrid', index: 1 });
await dashboard.viewSidebar.verifyView({ title: 'CityGrid', index: 0 });
await dashboard.viewSidebar.renameView({
title: 'CityGrid',
newTitle: 'CityGrid2',
});
await dashboard.viewSidebar.verifyView({
title: 'CityGrid2',
index: 1,
index: 0,
});
await dashboard.viewSidebar.createFormView({ title: 'CityForm' });
await dashboard.viewSidebar.verifyView({ title: 'CityForm', index: 2 });
await dashboard.viewSidebar.verifyView({ title: 'CityForm', index: 1 });
await dashboard.viewSidebar.renameView({
title: 'CityForm',
newTitle: 'CityForm2',
});
await dashboard.viewSidebar.verifyView({
title: 'CityForm2',
index: 2,
index: 1,
});
await dashboard.viewSidebar.createGalleryView({ title: 'CityGallery' });
await dashboard.viewSidebar.verifyView({ title: 'CityGallery', index: 3 });
await dashboard.viewSidebar.verifyView({ title: 'CityGallery', index: 2 });
await dashboard.viewSidebar.renameView({
title: 'CityGallery',
newTitle: 'CityGallery2',
@ -51,7 +51,7 @@ test.describe('Views CRUD Operations', () => {
await dashboard.viewSidebar.verifyView({
title: 'CityGallery2',
index: 3,
index: 2,
});
await dashboard.viewSidebar.changeViewIcon({
@ -77,14 +77,14 @@ test.describe('Views CRUD Operations', () => {
await dashboard.viewSidebar.deleteView({ title: 'CityForm2' });
await dashboard.viewSidebar.verifyViewNotPresent({
title: 'CityForm2',
index: 2,
index: 1,
});
// fix index after enabling reorder test
await dashboard.viewSidebar.deleteView({ title: 'CityGallery2' });
await dashboard.viewSidebar.verifyViewNotPresent({
title: 'CityGallery2',
index: 1,
index: 0,
});
});

4
tests/playwright/tests/db/views/viewForm.spec.ts

@ -31,7 +31,7 @@ test.describe('Form view', () => {
await dashboard.treeView.openTable({ title: 'Country' });
await dashboard.viewSidebar.createFormView({ title: 'CountryForm' });
await dashboard.viewSidebar.verifyView({ title: 'CountryForm', index: 1 });
await dashboard.viewSidebar.verifyView({ title: 'CountryForm', index: 0 });
// verify form-view fields order
await form.verifyFormViewFieldsOrder({
@ -92,7 +92,7 @@ test.describe('Form view', () => {
await dashboard.treeView.openTable({ title: 'Country' });
await dashboard.viewSidebar.createFormView({ title: 'CountryForm' });
await dashboard.viewSidebar.verifyView({ title: 'CountryForm', index: 1 });
await dashboard.viewSidebar.verifyView({ title: 'CountryForm', index: 0 });
await form.configureHeader({
title: 'Country',

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

@ -61,7 +61,7 @@ test.describe('View', () => {
});
await dashboard.viewSidebar.verifyView({
title: 'Film Kanban',
index: 1,
index: 0,
});
// configure stack-by field
@ -202,7 +202,7 @@ test.describe('View', () => {
});
await dashboard.viewSidebar.verifyView({
title: 'Film Kanban',
index: 1,
index: 0,
});
await toolbar.sort.add({
@ -227,7 +227,7 @@ test.describe('View', () => {
await dashboard.viewSidebar.copyView({ title: 'Film Kanban' });
await dashboard.viewSidebar.verifyView({
title: 'Untitled Kanban',
index: 2,
index: 1,
});
const kanban = dashboard.kanban;
await kanban.verifyStackCount({ count: 6 });
@ -328,7 +328,7 @@ test.describe('View', () => {
});
await dashboard.viewSidebar.verifyView({
title: 'Film Kanban',
index: 1,
index: 0,
});
// Share view

Loading…
Cancel
Save