div');
const count = await locator.count();
for (let i = 0; i < count; i++) {
+ await locator.nth(i).scrollIntoViewIfNeeded();
const text = await locator.nth(i).getByTestId('nc-field-title').textContent();
fieldsText.push(text);
}
- // verify field inserted above the target field
if (insertAboveColumnTitle) {
+ // verify field inserted above the target field
expect(fieldsText[fieldsText.findIndex(title => title.startsWith(insertAboveColumnTitle)) - 1]).toBe(title);
- }
-
- // verify field inserted below the target field
- if (insertBelowColumnTitle) {
- expect(fieldsText[fieldsText.findIndex(title => title.startsWith(insertAboveColumnTitle)) + 1]).toBe(title);
+ } else if (insertBelowColumnTitle) {
+ // verify field inserted below the target field
+ expect(fieldsText[fieldsText.findIndex(title => title.startsWith(insertBelowColumnTitle)) + 1]).toBe(title);
+ } else {
+ // verify field inserted at the end
+ expect(fieldsText[fieldsText.length - 1]).toBe(title);
}
}
@@ -222,18 +238,19 @@ export class FieldsPage extends BasePage {
async saveChanges() {
await this.waitForResponse({
uiAction: async () => await this.saveChangesButton.click(),
- requestUrlPathToMatch: 'api/v1/db/meta/tables/',
- httpMethodsToMatch: ['POST'],
- responseJsonMatcher: json => json['failedOps']?.length === 0,
+ requestUrlPathToMatch: 'api/v1/db/meta/views/',
+ httpMethodsToMatch: ['GET'],
+ responseJsonMatcher: json => json['list'],
});
+ await this.rootPage.waitForTimeout(200);
}
- async getField({ title }: { title: string }) {
- return this.fieldListWrapper.getByTestId('nc-field-title').locator(`text=${title}`);
+ getField({ title }: { title: string }) {
+ return this.fieldListWrapper.getByTestId(`nc-field-item-${title}`);
}
async getFieldVisibilityCheckbox({ title }: { title: string }) {
- return (await this.getField({ title })).getByTestId('nc-field-visibility-checkbox');
+ return this.getField({ title }).getByTestId('nc-field-visibility-checkbox');
}
async selectFieldAction({
@@ -245,10 +262,11 @@ export class FieldsPage extends BasePage {
action: 'copy-id' | 'duplicate' | 'insert-above' | 'insert-below' | 'delete';
isDisplayValueField?: boolean;
}) {
- const field = await this.getField({ title });
+ const field = this.getField({ title });
+ await field.scrollIntoViewIfNeeded();
await field.hover();
- await field.getByTestId('nc-field-item-action-button').waitFor({ state: 'visible' });
+ // await field.getByTestId('nc-field-item-action-button').waitFor({ state: 'visible' });
await field.getByTestId('nc-field-item-action-button').click();
const fieldActionDropdown = isDisplayValueField
diff --git a/tests/playwright/pages/Dashboard/Details/index.ts b/tests/playwright/pages/Dashboard/Details/index.ts
index 4b9cddde66..2ca6027d9c 100644
--- a/tests/playwright/pages/Dashboard/Details/index.ts
+++ b/tests/playwright/pages/Dashboard/Details/index.ts
@@ -4,12 +4,14 @@ import { TopbarPage } from '../common/Topbar';
import { Locator } from '@playwright/test';
import { WebhookPage } from './WebhookPage';
import { ErdPage } from './ErdPage';
+import { FieldsPage } from './FieldsPage';
export class DetailsPage extends BasePage {
readonly dashboard: DashboardPage;
readonly topbar: TopbarPage;
readonly webhook: WebhookPage;
readonly relations: ErdPage;
+ readonly fields: FieldsPage;
readonly tab_webhooks: Locator;
readonly tab_apiSnippet: Locator;
@@ -24,6 +26,7 @@ export class DetailsPage extends BasePage {
this.topbar = dashboard.grid.topbar;
this.webhook = new WebhookPage(this);
this.relations = new ErdPage(this);
+ this.fields = new FieldsPage(this);
this.tab_webhooks = this.get().locator(`[data-testid="nc-webhooks-tab"]`);
this.tab_apiSnippet = this.get().locator(`[data-testid="nc-apis-tab"]`);
diff --git a/tests/playwright/tests/db/features/multiFieldEditor.spec.ts b/tests/playwright/tests/db/features/multiFieldEditor.spec.ts
new file mode 100644
index 0000000000..c88c80b0a9
--- /dev/null
+++ b/tests/playwright/tests/db/features/multiFieldEditor.spec.ts
@@ -0,0 +1,49 @@
+import { test } from '@playwright/test';
+import { DashboardPage } from '../../../pages/Dashboard';
+import { GridPage } from '../../../pages/Dashboard/Grid';
+import setup, { unsetup } from '../../../setup';
+import { FieldsPage } from '../../../pages/Dashboard/Details/FieldsPage';
+
+test.describe('Multi Field Editor', () => {
+ let dashboard: DashboardPage, grid: GridPage, fields: FieldsPage;
+ let context: any;
+ const defaultFieldName = 'Multi Field Editor';
+
+ test.beforeEach(async ({ page }) => {
+ context = await setup({ page, isEmptyProject: true });
+ dashboard = new DashboardPage(page, context.base);
+ grid = dashboard.grid;
+ fields = dashboard.details.fields;
+
+ await dashboard.treeView.createTable({ title: 'Multifield', baseTitle: context.base.title });
+ await dashboard.grid.topbar.openDetailedTab();
+ await dashboard.details.clickFieldsTab();
+
+ // Add New Field
+ await fields.createOrUpdate({ title: defaultFieldName });
+ });
+
+ test.afterEach(async () => {
+ await unsetup(context);
+ });
+
+ const openMultiFieldOfATable = async (tableName: string) => {
+ await dashboard.treeView.openTable({ title: tableName });
+ await dashboard.grid.topbar.openDetailedTab();
+ await dashboard.details.clickRelationsTab();
+ };
+
+ test('Add New field and update', async () => {
+ // Add New Field
+ await fields.createOrUpdate({ title: 'Name' });
+
+ // Update Field title
+ await fields.getField({ title: 'Name' }).click();
+ await fields.createOrUpdate({ title: 'Updated Name', isUpdateMode: true });
+ });
+
+ test('Field action menu: CopyId, Duplicate, InsertAbove, InsertBelow', async () => {
+ await fields.createOrUpdate({ title: 'Above Inserted Field', insertAboveColumnTitle: defaultFieldName });
+ await fields.createOrUpdate({ title: 'Below Inserted Field', insertBelowColumnTitle: defaultFieldName });
+ });
+});