mirror of https://github.com/nocodb/nocodb
Raju Udava
11 months ago
committed by
GitHub
5 changed files with 835 additions and 46 deletions
@ -0,0 +1,318 @@ |
|||||||
|
// Fields
|
||||||
|
|
||||||
|
import BasePage from '../../Base'; |
||||||
|
import { expect, Locator } from '@playwright/test'; |
||||||
|
import { DetailsPage } from './index'; |
||||||
|
import { UITypes } from 'nocodb-sdk'; |
||||||
|
|
||||||
|
export class FieldsPage extends BasePage { |
||||||
|
readonly detailsPage: DetailsPage; |
||||||
|
|
||||||
|
readonly searchFieldInput: Locator; |
||||||
|
readonly addNewFieldButton: Locator; |
||||||
|
readonly resetFieldChangesButton: Locator; |
||||||
|
readonly saveChangesButton: Locator; |
||||||
|
|
||||||
|
readonly addOrEditColumn: Locator; |
||||||
|
readonly fieldListWrapper: Locator; |
||||||
|
|
||||||
|
constructor(details: DetailsPage) { |
||||||
|
super(details.rootPage); |
||||||
|
this.detailsPage = details; |
||||||
|
this.searchFieldInput = this.get().getByTestId('nc-field-search-input'); |
||||||
|
this.addNewFieldButton = this.get().getByTestId('nc-field-add-new'); |
||||||
|
this.resetFieldChangesButton = this.get().getByTestId('nc-field-reset'); |
||||||
|
this.saveChangesButton = this.get().getByTestId('nc-field-save-changes'); |
||||||
|
this.addOrEditColumn = this.get().getByTestId('add-or-edit-column'); |
||||||
|
this.fieldListWrapper = this.get().getByTestId('nc-field-list-wrapper'); |
||||||
|
} |
||||||
|
|
||||||
|
get() { |
||||||
|
return this.detailsPage.get().locator('.nc-fields-wrapper'); |
||||||
|
} |
||||||
|
|
||||||
|
async fillSearch({ title }: { title: string }) { |
||||||
|
const searchInput = this.get().getByTestId('nc-field-search-input'); |
||||||
|
await searchInput.click(); |
||||||
|
await searchInput.fill(title); |
||||||
|
} |
||||||
|
|
||||||
|
async clearSearch() { |
||||||
|
await this.get().getByTestId('nc-field-clear-search').click(); |
||||||
|
} |
||||||
|
|
||||||
|
async clickNewField() { |
||||||
|
await this.addNewFieldButton.click(); |
||||||
|
} |
||||||
|
|
||||||
|
async clickRestoreField({ title }: { title: string }) { |
||||||
|
await this.getField({ title }).getByTestId('nc-field-restore-changes').click(); |
||||||
|
} |
||||||
|
|
||||||
|
async createOrUpdate({ |
||||||
|
title, |
||||||
|
type = UITypes.SingleLineText, |
||||||
|
isUpdateMode = false, |
||||||
|
saveChanges = true, |
||||||
|
formula = '', |
||||||
|
qrCodeValueColumnTitle = '', |
||||||
|
barcodeValueColumnTitle = '', |
||||||
|
barcodeFormat = '', |
||||||
|
childTable = '', |
||||||
|
childColumn = '', |
||||||
|
relationType = '', |
||||||
|
rollupType = '', |
||||||
|
format = '', |
||||||
|
dateFormat = 'YYYY-MM-DD', |
||||||
|
timeFormat = 'HH:mm', |
||||||
|
insertAboveColumnTitle, |
||||||
|
insertBelowColumnTitle, |
||||||
|
}: { |
||||||
|
title: string; |
||||||
|
type?: UITypes; |
||||||
|
isUpdateMode?: boolean; |
||||||
|
saveChanges?: boolean; |
||||||
|
formula?: string; |
||||||
|
qrCodeValueColumnTitle?: string; |
||||||
|
barcodeValueColumnTitle?: string; |
||||||
|
barcodeFormat?: string; |
||||||
|
childTable?: string; |
||||||
|
childColumn?: string; |
||||||
|
relationType?: string; |
||||||
|
rollupType?: string; |
||||||
|
format?: string; |
||||||
|
dateFormat?: string; |
||||||
|
timeFormat?: string; |
||||||
|
insertAboveColumnTitle?: string; |
||||||
|
insertBelowColumnTitle?: string; |
||||||
|
}) { |
||||||
|
if (!isUpdateMode) { |
||||||
|
if (insertAboveColumnTitle) { |
||||||
|
await this.selectFieldAction({ title: insertAboveColumnTitle, action: 'insert-above' }); |
||||||
|
} else if (insertBelowColumnTitle) { |
||||||
|
await this.selectFieldAction({ title: insertBelowColumnTitle, action: 'insert-below' }); |
||||||
|
} else { |
||||||
|
await this.clickNewField(); |
||||||
|
} |
||||||
|
} |
||||||
|
await this.addOrEditColumn.waitFor({ state: 'visible' }); |
||||||
|
|
||||||
|
await this.fillTitle({ title }); |
||||||
|
|
||||||
|
await this.selectType({ type }); |
||||||
|
await this.rootPage.waitForTimeout(500); |
||||||
|
|
||||||
|
switch (type) { |
||||||
|
case 'SingleSelect': |
||||||
|
case 'MultiSelect': |
||||||
|
break; |
||||||
|
case 'Duration': |
||||||
|
if (format) { |
||||||
|
await this.addOrEditColumn.locator('.ant-select-single').nth(1).click(); |
||||||
|
await this.rootPage |
||||||
|
.locator(`.ant-select-item`, { |
||||||
|
hasText: format, |
||||||
|
}) |
||||||
|
.click(); |
||||||
|
} |
||||||
|
break; |
||||||
|
case 'Date': |
||||||
|
await this.addOrEditColumn.locator('.nc-date-select').click(); |
||||||
|
await this.rootPage.locator('.nc-date-select').pressSequentially(dateFormat); |
||||||
|
await this.rootPage.locator('.ant-select-item').locator(`text="${dateFormat}"`).click(); |
||||||
|
break; |
||||||
|
case 'DateTime': |
||||||
|
// Date Format
|
||||||
|
await this.addOrEditColumn.locator('.nc-date-select').click(); |
||||||
|
await this.rootPage.locator('.ant-select-item').locator(`text="${dateFormat}"`).click(); |
||||||
|
// Time Format
|
||||||
|
await this.addOrEditColumn.locator('.nc-time-select').click(); |
||||||
|
await this.rootPage.locator('.ant-select-item').locator(`text="${timeFormat}"`).click(); |
||||||
|
break; |
||||||
|
case 'Formula': |
||||||
|
await this.addOrEditColumn.locator('.nc-formula-input').fill(formula); |
||||||
|
break; |
||||||
|
case 'QrCode': |
||||||
|
await this.addOrEditColumn.locator('.ant-select-single').nth(1).click(); |
||||||
|
await this.rootPage |
||||||
|
.locator(`.ant-select-item`) |
||||||
|
.locator(`[data-testid="nc-qr-${qrCodeValueColumnTitle}"]`) |
||||||
|
.click(); |
||||||
|
break; |
||||||
|
case 'Barcode': |
||||||
|
await this.addOrEditColumn.locator('.ant-select-single').nth(1).click(); |
||||||
|
await this.rootPage |
||||||
|
.locator(`.ant-select-item`, { |
||||||
|
hasText: new RegExp(`^${barcodeValueColumnTitle}$`), |
||||||
|
}) |
||||||
|
.click(); |
||||||
|
break; |
||||||
|
case 'Lookup': |
||||||
|
await this.addOrEditColumn.locator('.ant-select-single').nth(1).click(); |
||||||
|
await this.rootPage |
||||||
|
.locator(`.ant-select-item`, { |
||||||
|
hasText: childTable, |
||||||
|
}) |
||||||
|
.click(); |
||||||
|
await this.addOrEditColumn.locator('.ant-select-single').nth(2).click(); |
||||||
|
await this.rootPage |
||||||
|
.locator(`.ant-select-item`, { |
||||||
|
hasText: childColumn, |
||||||
|
}) |
||||||
|
.last() |
||||||
|
.click(); |
||||||
|
break; |
||||||
|
case 'Rollup': |
||||||
|
await this.addOrEditColumn.locator('.ant-select-single').nth(1).click(); |
||||||
|
await this.rootPage |
||||||
|
.locator(`.ant-select-item`, { |
||||||
|
hasText: childTable, |
||||||
|
}) |
||||||
|
.click(); |
||||||
|
await this.addOrEditColumn.locator('.ant-select-single').nth(2).click(); |
||||||
|
await this.rootPage |
||||||
|
.locator(`.nc-dropdown-relation-column >> .ant-select-item`, { |
||||||
|
hasText: childColumn, |
||||||
|
}) |
||||||
|
.click(); |
||||||
|
await this.addOrEditColumn.locator('.ant-select-single').nth(3).click(); |
||||||
|
await this.rootPage |
||||||
|
.locator(`.nc-dropdown-rollup-function >> .ant-select-item`, { |
||||||
|
hasText: rollupType, |
||||||
|
}) |
||||||
|
.nth(0) |
||||||
|
.click(); |
||||||
|
break; |
||||||
|
case 'Links': |
||||||
|
await this.addOrEditColumn |
||||||
|
.locator('.nc-ltar-relation-type >> .ant-radio') |
||||||
|
.nth(relationType === 'Has Many' ? 0 : 1) |
||||||
|
.click(); |
||||||
|
await this.addOrEditColumn.locator('.ant-select-single').nth(1).click(); |
||||||
|
await this.rootPage.locator(`.nc-ltar-child-table >> input[type="search"]`).fill(childTable); |
||||||
|
await this.rootPage |
||||||
|
.locator(`.nc-dropdown-ltar-child-table >> .ant-select-item`, { |
||||||
|
hasText: childTable, |
||||||
|
}) |
||||||
|
.nth(0) |
||||||
|
.click(); |
||||||
|
break; |
||||||
|
case 'User': |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (saveChanges) { |
||||||
|
await this.saveChanges(); |
||||||
|
|
||||||
|
const fieldsText = await this.getAllFieldText(); |
||||||
|
|
||||||
|
if (insertAboveColumnTitle) { |
||||||
|
// verify field inserted above the target field
|
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
async fillTitle({ title }: { title: string }) { |
||||||
|
const fieldTitleInput = this.addOrEditColumn.locator('.nc-fields-input'); |
||||||
|
await fieldTitleInput.click(); |
||||||
|
await fieldTitleInput.fill(title); |
||||||
|
} |
||||||
|
|
||||||
|
async selectType({ type }: { type: string }) { |
||||||
|
await this.addOrEditColumn.locator('.ant-select-selector > .ant-select-selection-item').click(); |
||||||
|
|
||||||
|
await this.addOrEditColumn.locator('.ant-select-selection-search-input[aria-expanded="true"]').waitFor(); |
||||||
|
await this.addOrEditColumn.locator('.ant-select-selection-search-input[aria-expanded="true"]').fill(type); |
||||||
|
|
||||||
|
// Select column type
|
||||||
|
await this.rootPage.locator('.rc-virtual-list-holder-inner > div').locator(`text="${type}"`).click(); |
||||||
|
} |
||||||
|
|
||||||
|
async saveChanges() { |
||||||
|
await this.waitForResponse({ |
||||||
|
uiAction: async () => await this.saveChangesButton.click(), |
||||||
|
requestUrlPathToMatch: 'api/v1/db/meta/views/', |
||||||
|
httpMethodsToMatch: ['GET'], |
||||||
|
responseJsonMatcher: json => json['list'], |
||||||
|
}); |
||||||
|
await this.rootPage.waitForTimeout(200); |
||||||
|
} |
||||||
|
|
||||||
|
getField({ title }: { title: string }) { |
||||||
|
return this.fieldListWrapper.getByTestId(`nc-field-item-${title}`); |
||||||
|
} |
||||||
|
|
||||||
|
async getFieldVisibilityCheckbox({ title }: { title: string }) { |
||||||
|
return this.getField({ title }).getByTestId('nc-field-visibility-checkbox'); |
||||||
|
} |
||||||
|
|
||||||
|
async selectFieldAction({ |
||||||
|
title, |
||||||
|
action, |
||||||
|
isDisplayValueField = false, |
||||||
|
}: { |
||||||
|
title: string; |
||||||
|
action: 'copy-id' | 'duplicate' | 'insert-above' | 'insert-below' | 'delete'; |
||||||
|
isDisplayValueField?: boolean; |
||||||
|
}) { |
||||||
|
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').click(); |
||||||
|
|
||||||
|
const fieldActionDropdown = isDisplayValueField |
||||||
|
? this.rootPage.locator('.nc-field-item-action-dropdown-display-column') |
||||||
|
: this.rootPage.locator('.nc-field-item-action-dropdown'); |
||||||
|
|
||||||
|
await fieldActionDropdown.waitFor({ state: 'visible' }); |
||||||
|
await fieldActionDropdown.getByTestId(`nc-field-item-action-${action}`).click(); |
||||||
|
|
||||||
|
if (action === 'copy-id') { |
||||||
|
await field.getByTestId('nc-field-item-action-button').click(); |
||||||
|
} |
||||||
|
|
||||||
|
await fieldActionDropdown.waitFor({ state: 'hidden' }); |
||||||
|
} |
||||||
|
|
||||||
|
async getAllFieldText() { |
||||||
|
const fieldsText = []; |
||||||
|
const locator = this.fieldListWrapper.locator('>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); |
||||||
|
} |
||||||
|
return fieldsText; |
||||||
|
} |
||||||
|
|
||||||
|
async getFieldId({ title, isDisplayValueField = false }: { title: string; isDisplayValueField?: boolean }) { |
||||||
|
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').click(); |
||||||
|
|
||||||
|
const fieldActionDropdown = isDisplayValueField |
||||||
|
? this.rootPage.locator('.nc-field-item-action-dropdown-display-column') |
||||||
|
: this.rootPage.locator('.nc-field-item-action-dropdown'); |
||||||
|
|
||||||
|
await fieldActionDropdown.waitFor({ state: 'visible' }); |
||||||
|
const fieldId = await fieldActionDropdown.getByTestId('nc-field-item-id').textContent(); |
||||||
|
await field.getByTestId('nc-field-item-action-button').click(); |
||||||
|
await fieldActionDropdown.waitFor({ state: 'hidden' }); |
||||||
|
return fieldId; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,380 @@ |
|||||||
|
import { expect, test } from '@playwright/test'; |
||||||
|
import { DashboardPage } from '../../../pages/Dashboard'; |
||||||
|
import setup, { unsetup } from '../../../setup'; |
||||||
|
import { FieldsPage } from '../../../pages/Dashboard/Details/FieldsPage'; |
||||||
|
import { getTextExcludeIconText } from '../../utils/general'; |
||||||
|
import { UITypes } from 'nocodb-sdk'; |
||||||
|
|
||||||
|
const allFieldList = [ |
||||||
|
{ |
||||||
|
title: 'Single Line Text', |
||||||
|
type: UITypes.SingleLineText, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Long Text', |
||||||
|
type: UITypes.LongText, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Number', |
||||||
|
type: UITypes.Number, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Decimal', |
||||||
|
type: UITypes.Decimal, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Attachment', |
||||||
|
type: UITypes.Attachment, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Checkbox', |
||||||
|
type: UITypes.Checkbox, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'MultiSelect', |
||||||
|
type: UITypes.MultiSelect, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'SingleSelect', |
||||||
|
type: UITypes.SingleSelect, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Date', |
||||||
|
type: UITypes.Date, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'DateTime', |
||||||
|
type: UITypes.DateTime, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Year', |
||||||
|
type: UITypes.Year, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Time', |
||||||
|
type: UITypes.Time, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'PhoneNumber', |
||||||
|
type: UITypes.PhoneNumber, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Email', |
||||||
|
type: UITypes.Email, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'URL', |
||||||
|
type: UITypes.URL, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Currency', |
||||||
|
type: UITypes.Currency, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Percent', |
||||||
|
type: UITypes.Percent, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Duration', |
||||||
|
type: UITypes.Duration, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Rating', |
||||||
|
type: UITypes.Rating, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Formula', |
||||||
|
type: UITypes.Formula, |
||||||
|
formula: 'LEN({Title})', |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'QrCode', |
||||||
|
type: UITypes.QrCode, |
||||||
|
qrCodeValueColumnTitle: 'Title', |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Barcode', |
||||||
|
type: UITypes.Barcode, |
||||||
|
barcodeValueColumnTitle: 'Title', |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Geometry', |
||||||
|
type: UITypes.Geometry, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'JSON', |
||||||
|
type: UITypes.JSON, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'User', |
||||||
|
type: UITypes.User, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Links', |
||||||
|
type: UITypes.Links, |
||||||
|
relationType: 'Has Many', |
||||||
|
childTable: 'Multifield', |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
test.describe('Multi Field Editor', () => { |
||||||
|
let dashboard: DashboardPage, 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); |
||||||
|
fields = dashboard.details.fields; |
||||||
|
|
||||||
|
await dashboard.treeView.createTable({ title: 'Multifield', baseTitle: context.base.title }); |
||||||
|
await openMultiFieldOfATable(); |
||||||
|
}); |
||||||
|
|
||||||
|
test.afterEach(async () => { |
||||||
|
await unsetup(context); |
||||||
|
}); |
||||||
|
|
||||||
|
async function openMultiFieldOfATable() { |
||||||
|
await dashboard.grid.topbar.openDetailedTab(); |
||||||
|
await dashboard.details.clickFieldsTab(); |
||||||
|
} |
||||||
|
|
||||||
|
async function toggleShowSystemFieldsFromDataTab() { |
||||||
|
await dashboard.grid.topbar.openDataTab(); |
||||||
|
await dashboard.grid.toolbar.fields.toggleShowSystemFields(); |
||||||
|
await openMultiFieldOfATable(); |
||||||
|
} |
||||||
|
|
||||||
|
const verifyGridColumnHeaders = async ({ fields = [] }: { fields: string[] }) => { |
||||||
|
await dashboard.grid.topbar.openDataTab(); |
||||||
|
|
||||||
|
const locator = dashboard.grid.get().locator(`th`); |
||||||
|
const count = await locator.count(); |
||||||
|
|
||||||
|
// exclude first checkbox and last add new column
|
||||||
|
expect(count - 2).toBe(fields.length); |
||||||
|
|
||||||
|
for (let i = 1; i < count - 1; i++) { |
||||||
|
const header = locator.nth(i); |
||||||
|
const text = await getTextExcludeIconText(header); |
||||||
|
expect(text).toBe(fields[i - 1]); |
||||||
|
} |
||||||
|
|
||||||
|
await openMultiFieldOfATable(); |
||||||
|
}; |
||||||
|
|
||||||
|
const searchAndVerifyFields = async ({ searchQuery, fieldList }: { searchQuery: string; fieldList: string[] }) => { |
||||||
|
await fields.searchFieldInput.fill(searchQuery); |
||||||
|
|
||||||
|
const allFields = await fields.getAllFieldText(); |
||||||
|
expect(allFields).toEqual( |
||||||
|
searchQuery ? fieldList.filter(field => field.toLowerCase().includes(searchQuery.toLowerCase())) : fieldList |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
test('Verify system fields are not listed, Add New field, update & Restore, reset', async () => { |
||||||
|
//Verify system fields are not listed
|
||||||
|
await toggleShowSystemFieldsFromDataTab(); |
||||||
|
let fieldsText = await fields.getAllFieldText(); |
||||||
|
expect(fieldsText.length).toBe(1); |
||||||
|
await toggleShowSystemFieldsFromDataTab(); |
||||||
|
|
||||||
|
// Add New Field
|
||||||
|
await fields.createOrUpdate({ title: 'Name', saveChanges: false }); |
||||||
|
await expect(fields.getField({ title: 'Name' })).toContainText('New field'); |
||||||
|
await fields.saveChanges(); |
||||||
|
|
||||||
|
// Update Field title
|
||||||
|
await fields.getField({ title: 'Name' }).click(); |
||||||
|
await fields.createOrUpdate({ title: 'Updated Name', saveChanges: false, isUpdateMode: true }); |
||||||
|
await expect(fields.getField({ title: 'Updated Name' })).toContainText('Updated field'); |
||||||
|
await fields.saveChanges(); |
||||||
|
|
||||||
|
// Update and restore field changes
|
||||||
|
await fields.getField({ title: 'Updated Name' }).click(); |
||||||
|
await fields.createOrUpdate({ title: 'Updated Name to restore', saveChanges: false, isUpdateMode: true }); |
||||||
|
await fields.clickRestoreField({ title: 'Updated Name to restore' }); |
||||||
|
|
||||||
|
// verify grid column header
|
||||||
|
fieldsText = await fields.getAllFieldText(); |
||||||
|
expect(fieldsText).toEqual(['Title', 'Updated Name']); |
||||||
|
await verifyGridColumnHeaders({ fields: fieldsText }); |
||||||
|
|
||||||
|
// add new fields then reset changes and verify
|
||||||
|
await fields.createOrUpdate({ title: 'field to reset', saveChanges: false }); |
||||||
|
await fields.createOrUpdate({ title: 'Random', saveChanges: false }); |
||||||
|
await fields.resetFieldChangesButton.click(); |
||||||
|
|
||||||
|
// verify with old fields
|
||||||
|
await verifyGridColumnHeaders({ fields: fieldsText }); |
||||||
|
}); |
||||||
|
|
||||||
|
// Todo: remove `skip`, if `optimized dependencies changed. reloading` issue is fixed
|
||||||
|
test.skip('Add all fields and check status on clicking each field', async () => { |
||||||
|
// Add all fields, verify status and save
|
||||||
|
for (const field of allFieldList) { |
||||||
|
await fields.createOrUpdate({ ...field, saveChanges: false }); |
||||||
|
await expect(fields.getField({ title: field.title })).toContainText('New field'); |
||||||
|
await fields.saveChanges(); |
||||||
|
} |
||||||
|
let fieldsText = await fields.getAllFieldText(); |
||||||
|
|
||||||
|
// verify all newly added field and its order
|
||||||
|
expect(fieldsText).toEqual(['Title', ...allFieldList.map(field => field.title)]); |
||||||
|
|
||||||
|
// click on each field and check status
|
||||||
|
fieldsText = await fields.getAllFieldText(); |
||||||
|
for (const title of fieldsText) { |
||||||
|
await fields.getField({ title }).click(); |
||||||
|
await expect(fields.getField({ title })).not.toContainText(['New field', 'Updated field']); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
test('Field operations: CopyId, Duplicate, InsertAbove, InsertBelow, Delete, Hide', async () => { |
||||||
|
// Add New Field
|
||||||
|
await fields.createOrUpdate({ title: defaultFieldName }); |
||||||
|
|
||||||
|
// copy-id and verify
|
||||||
|
const fieldId = await fields.getFieldId({ title: defaultFieldName }); |
||||||
|
await fields.selectFieldAction({ title: defaultFieldName, action: 'copy-id' }); |
||||||
|
expect(fieldId).toBe(await dashboard.getClipboardText()); |
||||||
|
|
||||||
|
// duplicate and verify
|
||||||
|
await fields.selectFieldAction({ title: defaultFieldName, action: 'duplicate' }); |
||||||
|
await fields.saveChanges(); |
||||||
|
|
||||||
|
let fieldsText = await fields.getAllFieldText(); |
||||||
|
expect(fieldsText[fieldsText.findIndex(field => field === defaultFieldName) + 1]).toBe(`${defaultFieldName}_copy`); |
||||||
|
|
||||||
|
// insert and verify
|
||||||
|
await fields.createOrUpdate({ title: 'Above Inserted Field', insertAboveColumnTitle: defaultFieldName }); |
||||||
|
await fields.createOrUpdate({ title: 'Below Inserted Field', insertBelowColumnTitle: defaultFieldName }); |
||||||
|
|
||||||
|
// delete and verify
|
||||||
|
await fields.selectFieldAction({ title: `${defaultFieldName}_copy`, action: 'delete' }); |
||||||
|
await expect(fields.getField({ title: `${defaultFieldName}_copy` })).toContainText('Deleted field'); |
||||||
|
|
||||||
|
await fields.saveChanges(); |
||||||
|
|
||||||
|
fieldsText = await fields.getAllFieldText(); |
||||||
|
expect(!fieldsText.includes(`${defaultFieldName}_copy`)).toBeTruthy(); |
||||||
|
|
||||||
|
// verify grid column header
|
||||||
|
await verifyGridColumnHeaders({ fields: fieldsText }); |
||||||
|
|
||||||
|
// Hide field and verify grid column header
|
||||||
|
await (await fields.getFieldVisibilityCheckbox({ title: defaultFieldName })).click(); |
||||||
|
await fields.saveChanges(); |
||||||
|
|
||||||
|
await verifyGridColumnHeaders({ fields: fieldsText.filter(field => field !== defaultFieldName) }); |
||||||
|
}); |
||||||
|
|
||||||
|
test('Search field and verify', async () => { |
||||||
|
const fieldList = ['Single Line Text', 'Long text', 'Rich text', 'Number', 'Percentage']; |
||||||
|
|
||||||
|
for (const field of fieldList) { |
||||||
|
await fields.createOrUpdate({ title: field, saveChanges: false }); |
||||||
|
} |
||||||
|
await fields.saveChanges(); |
||||||
|
|
||||||
|
let searchQuery = 'text'; |
||||||
|
await searchAndVerifyFields({ |
||||||
|
searchQuery, |
||||||
|
fieldList, |
||||||
|
}); |
||||||
|
|
||||||
|
searchQuery = 'Rich text'; |
||||||
|
await searchAndVerifyFields({ |
||||||
|
searchQuery, |
||||||
|
fieldList: ['Title', ...fieldList], |
||||||
|
}); |
||||||
|
|
||||||
|
// clear search and verify
|
||||||
|
await fields.clearSearch(); |
||||||
|
await searchAndVerifyFields({ |
||||||
|
searchQuery: '', |
||||||
|
fieldList: ['Title', ...fieldList], |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
test('Field Reorder and verify', async () => { |
||||||
|
// default order: ['Title', 'Single Line Text', 'Long Text', 'Number', 'Percent','Links']
|
||||||
|
const fieldList = [ |
||||||
|
{ |
||||||
|
title: 'Single Line Text', |
||||||
|
type: UITypes.SingleLineText, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Long Text', |
||||||
|
type: UITypes.LongText, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Number', |
||||||
|
type: UITypes.Number, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Percent', |
||||||
|
type: UITypes.Percent, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Links', |
||||||
|
type: UITypes.Links, |
||||||
|
relationType: 'Has Many', |
||||||
|
childTable: 'Multifield', |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
for (const field of fieldList) { |
||||||
|
await fields.createOrUpdate({ ...field, saveChanges: false }); |
||||||
|
} |
||||||
|
await fields.saveChanges(); |
||||||
|
// updated order : ['Title', 'Long Text','Single Line Text', 'Number', 'Percent','Links']
|
||||||
|
await fields.getField({ title: fieldList[0].title }).dragTo(fields.getField({ title: fieldList[1].title })); |
||||||
|
await expect(fields.getField({ title: fieldList[0].title })).toContainText('Updated field'); |
||||||
|
|
||||||
|
// updated order : ['Title', 'Long Text','Single Line Text', 'Number','Links', 'Percent']
|
||||||
|
await fields.getField({ title: fieldList[4].title }).dragTo(fields.getField({ title: fieldList[3].title })); |
||||||
|
await expect(fields.getField({ title: fieldList[4].title })).toContainText('Updated field'); |
||||||
|
|
||||||
|
await fields.saveChanges(); |
||||||
|
const fieldsText = await fields.getAllFieldText(); |
||||||
|
const expectedFieldText = ['Title', 'Long Text', 'Single Line Text', 'Number', 'Links', 'Percent']; |
||||||
|
|
||||||
|
expect(fieldsText).toEqual(expectedFieldText); |
||||||
|
|
||||||
|
await verifyGridColumnHeaders({ fields: expectedFieldText }); |
||||||
|
}); |
||||||
|
|
||||||
|
test('Keyboard shortcuts: add new field, save and delete', async () => { |
||||||
|
// add new field
|
||||||
|
await dashboard.rootPage.keyboard.press('Alt+C'); |
||||||
|
|
||||||
|
// verify field is added and has `New field` status
|
||||||
|
let fieldsText = await fields.getAllFieldText(); |
||||||
|
await expect(fields.getField({ title: fieldsText[fieldsText.length - 1] })).toContainText('New field'); |
||||||
|
|
||||||
|
// update title
|
||||||
|
await fields.createOrUpdate({ title: defaultFieldName, isUpdateMode: true }); |
||||||
|
|
||||||
|
// save the changes
|
||||||
|
await dashboard.rootPage.keyboard.press((await dashboard.isMacOs()) ? 'Meta+S' : 'Control+S'); |
||||||
|
await dashboard.rootPage.waitForTimeout(500); |
||||||
|
|
||||||
|
// verify result
|
||||||
|
fieldsText = await fields.getAllFieldText(); |
||||||
|
expect(fieldsText).toEqual(['Title', defaultFieldName]); |
||||||
|
|
||||||
|
// delete field
|
||||||
|
await fields.getField({ title: defaultFieldName }).click(); |
||||||
|
await dashboard.rootPage.keyboard.press((await dashboard.isMacOs()) ? 'Meta+Delete' : 'Delete'); |
||||||
|
await expect(fields.getField({ title: defaultFieldName })).toContainText('Deleted field'); |
||||||
|
|
||||||
|
// save the changes
|
||||||
|
await dashboard.rootPage.keyboard.press((await dashboard.isMacOs()) ? 'Meta+S' : 'Control+S'); |
||||||
|
await dashboard.rootPage.waitForTimeout(500); |
||||||
|
|
||||||
|
fieldsText = await fields.getAllFieldText(); |
||||||
|
expect(fieldsText).toEqual(['Title']); |
||||||
|
}); |
||||||
|
}); |
Loading…
Reference in new issue