Browse Source

feat(testing): Added Table Operations

pull/3848/head
Muhammed Mustafa 2 years ago
parent
commit
3aafa89a49
  1. 8
      packages/nc-gui/components/dashboard/TreeView.vue
  2. 10
      packages/nc-gui/components/dashboard/settings/AuditTab.vue
  3. 7
      packages/nc-gui/components/dashboard/settings/Modal.vue
  4. 1
      packages/nc-gui/pages/[projectType]/[projectId]/index.vue
  5. 31
      scripts/playwright/pages/Dashboard.ts
  6. 45
      scripts/playwright/pages/Settings/Audit.ts
  7. 33
      scripts/playwright/pages/Settings/index.ts
  8. 34
      scripts/playwright/tests/tableOperations.spec.ts

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

@ -355,12 +355,16 @@ function openTableCreateDialog() {
<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"> <div class="nc-project-menu-item" :pw-data="`sidebar-table-rename-${table.title}`">
{{ $t('general.rename') }} {{ $t('general.rename') }}
</div> </div>
</a-menu-item> </a-menu-item>
<a-menu-item v-if="isUIAllowed('table-delete')" @click="deleteTable(table)"> <a-menu-item
v-if="isUIAllowed('table-delete')"
:pw-data="`sidebar-table-delete-${table.title}`"
@click="deleteTable(table)"
>
<div class="nc-project-menu-item"> <div class="nc-project-menu-item">
{{ $t('general.delete') }} {{ $t('general.delete') }}
</div> </div>

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

@ -105,7 +105,15 @@ const columns = [
/> />
</div> </div>
<a-table class="w-full" size="small" :data-source="audits ?? []" :columns="columns" :pagination="false" :loading="isLoading"> <a-table
class="w-full"
size="small"
:data-source="audits ?? []"
:columns="columns"
:pagination="false"
:loading="isLoading"
data-pw="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')" />
</template> </template>

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

@ -175,7 +175,12 @@ watch(
{{ $t('activity.settings') }} {{ $t('activity.settings') }}
</a-typography-title> </a-typography-title>
<a-button type="text" class="!rounded-md border-none -mt-1.5 -mr-1" @click="vModel = false"> <a-button
type="text"
class="!rounded-md border-none -mt-1.5 -mr-1"
pw-data="settings-modal-close-button"
@click="vModel = false"
>
<template #icon> <template #icon>
<MdiClose class="cursor-pointer mt-1 nc-modal-close" /> <MdiClose class="cursor-pointer mt-1 nc-modal-close" />
</template> </template>

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

@ -229,6 +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']"
pw-data="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">

31
scripts/playwright/pages/Dashboard.ts

@ -1,14 +1,17 @@
// playwright-dev-page.ts // playwright-dev-page.ts
import { expect, Locator, Page } from '@playwright/test'; import { expect, Locator, Page } from '@playwright/test';
import { BasePage } from './Base';
export class DashboardPage { export class DashboardPage {
readonly project: any; readonly project: any;
readonly page: Page; readonly page: Page;
readonly tablesSideBar: Locator; readonly tablesSideBar: Locator;
readonly tabBar: Locator; readonly tabBar: Locator;
readonly base: BasePage;
constructor(page: Page, project: any) { constructor(page: Page, project: any) {
this.page = page; this.page = page;
this.base = new BasePage(page);
this.project = project; this.project = project;
this.tablesSideBar = page.locator('.nc-treeview-container'); this.tablesSideBar = page.locator('.nc-treeview-container');
this.tabBar = page.locator('.nc-tab-bar'); this.tabBar = page.locator('.nc-tab-bar');
@ -18,6 +21,11 @@ export class DashboardPage {
await this.page.goto(`http://localhost:3000/#/nc/${this.project.id}/auth`); await this.page.goto(`http://localhost:3000/#/nc/${this.project.id}/auth`);
} }
async gotoSettings() {
await this.page.locator('[pw-data="nc-project-menu"]').click();
await this.page.locator('div.nc-project-menu-item:has-text(" Team & Settings")').click();
}
async openTable({ title }: { title: string }) { async openTable({ title }: { title: string }) {
await this.tablesSideBar.locator(`.nc-project-tree-tbl-${title}`).click(); await this.tablesSideBar.locator(`.nc-project-tree-tbl-${title}`).click();
await this.tabBar.textContent().then((text) => expect(text).toContain(title)); await this.tabBar.textContent().then((text) => expect(text).toContain(title));
@ -34,4 +42,27 @@ export class DashboardPage {
await expect(this.page).toHaveURL(`http://localhost:3000/#/nc/${this.project.id}/table/${title}`); await expect(this.page).toHaveURL(`http://localhost:3000/#/nc/${this.project.id}/table/${title}`);
await this.page.locator('[pw-data="grid-load-spinner"]').waitFor({ state: 'hidden' }); await this.page.locator('[pw-data="grid-load-spinner"]').waitFor({ state: 'hidden' });
} }
async verifyTableExistsInSidebar({ title }: { title: string }) {
await expect(this.tablesSideBar.locator(`.nc-project-tree-tbl-${title}`)).toBeVisible();
}
async verifyTableDoesNotExistInSidebar({ title }: { title: string }) {
await expect(await this.tablesSideBar.locator(`.nc-project-tree-tbl-${title}`).count()).toBe(0);
}
async deleteTable({ title }: { title: string }) {
await this.tablesSideBar.locator(`.nc-project-tree-tbl-${title}`).click({ button: 'right' });
await this.page.locator('div.nc-project-menu-item:has-text("Delete")').click();
await this.page.locator('button:has-text("Yes")').click();
await this.base.toastWait({ message:'Deleted table successfully' })
}
async renameTable({ title, newTitle }: { title: string; newTitle: string }) {
await this.tablesSideBar.locator(`.nc-project-tree-tbl-${title}`).click({ button: 'right' });
await this.page.locator('div.nc-project-menu-item:has-text("Rename")').click();
await this.page.locator('[placeholder="Enter table name"]').fill(newTitle);
await this.page.locator('button:has-text("Submit")').click();
await this.base.toastWait({ message:'Table renamed successfully' })
}
} }

45
scripts/playwright/pages/Settings/Audit.ts

@ -0,0 +1,45 @@
// playwright-dev-page.ts
import { Page, expect } from '@playwright/test';
import { SettingsPage } from '.';
export class AuditSettingsPage {
private readonly settings: SettingsPage;
constructor(settings: SettingsPage) {
this.settings = settings;
}
async verifyRow(
{index, opType, opSubtype, description, user, created}:
{index: number,opType?: string, opSubtype?: string, description?: string, user?: string, created?: string}
) {
const table = await this.settings.get().locator(`div[data-pw="audit-tab-table"]`);
const row = table.locator(`tr.ant-table-row`).nth(index);
if(opType) {
await row.locator(`td.ant-table-cell`).nth(0).textContent()
.then((text) => expect(text).toContain(opType));
}
if(opSubtype) {
await row.locator(`td.ant-table-cell`).nth(1).textContent()
.then((text) => expect(text).toContain(opSubtype));
}
if(description) {
await row.locator(`td.ant-table-cell`).nth(2).textContent()
.then((text) => expect(text).toContain(description));
}
if(user) {
await row.locator(`td.ant-table-cell`).nth(3).textContent()
.then((text) => expect(text).toContain(user));
}
if(created) {
await row.locator(`td.ant-table-cell`).nth(4).textContent()
.then((text) => expect(text).toContain(created));
}
}
}

33
scripts/playwright/pages/Settings/index.ts

@ -0,0 +1,33 @@
// playwright-dev-page.ts
import { Page } from '@playwright/test';
import { AuditSettingsPage } from './Audit';
const tabInfo = {
'Team & Auth': 'teamAndAuth',
'App Store': 'appStore',
'Project Metadata': 'projMetaData',
'Audit': 'audit',
}
export class SettingsPage {
private readonly page: Page;
readonly audit: AuditSettingsPage;
constructor(page: Page) {
this.page = page;
this.audit = new AuditSettingsPage(this);
}
get() {
return this.page.locator('.nc-modal-settings');
}
async selectTab({title}: {title: string}) {
await this.page.locator(`li[data-menu-id="${tabInfo[title]}"]`).click();
}
async close() {
await this.page.locator('[pw-data="settings-modal-close-button"]').click();
}
}

34
scripts/playwright/tests/tableOperations.spec.ts

@ -0,0 +1,34 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import { SettingsPage } from '../pages/Settings';
import setup from '../setup';
test.describe('Table Operations', () => {
let dashboard: DashboardPage, settings: SettingsPage;
let context: any;
test.beforeEach(async ({page}) => {
context = await setup({ page });
dashboard = new DashboardPage(page, context.project);
settings = new SettingsPage(page);
})
test('Create, and delete table, verify in audit tab, and rename City table', async () => {
await dashboard.createTable({title: "tablex"});
await dashboard.verifyTableExistsInSidebar({title: "tablex"});
await dashboard.deleteTable({title: "tablex"});
await dashboard.verifyTableDoesNotExistInSidebar({title: "tablex"});
await dashboard.gotoSettings();
await settings.selectTab({title: 'Audit'});
await settings.audit.verifyRow({index: 0, opType: 'TABLE', opSubtype: 'DELETED', user: 'user@nocodb.com'});
await settings.audit.verifyRow({index: 1, opType: 'TABLE', opSubtype: 'CREATED', user: 'user@nocodb.com'});
await settings.close();
await dashboard.renameTable({title: "City", newTitle: "Cityx"});
await dashboard.verifyTableExistsInSidebar({title: "Cityx"});
});
});
Loading…
Cancel
Save