diff --git a/packages/nc-gui/components/cell/attachment/Modal.vue b/packages/nc-gui/components/cell/attachment/Modal.vue index e25ac28674..ae89941f80 100644 --- a/packages/nc-gui/components/cell/attachment/Modal.vue +++ b/packages/nc-gui/components/cell/attachment/Modal.vue @@ -89,6 +89,7 @@ function onRemoveFileClick(title: any, i: number) {
diff --git a/packages/nc-gui/components/smartsheet/column/AttachmentOptions.vue b/packages/nc-gui/components/smartsheet/column/AttachmentOptions.vue index 55991daee2..faf8c43906 100644 --- a/packages/nc-gui/components/smartsheet/column/AttachmentOptions.vue +++ b/packages/nc-gui/components/smartsheet/column/AttachmentOptions.vue @@ -73,13 +73,13 @@ watch(searchValue, (value) => { - + - + diff --git a/tests/playwright/fixtures/sampleFiles/Image/1.jpeg b/tests/playwright/fixtures/sampleFiles/Image/1.jpeg new file mode 100644 index 0000000000..38f6296de1 Binary files /dev/null and b/tests/playwright/fixtures/sampleFiles/Image/1.jpeg differ diff --git a/tests/playwright/fixtures/sampleFiles/Image/2.png b/tests/playwright/fixtures/sampleFiles/Image/2.png new file mode 100644 index 0000000000..8a8062b9ad Binary files /dev/null and b/tests/playwright/fixtures/sampleFiles/Image/2.png differ diff --git a/tests/playwright/fixtures/sampleFiles/Image/3.jpeg b/tests/playwright/fixtures/sampleFiles/Image/3.jpeg new file mode 100644 index 0000000000..f3a232b77e Binary files /dev/null and b/tests/playwright/fixtures/sampleFiles/Image/3.jpeg differ diff --git a/tests/playwright/fixtures/sampleFiles/Image/4.jpeg b/tests/playwright/fixtures/sampleFiles/Image/4.jpeg new file mode 100644 index 0000000000..72fe2b94d9 Binary files /dev/null and b/tests/playwright/fixtures/sampleFiles/Image/4.jpeg differ diff --git a/tests/playwright/fixtures/sampleFiles/Image/5.jpeg b/tests/playwright/fixtures/sampleFiles/Image/5.jpeg new file mode 100644 index 0000000000..becc1dcdb9 Binary files /dev/null and b/tests/playwright/fixtures/sampleFiles/Image/5.jpeg differ diff --git a/tests/playwright/fixtures/sampleFiles/Image/6_bigSize.png b/tests/playwright/fixtures/sampleFiles/Image/6_bigSize.png new file mode 100644 index 0000000000..9fb77d9311 Binary files /dev/null and b/tests/playwright/fixtures/sampleFiles/Image/6_bigSize.png differ diff --git a/tests/playwright/pages/Dashboard/Grid/Column/Attachment.ts b/tests/playwright/pages/Dashboard/Grid/Column/Attachment.ts new file mode 100644 index 0000000000..0eb5f82185 --- /dev/null +++ b/tests/playwright/pages/Dashboard/Grid/Column/Attachment.ts @@ -0,0 +1,158 @@ +import { ColumnPageObject } from '.'; +import BasePage from '../../../Base'; +import { expect } from '@playwright/test'; + +export class AttachmentColumnPageObject extends BasePage { + readonly column: ColumnPageObject; + + constructor(column: ColumnPageObject) { + super(column.rootPage); + this.column = column; + } + + get() { + return this.column.get(); + } + + async advanceConfig({ + columnTitle, + fileCount, + fileSize, + fileTypesExcludeList, + }: { + columnTitle: string; + fileCount?: number; + fileSize?: number; + fileTypesExcludeList?: string[]; + }) { + await this.column.openEdit({ title: columnTitle }); + await this.column.editMenuShowMore(); + + // text box : nc-attachment-max-count + // text box : nc-attachment-max-size + // checkbox : ant-tree-checkbox + // Checkbox order: Application, Audio, Image, Video, Misc + + if (fileCount) { + const inputMaxCount = await this.column.get().locator(`.nc-attachment-max-count`); + await inputMaxCount.locator(`input`).fill(fileCount.toString()); + } + + if (fileSize) { + const inputMaxSize = await this.column.get().locator(`.nc-attachment-max-size`); + await inputMaxSize.locator(`input`).fill(fileSize.toString()); + } + + if (fileTypesExcludeList) { + const treeList = await this.column.get().locator(`.ant-tree-list`); + const checkboxList = await treeList.locator(`.ant-tree-treenode`); + + // print count of treenode + const count = await checkboxList.count(); + console.log(`count: ${count}`); + + // log checkboxList + for (let i = 0; i < count; i++) { + const checkbox = await checkboxList.nth(i); + const text = await checkbox.innerText(); + console.log(`text: ${text}`); + } + + for (let i = 0; i < fileTypesExcludeList.length; i++) { + const fileType = fileTypesExcludeList[i]; + switch (fileType) { + case 'Application': + await checkboxList.nth(0).locator(`.ant-tree-checkbox`).click(); + break; + case 'Audio': + await checkboxList.nth(1).locator(`.ant-tree-checkbox`).click(); + break; + case 'Image': + await checkboxList.nth(2).locator(`.ant-tree-checkbox`).click(); + break; + case 'Video': + await checkboxList.nth(3).locator(`.ant-tree-checkbox`).click(); + break; + case 'Misc': + await checkboxList.nth(4).locator(`.ant-tree-checkbox`).click(); + break; + default: + break; + } + } + + await this.rootPage.waitForTimeout(1000); + } + + await this.column.save({ isUpdated: true }); + } + + // add multiple options at once after column creation is completed + // + async addOptions({ columnTitle, options }: { columnTitle: string; options: string[] }) { + await this.column.openEdit({ title: columnTitle }); + for (let i = 0; i < options.length; i++) { + await this.column.get().locator('button:has-text("Add option")').click(); + await this.column.get().locator(`[data-testid="select-column-option-input-${i}"]`).click(); + await this.column.get().locator(`[data-testid="select-column-option-input-${i}"]`).fill(options[i]); + } + await this.column.save({ isUpdated: true }); + } + + async editOption({ columnTitle, index, newOption }: { index: number; columnTitle: string; newOption: string }) { + await this.column.openEdit({ title: columnTitle }); + + await this.column.get().locator(`[data-testid="select-column-option-input-${index}"]`).click(); + await this.column.get().locator(`[data-testid="select-column-option-input-${index}"]`).fill(newOption); + + await this.column.save({ isUpdated: true }); + } + + async deleteOption({ columnTitle, index }: { index: number; columnTitle: string }) { + await this.column.openEdit({ title: columnTitle }); + + await this.column.get().locator(`svg[data-testid="select-column-option-remove-${index}"]`).click(); + + await expect(this.column.get().getByTestId(`select-column-option-${index}`)).toHaveClass(/removed/); + + await this.column.save({ isUpdated: true }); + } + + async deleteOptionWithUndo({ columnTitle, index }: { index: number; columnTitle: string }) { + await this.column.openEdit({ title: columnTitle }); + + await this.column.get().locator(`svg[data-testid="select-column-option-remove-${index}"]`).click(); + + await expect(this.column.get().getByTestId(`select-column-option-${index}`)).toHaveClass(/removed/); + + await this.column.get().locator(`svg[data-testid="select-column-option-remove-undo-${index}"]`).click(); + + await expect(this.column.get().getByTestId(`select-column-option-${index}`)).not.toHaveClass(/removed/); + + await this.column.save({ isUpdated: true }); + } + + async reorderOption({ + columnTitle, + sourceOption, + destinationOption, + }: { + columnTitle: string; + sourceOption: string; + destinationOption: string; + }) { + await this.column.openEdit({ title: columnTitle }); + + await this.column.rootPage.waitForTimeout(150); + + await this.column.rootPage.dragAndDrop( + `svg[data-testid="select-option-column-handle-icon-${sourceOption}"]`, + `svg[data-testid="select-option-column-handle-icon-${destinationOption}"]`, + { + force: true, + } + ); + + await this.column.save({ isUpdated: true }); + } +} diff --git a/tests/playwright/pages/Dashboard/Grid/Column/index.ts b/tests/playwright/pages/Dashboard/Grid/Column/index.ts index 483e6945a3..064b2093b5 100644 --- a/tests/playwright/pages/Dashboard/Grid/Column/index.ts +++ b/tests/playwright/pages/Dashboard/Grid/Column/index.ts @@ -2,15 +2,18 @@ import { expect } from '@playwright/test'; import { GridPage } from '..'; import BasePage from '../../../Base'; import { SelectOptionColumnPageObject } from './SelectOptionColumn'; +import { AttachmentColumnPageObject } from './Attachment'; export class ColumnPageObject extends BasePage { readonly grid: GridPage; readonly selectOption: SelectOptionColumnPageObject; + readonly attachmentColumnPageObject: AttachmentColumnPageObject; constructor(grid: GridPage) { super(grid.rootPage); this.grid = grid; this.selectOption = new SelectOptionColumnPageObject(this); + this.attachmentColumnPageObject = new AttachmentColumnPageObject(this); } get() { @@ -298,6 +301,10 @@ export class ColumnPageObject extends BasePage { } } + async editMenuShowMore() { + await this.rootPage.locator('.nc-more-options').click(); + } + 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(); diff --git a/tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts b/tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts index 34ffc416be..bb12fa8e1e 100644 --- a/tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts +++ b/tests/playwright/pages/Dashboard/common/Cell/AttachmentCell.ts @@ -28,7 +28,38 @@ export class AttachmentCellPageObject extends BasePage { return await this.attachFile({ filePickUIAction: attachFileAction, filePath }); } + async expandModalAddFile({ filePath }: { filePath: string[] }) { + const attachFileAction = this.rootPage + .locator('.ant-modal.nc-attachment-modal.active') + .locator('[data-testid="attachment-cell-file-picker-button"]') + .click(); + return await this.attachFile({ filePickUIAction: attachFileAction, filePath }); + } + + async expandModalOpen({ index, columnHeader }: { index?: number; columnHeader: string }) { + return this.get({ index, columnHeader }) + .locator('.nc-cell > .nc-attachment-cell > .group.cursor-pointer') + .last() + .click(); + } + async verifyFile({ index, columnHeader }: { index: number; columnHeader: string }) { await expect(await this.get({ index, columnHeader }).locator('.nc-attachment')).toBeVisible(); } + + async verifyFileCount({ index, columnHeader, count }: { index: number; columnHeader: string; count: number }) { + const attachments = await this.get({ index, columnHeader }).locator( + '.nc-cell > .nc-attachment-cell > .flex > .nc-attachment' + ); + + console.log(await attachments.count()); + expect(await attachments.count()).toBe(count); + + // attachments should be of count 'count' + // await expect(await attachments.count()).toBe(count); + } + + async expandModalClose() { + return this.rootPage.locator('.ant-modal.nc-attachment-modal.active').press('Escape'); + } } diff --git a/tests/playwright/tests/columnAttachments.spec.ts b/tests/playwright/tests/columnAttachments.spec.ts index 3a378bd9e2..92b549db91 100644 --- a/tests/playwright/tests/columnAttachments.spec.ts +++ b/tests/playwright/tests/columnAttachments.spec.ts @@ -2,14 +2,18 @@ import { expect, test } from '@playwright/test'; import { DashboardPage } from '../pages/Dashboard'; import { SharedFormPage } from '../pages/SharedForm'; import setup from '../setup'; +import { AccountPage } from '../pages/Account'; +import { AccountLicensePage } from '../pages/Account/License'; test.describe('Attachment column', () => { let dashboard: DashboardPage; - let context: any; + let accountLicensePage: AccountLicensePage, accountPage: AccountPage, context: any; test.beforeEach(async ({ page }) => { context = await setup({ page }); dashboard = new DashboardPage(page, context.project); + accountPage = new AccountPage(page); + accountLicensePage = new AccountLicensePage(accountPage); }); test('Create and verify atttachent column, verify it in shared form,', async ({ page, context }) => { @@ -90,4 +94,118 @@ test.describe('Attachment column', () => { await expect(cells[1]).toBe('South Hill'); await expect(cells[2].includes('4.json(http://localhost:8080/download/')).toBe(true); }); + + test('Attachment enterprise features,', async ({ page, context }) => { + // configure enterprise key + test.slow(); + await accountLicensePage.goto(); + await accountLicensePage.saveLicenseKey('1234567890'); + await dashboard.goto(); + + await dashboard.treeView.openTable({ title: 'Country' }); + await dashboard.grid.column.create({ + title: 'testAttach', + type: 'Attachment', + }); + await dashboard.grid.column.attachmentColumnPageObject.advanceConfig({ + columnTitle: 'testAttach', + fileCount: 2, + fileSize: 1, + // allow only image type + fileTypesExcludeList: ['Application', 'Video', 'Audio', 'Misc'], + }); + + // in-cell, add big file, should get rejected + const bigFile = [`${process.cwd()}/fixtures/sampleFiles/Image/6_bigSize.png`]; + await dashboard.grid.cell.attachment.addFile({ + index: 1, + columnHeader: 'testAttach', + filePath: bigFile, + }); + // The size of ${file.name} exceeds the maximum file size ${attachmentMeta.maxAttachmentSize} MB. + await dashboard.verifyToast({ message: 'The size of 6_bigSize.png exceeds the maximum file size 1 MB.' }); + + // in-cell, add 2 files, should get accepted + const twoFileArray = [ + `${process.cwd()}/fixtures/sampleFiles/Image/1.jpeg`, + `${process.cwd()}/fixtures/sampleFiles/Image/2.png`, + ]; + await dashboard.grid.cell.attachment.addFile({ + index: 1, + columnHeader: 'testAttach', + filePath: twoFileArray, + }); + await dashboard.rootPage.waitForTimeout(2000); + await dashboard.grid.cell.attachment.verifyFileCount({ + index: 1, + columnHeader: 'testAttach', + count: 2, + }); + + // add another file, should get rejected + const oneFileArray = [`${process.cwd()}/fixtures/sampleFiles/Image/3.jpeg`]; + await dashboard.grid.cell.attachment.addFile({ + index: 1, + columnHeader: 'testAttach', + filePath: oneFileArray, + }); + // wait for toast 'You can only upload at most 2 files to this cell' + await dashboard.verifyToast({ message: 'You can only upload at most 2 files to this cell' }); + + // try to upload 3 files in one go, should get rejected + const threeFileArray = [ + `${process.cwd()}/fixtures/sampleFiles/Image/1.jpeg`, + `${process.cwd()}/fixtures/sampleFiles/Image/2.png`, + `${process.cwd()}/fixtures/sampleFiles/Image/3.jpeg`, + ]; + await dashboard.grid.cell.attachment.addFile({ + index: 2, + columnHeader: 'testAttach', + filePath: threeFileArray, + }); + await dashboard.verifyToast({ message: 'You can only upload at most 2 files to this cell' }); + + // open expand modal, try to insert file type not supported + // message: ${file.name} has the mime type ${file.type} which is not allowed in this column. + await dashboard.grid.cell.attachment.addFile({ + index: 3, + columnHeader: 'testAttach', + filePath: [`${process.cwd()}/fixtures/sampleFiles/1.json`], + }); + await dashboard.verifyToast({ + message: '1.json has the mime type application/json which is not allowed in this column.', + }); + + // Expand modal + + // open expand modal, try to insert more files + await dashboard.grid.cell.attachment.expandModalOpen({ + index: 1, + columnHeader: 'testAttach', + }); + await dashboard.grid.cell.attachment.expandModalAddFile({ + filePath: oneFileArray, + }); + await dashboard.verifyToast({ message: 'You can only upload at most 2 files to this cell' }); + + // open expand modal, try to insert file type not supported + // message: ${file.name} has the mime type ${file.type} which is not allowed in this column. + await dashboard.grid.cell.attachment.expandModalAddFile({ + filePath: [`${process.cwd()}/fixtures/sampleFiles/1.json`], + }); + await dashboard.verifyToast({ + message: '1.json has the mime type application/json which is not allowed in this column.', + }); + + // open expand modal, try to insert big file + // message: The size of ${file.name} exceeds the maximum file size ${attachmentMeta.maxAttachmentSize} MB. + await dashboard.grid.cell.attachment.expandModalAddFile({ + filePath: bigFile, + }); + await dashboard.verifyToast({ message: 'The size of 6_bigSize.png exceeds the maximum file size 1 MB.' }); + await dashboard.grid.cell.attachment.expandModalClose(); + + // wait for timeout + // await dashboard.rootPage.waitForTimeout(20000); + }); });