Browse Source

Merge pull request #4547 from nocodb/feat/field-option-followup

test: Field menu followup - playwright test
pull/4551/head
Pranav C 2 years ago committed by GitHub
parent
commit
046ab0bfef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .github/workflows/ci-cd.yml
  2. 30
      packages/nc-gui/components/smartsheet/header/Menu.vue
  3. 8
      packages/nc-gui/lang/en.json
  4. 93
      tests/playwright/pages/Dashboard/Grid/Column/index.ts
  5. 2
      tests/playwright/pages/Dashboard/common/Cell/index.ts
  6. 134
      tests/playwright/tests/columnMenuOperations.spec.ts

3
.github/workflows/ci-cd.yml

@ -18,6 +18,7 @@ on:
- "packages/nc-gui/**"
- "packages/nocodb/**"
- ".github/workflows/ci-cd.yml"
- "tests/playwright/**"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@ -97,4 +98,4 @@ jobs:
uses: ./.github/workflows/playwright-test-workflow.yml
with:
db: pg
shard: 2
shard: 2

30
packages/nc-gui/components/smartsheet/header/Menu.vue

@ -225,23 +225,26 @@ const hideField = async () => {
<template v-if="column.uidt !== UITypes.LinkToAnotherRecord || column.colOptions.type !== RelationTypes.BELONGS_TO">
<a-divider class="!my-0" />
<a-menu-item @click="sortByColumn('asc')">
<div class="nc-column-insert-after nc-header-menu-item">
<div v-e="['a:field:sort', { dir: 'asc' }]" class="nc-column-insert-after nc-header-menu-item">
<MdiSortAscending class="text-primary" />
Sort Ascending
<!-- Sort Ascending -->
{{ $t('general.sortAsc') }}
</div>
</a-menu-item>
<a-menu-item @click="sortByColumn('desc')">
<div class="nc-column-insert-before nc-header-menu-item">
<div v-e="['a:field:sort', { dir: 'desc' }]" class="nc-column-insert-before nc-header-menu-item">
<MdiSortDescending class="text-primary" />
Sort Descending
<!-- Sort Descending -->
{{ $t('general.sortDesc') }}
</div>
</a-menu-item>
</template>
<a-divider class="!my-0" />
<a-menu-item @click="hideField">
<div class="nc-column-insert-before nc-header-menu-item">
<div v-e="['a:field:hide']" class="nc-column-insert-before nc-header-menu-item">
<MdiEyeOffOutline class="text-primary" />
Hide Field
<!-- Hide Field -->
{{ $t('general.hideField') }}
</div>
</a-menu-item>
@ -251,21 +254,24 @@ const hideField = async () => {
v-if="column.uidt !== UITypes.LinkToAnotherRecord && column.uidt !== UITypes.Lookup && !column.pk"
@click="duplicateColumn"
>
<div class="nc-column-duplicate nc-header-menu-item">
<div v-e="['a:field:duplicate']" class="nc-column-duplicate nc-header-menu-item">
<MdiFileReplaceOutline class="text-primary" />
Duplicate
<!-- Duplicate -->
{{ t('general.duplicate') }}
</div>
</a-menu-item>
<a-menu-item @click="addColumn()">
<div class="nc-column-insert-after nc-header-menu-item">
<div v-e="['a:field:insert:after']" class="nc-column-insert-after nc-header-menu-item">
<MdiTableColumnPlusAfter class="text-primary" />
Insert After
<!-- Insert After -->
{{ t('general.insertAfter') }}
</div>
</a-menu-item>
<a-menu-item @click="addColumn(true)">
<div class="nc-column-insert-before nc-header-menu-item">
<div v-e="['a:field:insert:before']" class="nc-column-insert-before nc-header-menu-item">
<MdiTableColumnPlusBefore class="text-primary" />
Insert before
<!-- Insert Before -->
{{ t('general.insertBefore') }}
</div>
</a-menu-item>
<a-divider class="!my-0" />

8
packages/nc-gui/lang/en.json

@ -69,7 +69,13 @@
"betaNote": "This feature is currently in beta.",
"moreInfo": "More information can be found here",
"logs": "Logs",
"groupingField": "Grouping Field"
"groupingField": "Grouping Field",
"duplicate": "Duplicate",
"insertAfter": "Insert After",
"insertBefore": "Insert Before",
"hideField": "Hide Field",
"sortAsc": "Sort Ascending",
"sortDesc": "Sort Descending"
},
"objects": {
"project": "Project",

93
tests/playwright/pages/Dashboard/Grid/Column/index.ts

@ -1,4 +1,4 @@
import { expect, Page } from '@playwright/test';
import { expect } from '@playwright/test';
import { GridPage } from '..';
import BasePage from '../../../Base';
import { SelectOptionColumnPageObject } from './SelectOptionColumn';
@ -35,6 +35,8 @@ export class ColumnPageObject extends BasePage {
relationType = '',
rollupType = '',
format = '',
insertAfterColumnTitle,
insertBeforeColumnTitle,
}: {
title: string;
type?: string;
@ -45,8 +47,19 @@ export class ColumnPageObject extends BasePage {
relationType?: string;
rollupType?: string;
format?: string;
insertBeforeColumnTitle?: string;
insertAfterColumnTitle?: string;
}) {
await this.grid.get().locator('.nc-column-add').click();
if (insertBeforeColumnTitle) {
await this.grid.get().locator(`th[data-title="${insertBeforeColumnTitle}"] .nc-ui-dt-dropdown`).click();
await this.rootPage.locator('li[role="menuitem"]:has-text("Insert Before"):visible').click();
} else if (insertAfterColumnTitle) {
await this.grid.get().locator(`th[data-title="${insertAfterColumnTitle}"] .nc-ui-dt-dropdown`).click();
await this.rootPage.locator('li[role="menuitem"]:has-text("Insert After"):visible').click();
} else {
await this.grid.get().locator('.nc-column-add').click();
}
await this.rootPage.waitForTimeout(500);
await this.fillTitle({ title });
await this.rootPage.waitForTimeout(500);
@ -54,8 +67,6 @@ export class ColumnPageObject extends BasePage {
await this.rootPage.waitForTimeout(500);
switch (type) {
case 'SingleTextLine':
break;
case 'SingleSelect':
case 'MultiSelect':
await this.selectOption.addOption({
@ -142,6 +153,30 @@ export class ColumnPageObject extends BasePage {
}
await this.save();
// verify column inserted after the target column
if (insertAfterColumnTitle) {
const headersText = await this.grid.get().locator(`th`).allTextContents();
await expect(
this.grid
.get()
.locator(`th`)
.nth(headersText.findIndex(title => title.startsWith(insertAfterColumnTitle)) + 1)
).toHaveText(title);
}
// verify column inserted before the target column
if (insertBeforeColumnTitle) {
const headersText = await this.grid.get().locator(`th`).allTextContents();
await expect(
this.grid
.get()
.locator(`th`)
.nth(headersText.findIndex(title => title.startsWith(insertBeforeColumnTitle)) - 1)
).toHaveText(title);
}
}
async fillTitle({ title }: { title: string }) {
@ -213,6 +248,26 @@ export class ColumnPageObject extends BasePage {
}
}
async duplicateColumn({ title, expectedTitle = `${title}_copy` }: { title: string; expectedTitle?: string }) {
await this.grid.get().locator(`th[data-title="${title}"] .nc-ui-dt-dropdown`).click();
await this.rootPage.locator('li[role="menuitem"]:has-text("Duplicate"):visible').click();
await this.verifyToast({ message: 'Column duplicated successfully' });
await this.grid.get().locator(`th[data-title="${expectedTitle}"]`).isVisible();
}
async hideColumn({ title }: { title: string }) {
await this.grid.get().locator(`th[data-title="${title}"] .nc-ui-dt-dropdown`).click();
await this.waitForResponse({
uiAction: this.rootPage.locator('li[role="menuitem"]:has-text("Hide Field"):visible').click(),
requestUrlPathToMatch: 'api/v1/db/meta/views',
httpMethodsToMatch: ['PATCH'],
});
await expect(this.grid.get().locator(`th[data-title="${title}"]`)).toHaveCount(0);
}
async save({ isUpdated }: { isUpdated?: boolean } = {}) {
await this.waitForResponse({
uiAction: this.get().locator('button:has-text("Save")').click(),
@ -245,4 +300,34 @@ export class ColumnPageObject extends BasePage {
await this.grid.get().locator('.nc-ui-dt-dropdown:visible').first().click();
}
}
async sortColumn({ title, direction = 'asc' }: { title: string; direction: 'asc' | 'desc' }) {
await this.grid.get().locator(`th[data-title="${title}"] .nc-ui-dt-dropdown`).click();
let menuOption;
if (direction === 'desc') {
menuOption = this.rootPage.locator('li[role="menuitem"]:has-text("Sort Descending"):visible').click();
} else {
menuOption = this.rootPage.locator('li[role="menuitem"]:has-text("Sort Ascending"):visible').click();
}
await this.waitForResponse({
uiAction: menuOption,
httpMethodsToMatch: ['POST'],
requestUrlPathToMatch: `/sorts`,
});
await this.grid.toolbar.parent.dashboard.waitForLoaderToDisappear();
await this.grid.toolbar.clickSort();
await this.rootPage.locator(`.ant-select-selection-item:has-text("${title}")`).first().isVisible();
await this.rootPage
.locator(
`.nc-sort-dir-select:has-text("${direction === 'asc' ? '1 → 9' : '9 → 1'}"),.nc-sort-dir-select:has-text("${
direction === 'asc' ? 'A → Z' : 'Z → A'
}")`
)
.first()
.isVisible();
}
}

2
tests/playwright/pages/Dashboard/common/Cell/index.ts

@ -193,6 +193,6 @@ export class CellPageObject extends BasePage {
) {
await this.get({ index, columnHeader }).click(...clickOptions);
await this.get({ index, columnHeader }).press('Control+C');
await this.get({ index, columnHeader }).press((await this.isMacOs()) ? 'Meta+C' : 'Control+C');
}
}

134
tests/playwright/tests/columnMenuOperations.spec.ts

@ -0,0 +1,134 @@
import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup';
const columns = [
{
title: 'SingleLineText',
type: 'SingleLineText',
},
{
title: 'LongText',
type: 'LongText',
},
// todo: Number column creation not works
// {
// title: 'Number',
// type: 'Number',
// },
{
title: 'Decimal',
type: 'Decimal',
},
{
title: 'Checkbox',
type: 'Checkbox',
},
{
title: 'Email',
type: 'Email',
},
{
title: 'PhoneNumber',
type: 'PhoneNumber',
},
{
title: 'Url',
type: 'URL',
},
];
test.describe('Column menu operations', () => {
let dashboard: DashboardPage;
let context: any;
test.beforeEach(async ({ page }) => {
context = await setup({ page });
dashboard = new DashboardPage(page, context.project);
});
test('Duplicate fields', async () => {
await dashboard.treeView.openTable({ title: 'Film' });
for (const { title, type } of columns) {
await dashboard.grid.column.create({
title,
type,
});
await dashboard.grid.column.duplicateColumn({
title,
});
await dashboard.grid.column.duplicateColumn({
title,
expectedTitle: `${title}_copy_1`,
});
}
await dashboard.closeTab({ title: 'Film' });
});
test('Insert after', async () => {
await dashboard.treeView.openTable({ title: 'Film' });
await dashboard.grid.column.create({
title: 'InsertAfterColumn',
type: 'SingleLineText',
insertAfterColumnTitle: 'Title',
});
await dashboard.grid.column.create({
title: 'InsertAfterColumn1',
type: 'SingleLineText',
insertAfterColumnTitle: 'Store List',
});
await dashboard.closeTab({ title: 'Film' });
});
test('Insert before', async () => {
await dashboard.treeView.openTable({ title: 'Film' });
await dashboard.grid.column.create({
title: 'InsertBeforeColumn',
type: 'SingleLineText',
insertBeforeColumnTitle: 'Title',
});
await dashboard.grid.column.create({
title: 'InsertBeforeColumn1',
type: 'SingleLineText',
insertBeforeColumnTitle: 'Store List',
});
await dashboard.closeTab({ title: 'Film' });
});
test('Hide column', async () => {
await dashboard.treeView.openTable({ title: 'Film' });
await dashboard.grid.column.hideColumn({
title: 'Title',
});
await dashboard.grid.column.hideColumn({
title: 'Store List',
});
await dashboard.closeTab({ title: 'Film' });
});
test('Sort column', async () => {
await dashboard.treeView.openTable({ title: 'Film' });
await dashboard.grid.column.sortColumn({
title: 'Title',
direction: 'asc',
});
await dashboard.grid.column.sortColumn({
title: 'ReleaseYear',
direction: 'desc',
});
await dashboard.closeTab({ title: 'Film' });
});
});
Loading…
Cancel
Save