From bb3910efc44c39f8cb8321cf75ed55f259d43448 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Fri, 23 Jun 2023 10:23:01 +0530 Subject: [PATCH] test: bulk update (draft) Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- packages/nc-gui/components/dlg/BulkUpdate.vue | 3 +- .../pages/Dashboard/BulkUpdate/index.ts | 143 ++++++++++++++++++ .../playwright/pages/Dashboard/Grid/index.ts | 13 ++ tests/playwright/pages/Dashboard/index.ts | 3 + tests/playwright/tests/db/bulkUpdate.spec.ts | 76 ++++++++++ 5 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 tests/playwright/pages/Dashboard/BulkUpdate/index.ts create mode 100644 tests/playwright/tests/db/bulkUpdate.spec.ts diff --git a/packages/nc-gui/components/dlg/BulkUpdate.vue b/packages/nc-gui/components/dlg/BulkUpdate.vue index fb19da504f..25e9542267 100644 --- a/packages/nc-gui/components/dlg/BulkUpdate.vue +++ b/packages/nc-gui/components/dlg/BulkUpdate.vue @@ -187,7 +187,7 @@ onMounted(() => { class="nc-drawer-bulk-update" :class="{ active: isExpanded }" > -
+
@@ -251,6 +251,7 @@ onMounted(() => {
diff --git a/tests/playwright/pages/Dashboard/BulkUpdate/index.ts b/tests/playwright/pages/Dashboard/BulkUpdate/index.ts new file mode 100644 index 0000000000..261f9c84a6 --- /dev/null +++ b/tests/playwright/pages/Dashboard/BulkUpdate/index.ts @@ -0,0 +1,143 @@ +import { expect, Locator } from '@playwright/test'; +import BasePage from '../../Base'; +import { DashboardPage } from '..'; +import { DateTimeCellPageObject } from '../common/Cell/DateTimeCell'; +import { getTextExcludeIconText } from '../../../tests/utils/general'; + +export class BulkUpdatePage extends BasePage { + readonly dashboard: DashboardPage; + readonly bulkUpdateButton: Locator; + readonly formHeader: Locator; + readonly columnsDrawer: Locator; + readonly form: Locator; + + constructor(dashboard: DashboardPage) { + super(dashboard.rootPage); + this.dashboard = dashboard; + this.bulkUpdateButton = this.dashboard.get().locator('.nc-bulk-update-save-btn'); + this.formHeader = this.dashboard.get().locator('.nc-bulk-update-form-header'); + this.columnsDrawer = this.dashboard.get().locator('.nc-columns-drawer'); + this.form = this.dashboard.get().locator('div.form'); + } + + get() { + return this.dashboard.get().locator(`.nc-drawer-bulk-update`); + } + + async close() { + return this.dashboard.rootPage.keyboard.press('Escape'); + } + + async getInactiveColumn(index: number) { + const inactiveColumns = await this.columnsDrawer.locator('.ant-card'); + return inactiveColumns.nth(index); + } + + async getActiveColumn(index: number) { + const activeColumns = await this.form.locator('[data-testid="nc-form-fields"]'); + return activeColumns.nth(index); + } + + async getInactiveColumns() { + const inactiveColumns = await this.columnsDrawer.locator('.ant-card'); + const inactiveColumnsCount = await inactiveColumns.count(); + const inactiveColumnsTitles = []; + // get title for each inactive column + for (let i = 0; i < inactiveColumnsCount; i++) { + const title = await getTextExcludeIconText(inactiveColumns.nth(i).locator('.ant-card-body')); + inactiveColumnsTitles.push(title); + } + + return inactiveColumnsTitles; + } + + async getActiveColumns() { + const activeColumns = await this.form.locator('[data-testid="nc-form-fields"]'); + const activeColumnsCount = await activeColumns.count(); + const activeColumnsTitles = []; + // get title for each active column + for (let i = 0; i < activeColumnsCount; i++) { + const title = await getTextExcludeIconText(activeColumns.nth(i).locator('[data-testid="nc-form-input-label"]')); + activeColumnsTitles.push(title); + } + + return activeColumnsTitles; + } + + async removeField(index: number) { + const removeFieldButton = await this.form.locator('[data-testid="nc-form-fields"]'); + const removeFieldButtonCount = await removeFieldButton.count(); + await removeFieldButton.nth(index).locator('[data-testid="nc-form-fields-close-icon"]').click(); + const newRemoveFieldButtonCount = await removeFieldButton.count(); + expect(newRemoveFieldButtonCount).toBe(removeFieldButtonCount - 1); + } + + async addField(index: number) { + const addFieldButton = await this.columnsDrawer.locator('.ant-card'); + const addFieldButtonCount = await addFieldButton.count(); + await addFieldButton.nth(index).click(); + const newAddFieldButtonCount = await addFieldButton.count(); + expect(newAddFieldButtonCount).toBe(addFieldButtonCount - 1); + } + + ////////////////////////////////////////////////////////////////////////////// + + async fillField({ columnTitle, value, type = 'text' }: { columnTitle: string; value: string; type?: string }) { + const field = this.form.locator(`[data-testid="nc-form-input-${columnTitle}"]`); + await field.hover(); + switch (type) { + case 'text': + await field.locator('input').fill(value); + break; + case 'geodata': { + const [lat, long] = value.split(','); + await this.rootPage.locator(`[data-testid="nc-geo-data-set-location-button"]`).click(); + await this.rootPage.locator(`[data-testid="nc-geo-data-latitude"]`).fill(lat); + await this.rootPage.locator(`[data-testid="nc-geo-data-longitude"]`).fill(long); + await this.rootPage.locator(`[data-testid="nc-geo-data-save"]`).click(); + break; + } + case 'belongsTo': + await field.locator('.nc-action-icon').click(); + await this.dashboard.linkRecord.select(value); + break; + case 'hasMany': + case 'manyToMany': + await field.locator(`[data-testid="nc-child-list-button-link-to"]`).click(); + await this.dashboard.linkRecord.select(value); + break; + case 'dateTime': + await field.locator('.nc-cell').click(); + // eslint-disable-next-line no-case-declarations + const dateTimeObj = new DateTimeCellPageObject(this.dashboard.grid.cell); + await dateTimeObj.selectDate({ date: value.slice(0, 10) }); + await dateTimeObj.selectTime({ hour: +value.slice(11, 13), minute: +value.slice(14, 16) }); + await dateTimeObj.save(); + break; + } + } + + async save({ + awaitResponse = true, + }: { + awaitResponse?: boolean; + } = {}) { + await this.bulkUpdateButton.click(); + const confirmModal = await this.rootPage.locator('.ant-modal-confirm'); + + const saveRowAction = () => confirmModal.locator('.ant-btn-primary').click(); + if (!awaitResponse) { + await saveRowAction(); + } else { + await this.waitForResponse({ + uiAction: saveRowAction, + requestUrlPathToMatch: 'api/v1/db/data/noco/', + httpMethodsToMatch: ['GET'], + responseJsonMatcher: json => json['pageInfo'], + }); + } + + await this.get().waitFor({ state: 'hidden' }); + await this.rootPage.locator('[data-testid="grid-load-spinner"]').waitFor({ state: 'hidden' }); + } +} diff --git a/tests/playwright/pages/Dashboard/Grid/index.ts b/tests/playwright/pages/Dashboard/Grid/index.ts index 3614fcf2ee..e46ab247ae 100644 --- a/tests/playwright/pages/Dashboard/Grid/index.ts +++ b/tests/playwright/pages/Dashboard/Grid/index.ts @@ -220,6 +220,19 @@ export class GridPage extends BasePage { await this.deleteSelectedRows(); } + async updateSelectedRows() { + await this.get().locator('[data-testid="nc-check-all"]').nth(0).click({ + button: 'right', + }); + await this.rootPage.locator('text=Bulk Update').click(); + await this.dashboard.waitForLoaderToDisappear(); + } + + async updateAll() { + await this.selectAll(); + await this.updateSelectedRows(); + } + async verifyTotalRowCount({ count }: { count: number }) { // wait for 100 ms and try again : 5 times let i = 0; diff --git a/tests/playwright/pages/Dashboard/index.ts b/tests/playwright/pages/Dashboard/index.ts index 31ff8a9fa7..661d204904 100644 --- a/tests/playwright/pages/Dashboard/index.ts +++ b/tests/playwright/pages/Dashboard/index.ts @@ -3,6 +3,7 @@ import BasePage from '../Base'; import { GridPage } from './Grid'; import { FormPage } from './Form'; import { ExpandedFormPage } from './ExpandedForm'; +import { BulkUpdatePage } from './BulkUpdate'; import { ChildList } from './Grid/Column/LTAR/ChildList'; import { LinkRecord } from './Grid/Column/LTAR/LinkRecord'; import { TreeViewPage } from './TreeView'; @@ -29,6 +30,7 @@ export class DashboardPage extends BasePage { readonly kanban: KanbanPage; readonly map: MapPage; readonly expandedForm: ExpandedFormPage; + readonly bulkUpdateForm: BulkUpdatePage; readonly webhookForm: WebhookFormPage; readonly findRowByScanOverlay: FindRowByScanOverlay; readonly childList: ChildList; @@ -51,6 +53,7 @@ export class DashboardPage extends BasePage { this.kanban = new KanbanPage(this); this.map = new MapPage(this); this.expandedForm = new ExpandedFormPage(this); + this.bulkUpdateForm = new BulkUpdatePage(this); this.webhookForm = new WebhookFormPage(this); this.findRowByScanOverlay = new FindRowByScanOverlay(this); this.childList = new ChildList(this); diff --git a/tests/playwright/tests/db/bulkUpdate.spec.ts b/tests/playwright/tests/db/bulkUpdate.spec.ts new file mode 100644 index 0000000000..dcb6eab6dc --- /dev/null +++ b/tests/playwright/tests/db/bulkUpdate.spec.ts @@ -0,0 +1,76 @@ +import { expect, test } from '@playwright/test'; +import setup from '../../setup'; +import { DashboardPage } from '../../pages/Dashboard'; +import { Api } from 'nocodb-sdk'; +import { createDemoTable } from '../../setup/demoTable'; +import { BulkUpdatePage } from '../../pages/Dashboard/BulkUpdate'; + +test.describe('Bulk update', () => { + let dashboard: DashboardPage; + let bulkUpdateForm: BulkUpdatePage; + let context: any; + let api: Api; + let records: any[]; + + test.beforeEach(async ({ page }) => { + context = await setup({ page, isEmptyProject: true }); + dashboard = new DashboardPage(page, context.project); + bulkUpdateForm = dashboard.bulkUpdateForm; + + api = new Api({ + baseURL: `http://localhost:8080/`, + headers: { + 'xc-auth': context.token, + }, + }); + + const table = await createDemoTable({ context, type: 'textBased', recordCnt: 50 }); + records = await api.dbTableRow.list('noco', context.project.id, table.id, { limit: 50 }); + await page.reload(); + + await dashboard.closeTab({ title: 'Team & Auth' }); + await dashboard.treeView.openTable({ title: 'textBased' }); + + // Open bulk update form + await dashboard.grid.updateAll(); + }); + + test('General- Click to add & remove', async () => { + let inactiveColumns = await bulkUpdateForm.getInactiveColumns(); + expect(inactiveColumns).toEqual(['SingleLineText', 'MultiLineText', 'Email', 'PhoneNumber', 'URL']); + + let activeColumns = await bulkUpdateForm.getActiveColumns(); + expect(activeColumns).toEqual([]); + + await bulkUpdateForm.addField(0); + await bulkUpdateForm.addField(0); + + inactiveColumns = await bulkUpdateForm.getInactiveColumns(); + expect(inactiveColumns).toEqual(['Email', 'PhoneNumber', 'URL']); + + activeColumns = await bulkUpdateForm.getActiveColumns(); + expect(activeColumns).toEqual(['SingleLineText', 'MultiLineText']); + }); + + test('General- Drag drop', async () => { + const src = await bulkUpdateForm.getInactiveColumn(0); + const dst = await bulkUpdateForm.form; + + await src.dragTo(dst); + expect(await bulkUpdateForm.getActiveColumns()).toEqual(['SingleLineText']); + expect(await bulkUpdateForm.getInactiveColumns()).toEqual(['MultiLineText', 'Email', 'PhoneNumber', 'URL']); + + const src2 = await bulkUpdateForm.getActiveColumn(0); + const dst2 = await bulkUpdateForm.columnsDrawer; + + await src2.dragTo(dst2); + expect(await bulkUpdateForm.getActiveColumns()).toEqual([]); + expect(await bulkUpdateForm.getInactiveColumns()).toEqual([ + 'SingleLineText', + 'MultiLineText', + 'Email', + 'PhoneNumber', + 'URL', + ]); + }); +});