diff --git a/tests/playwright/pages/Dashboard/TreeView.ts b/tests/playwright/pages/Dashboard/TreeView.ts index 54b0e66da0..c3aa3fbf35 100644 --- a/tests/playwright/pages/Dashboard/TreeView.ts +++ b/tests/playwright/pages/Dashboard/TreeView.ts @@ -1,6 +1,8 @@ import { expect, Locator } from '@playwright/test'; import { DashboardPage } from '.'; import BasePage from '../Base'; +import { NcContext } from '../../setup'; +import { isEE } from '../../setup/db'; export class TreeViewPage extends BasePage { readonly dashboard: DashboardPage; @@ -25,7 +27,7 @@ export class TreeViewPage extends BasePage { .locator('[data-testid="nc-sidebar-add-project-entity"]'); } - getProjectContextMenu({ projectTitle }: { projectTitle: string }) { + private getProjectContextMenu({ projectTitle }: { projectTitle: string }) { return this.dashboard .get() .getByTestId(`nc-sidebar-project-title-${projectTitle}`) @@ -214,7 +216,9 @@ export class TreeViewPage extends BasePage { await settingsMenu.locator(`.nc-sidebar-project-project-settings`).click(); } - async quickImport({ title, projectTitle }: { title: string; projectTitle: string }) { + async quickImport({ title, projectTitle, context }: { title: string; projectTitle: string; context: NcContext }) { + projectTitle = this.scopedProjectTitle({ title: projectTitle, context }); + await this.getProjectContextMenu({ projectTitle }).hover(); await this.getProjectContextMenu({ projectTitle }).click(); const importMenu = this.dashboard.get().locator('.ant-dropdown-menu'); @@ -269,9 +273,12 @@ export class TreeViewPage extends BasePage { ).toHaveCount(1); } - async validateRoleAccess(param: { role: string; projectTitle?: string; tableTitle?: string }) { + async validateRoleAccess(param: { role: string; projectTitle?: string; tableTitle?: string; context: NcContext }) { + const context = param.context; + param.projectTitle = param.projectTitle ?? context.project.title; + const count = param.role.toLowerCase() === 'creator' || param.role.toLowerCase() === 'owner' ? 1 : 0; - const pjtNode = await this.getProject({ index: 0, title: param.projectTitle }); + const pjtNode = await this.getProject({ title: param.projectTitle }); await pjtNode.hover(); // add new table button & context menu is visible only for owner & creator @@ -284,59 +291,46 @@ export class TreeViewPage extends BasePage { await expect(tblNode.locator('.nc-tbl-context-menu')).toHaveCount(count); } - async openProject({ title, projectCount }: { title: string; projectCount?: number }) { - const nodes = this.get().locator(`.project-title-node`); - - // at times, page is not rendered yet when trying to open project - // hence retry logic to wait for expected number of projects to be available - if (projectCount) { - let retryCount = 0; - while (retryCount < 5) { - if ((await nodes.count()) === projectCount) break; - await this.rootPage.waitForTimeout(retryCount * 500); - retryCount++; - } - } + async openProject({ title, context }: { title: string; context: NcContext }) { + title = this.scopedProjectTitle({ title, context }); // loop through nodes.count() to find the node with title - for (let i = 0; i < (await nodes.count()); i++) { - const node = nodes.nth(i); - const nodeTitle = await node.innerText(); - - // check if nodeTitle contains title - if (nodeTitle.toLowerCase().includes(title.toLowerCase())) { - // click on node - await node.waitFor({ state: 'visible' }); - await node.click(); - break; - } - } + await this.get().getByTestId(`nc-sidebar-project-title-${title}`).click(); await this.rootPage.waitForTimeout(1000); } - private async getProject(param: { index: number; title?: string }) { - if (param.title) { - return this.get().getByTestId(`nc-sidebar-project-title-${param.title}`); - } + scopedProjectTitle({ title, context }: { title: string; context: NcContext }) { + if (isEE()) return title; - return this.get().locator(`.project-title-node`).nth(param.index); + if (title.toLowerCase().startsWith('xcdb')) return `${title}`; + + return title === context.project.title ? context.project.title : `nc-${context.workerId}-${title}`; + } + + private async getProject(param: { title?: string }) { + return this.get().getByTestId(`nc-sidebar-project-title-${param.title}`); } - async renameProject(param: { newTitle: string; title: string }) { + async renameProject(param: { newTitle: string; title: string; context: NcContext }) { + param.title = this.scopedProjectTitle({ title: param.title, context: param.context }); + param.newTitle = this.scopedProjectTitle({ title: param.newTitle, context: param.context }); + await this.getProjectContextMenu({ projectTitle: param.title }).hover(); await this.getProjectContextMenu({ projectTitle: param.title }).click(); const contextMenu = this.dashboard.get().locator('.ant-dropdown-menu.nc-scrollbar-md:visible').last(); await contextMenu.waitFor(); await contextMenu.locator(`.ant-dropdown-menu-item:has-text("Rename")`).click(); - const projectNodeInput = (await this.getProject({ index: 0, title: param.title })).locator('input'); + const projectNodeInput = (await this.getProject({ title: param.title })).locator('input'); await projectNodeInput.clear(); await projectNodeInput.fill(param.newTitle); await projectNodeInput.press('Enter'); } - async deleteProject(param: { title: string }) { + async deleteProject(param: { title: string; context: NcContext }) { + param.title = this.scopedProjectTitle({ title: param.title, context: param.context }); + await this.getProjectContextMenu({ projectTitle: param.title }).hover(); await this.getProjectContextMenu({ projectTitle: param.title }).click(); const contextMenu = this.dashboard.get().locator('.ant-dropdown-menu.nc-scrollbar-md:visible').last(); @@ -346,7 +340,9 @@ export class TreeViewPage extends BasePage { await this.rootPage.locator('div.ant-modal-content').locator(`button.ant-btn:has-text("Delete")`).click(); } - async duplicateProject(param: { title: string }) { + async duplicateProject(param: { title: string; context: NcContext }) { + param.title = this.scopedProjectTitle({ title: param.title, context: param.context }); + await this.getProjectContextMenu({ projectTitle: param.title }).hover(); await this.getProjectContextMenu({ projectTitle: param.title }).click(); const contextMenu = this.dashboard.get().locator('.ant-dropdown-menu.nc-scrollbar-md:visible'); diff --git a/tests/playwright/pages/Dashboard/common/Cell/index.ts b/tests/playwright/pages/Dashboard/common/Cell/index.ts index a6ca7cf2ef..db02bb0f1a 100644 --- a/tests/playwright/pages/Dashboard/common/Cell/index.ts +++ b/tests/playwright/pages/Dashboard/common/Cell/index.ts @@ -366,10 +366,9 @@ export class CellPageObject extends BasePage { await cell.locator('.nc-datatype-link').click(); await this.waitForResponse({ uiAction: async () => - this.rootPage - .locator(`[data-testid="nc-child-list-item"]`) - .nth((await this.rootPage.locator(`[data-testid="nc-child-list-item"]`).count()) - 1) - .click(), + this.rootPage.locator(`[data-testid="nc-child-list-item"]`).last().click({ + force: true, + }), requestUrlPathToMatch: '/api/v1/db/data/noco/', httpMethodsToMatch: ['GET'], }); diff --git a/tests/playwright/pages/Dashboard/common/LeftSidebar/index.ts b/tests/playwright/pages/Dashboard/common/LeftSidebar/index.ts index a7eec0d005..36cb8b5e65 100644 --- a/tests/playwright/pages/Dashboard/common/LeftSidebar/index.ts +++ b/tests/playwright/pages/Dashboard/common/LeftSidebar/index.ts @@ -2,6 +2,8 @@ import { Locator } from '@playwright/test'; import { DashboardPage } from '../../index'; import BasePage from '../../../Base'; import { getTextExcludeIconText } from '../../../../tests/utils/general'; +import { isEE } from '../../../../setup/db'; +import { NcContext } from '../../../../setup'; export class LeftSidebarPage extends BasePage { readonly project: any; @@ -30,7 +32,8 @@ export class LeftSidebarPage extends BasePage { return this.dashboard.get().locator('.nc-sidebar'); } - async createProject({ title }: { title: string }) { + async createProject({ title, context }: { title: string; context: NcContext }) { + title = isEE() ? title : `nc-${context.workerId}-${title}`; await this.btn_newProject.click(); await this.rootPage.locator('.ant-modal-content:has-text(" Create Database")').waitFor(); await this.rootPage.locator('.ant-modal-content:has-text(" Create Database")').locator('input').fill(title); diff --git a/tests/playwright/quickTests/commonTest.ts b/tests/playwright/quickTests/commonTest.ts index aa42792797..1d5ee0aef5 100644 --- a/tests/playwright/quickTests/commonTest.ts +++ b/tests/playwright/quickTests/commonTest.ts @@ -249,7 +249,7 @@ const quickVerify = async ({ // await dashboard.clickHome(); // const workspacePage = new WorkspacePage(dashboard.rootPage); // await workspacePage.projectDelete({ title: context.project.title }); - await dashboard.treeView.deleteProject({ title: context.project.title }); + await dashboard.treeView.deleteProject({ title: context.project.title, context }); } }; diff --git a/tests/playwright/setup/index.ts b/tests/playwright/setup/index.ts index 91ad7c6910..6d8eb61c19 100644 --- a/tests/playwright/setup/index.ts +++ b/tests/playwright/setup/index.ts @@ -244,7 +244,7 @@ async function localInit({ for (const p of projects.list) { // check if p.title starts with projectTitle if ( - p.title.startsWith(`pgExtREST_p${process.env.TEST_PARALLEL_INDEX}`) || + p.title.startsWith(`pgExtREST${process.env.TEST_PARALLEL_INDEX}`) || p.title.startsWith(`xcdb_p${process.env.TEST_PARALLEL_INDEX}`) ) { try { diff --git a/tests/playwright/tests/db/columns/columnLinkToAnotherRecord.spec.ts b/tests/playwright/tests/db/columns/columnLinkToAnotherRecord.spec.ts index 5225ff88ba..085068424e 100644 --- a/tests/playwright/tests/db/columns/columnLinkToAnotherRecord.spec.ts +++ b/tests/playwright/tests/db/columns/columnLinkToAnotherRecord.spec.ts @@ -170,6 +170,7 @@ test.describe('LTAR create & update', () => { // Unlink LTAR cells for (let i = 0; i < expected2.length; i++) { for (let j = 0; j < expected2[i].length; j++) { + await dashboard.rootPage.waitForTimeout(500); await dashboard.grid.cell.unlinkVirtualCell({ index: j, columnHeader: colHeaders2[i], diff --git a/tests/playwright/tests/db/features/baseShare.spec.ts b/tests/playwright/tests/db/features/baseShare.spec.ts index ce175c70e2..8265a8d19c 100644 --- a/tests/playwright/tests/db/features/baseShare.spec.ts +++ b/tests/playwright/tests/db/features/baseShare.spec.ts @@ -35,6 +35,7 @@ test.describe('Shared base', () => { await dashboard.treeView.validateRoleAccess({ role: role.toLowerCase(), + context, }); await dashboard.grid.verifyRoleAccess({ diff --git a/tests/playwright/tests/db/features/erd.spec.ts b/tests/playwright/tests/db/features/erd.spec.ts index 9760d42d12..cfd9248858 100644 --- a/tests/playwright/tests/db/features/erd.spec.ts +++ b/tests/playwright/tests/db/features/erd.spec.ts @@ -279,7 +279,7 @@ test.describe('Erd', () => { // Create table and verify ERD await dashboard.treeView.createTable({ title: 'Test', projectTitle: context.project.title }); // Verify in Settings ERD and table ERD - await dashboard.treeView.openProject({ title: context.project.title }); + await dashboard.treeView.openProject({ title: context.project.title, context }); await openProjectErd(); await dashboard.details.relations.verifyNode({ tableName: `Test`, diff --git a/tests/playwright/tests/db/features/import.spec.ts b/tests/playwright/tests/db/features/import.spec.ts index 4237d86394..42167552b4 100644 --- a/tests/playwright/tests/db/features/import.spec.ts +++ b/tests/playwright/tests/db/features/import.spec.ts @@ -21,7 +21,7 @@ test.describe('Import', () => { }); test('Airtable', async () => { - await dashboard.treeView.quickImport({ title: 'Airtable', projectTitle: context.project.title }); + await dashboard.treeView.quickImport({ title: 'Airtable', projectTitle: context.project.title, context }); await dashboard.importAirtable.import({ key: airtableApiKey, baseId: airtableApiBase, @@ -31,7 +31,7 @@ test.describe('Import', () => { }); test('CSV', async () => { - await dashboard.treeView.quickImport({ title: 'CSV file', projectTitle: context.project.title }); + await dashboard.treeView.quickImport({ title: 'CSV file', projectTitle: context.project.title, context }); }); test('Excel', async () => { @@ -46,7 +46,7 @@ test.describe('Import', () => { { name: 'Sheet4', columns: col }, ]; - await dashboard.treeView.quickImport({ title: 'Microsoft Excel', projectTitle: context.project.title }); + await dashboard.treeView.quickImport({ title: 'Microsoft Excel', projectTitle: context.project.title, context }); await dashboard.importTemplate.import({ file: `${process.cwd()}/fixtures/sampleFiles/simple.xlsx`, result: expected, @@ -66,6 +66,6 @@ test.describe('Import', () => { }); test('JSON', async () => { - await dashboard.treeView.quickImport({ title: 'JSON file', projectTitle: context.project.title }); + await dashboard.treeView.quickImport({ title: 'JSON file', projectTitle: context.project.title, context }); }); }); diff --git a/tests/playwright/tests/db/features/metaLTAR.spec.ts b/tests/playwright/tests/db/features/metaLTAR.spec.ts index 59eb80d683..d997a2f277 100644 --- a/tests/playwright/tests/db/features/metaLTAR.spec.ts +++ b/tests/playwright/tests/db/features/metaLTAR.spec.ts @@ -58,7 +58,7 @@ test.describe.serial('Test table', () => { // create a new xcdb project const xcdb = await createXcdb(context); await dashboard.rootPage.reload(); - await dashboard.treeView.openProject({ title: 'Xcdb', projectCount: 2 }); + await dashboard.treeView.openProject({ title: 'xcdb', context }); api = new Api({ baseURL: `http://localhost:8080/`, @@ -216,7 +216,7 @@ test.describe.serial('Test table', () => { }); test('Delete record - single, over UI', async () => { - await dashboard.treeView.openProject({ title: 'Xcdb', projectCount: 2 }); + await dashboard.treeView.openProject({ title: 'xcdb', context }); await dashboard.treeView.openTable({ title: 'Table0' }); await grid.deleteRow(0); diff --git a/tests/playwright/tests/db/general/projectOperations.spec.ts b/tests/playwright/tests/db/general/projectOperations.spec.ts index 7683ed4117..7ec0afc770 100644 --- a/tests/playwright/tests/db/general/projectOperations.spec.ts +++ b/tests/playwright/tests/db/general/projectOperations.spec.ts @@ -13,7 +13,7 @@ test.describe('Project operations', () => { let api: Api; test.setTimeout(100000); - async function getProjectList(workspaceId: string) { + async function getProjectList(workspaceId?: string) { let projectList: ProjectListType; if (isEE() && api['workspaceProject']) { projectList = await api['workspaceProject'].list(workspaceId); @@ -25,9 +25,9 @@ test.describe('Project operations', () => { } async function createTestProjectWithData(testProjectName: string) { - await dashboard.leftSidebar.createProject({ title: testProjectName }); - await dashboard.treeView.openProject({ title: testProjectName }); - await dashboard.treeView.quickImport({ title: 'Airtable', projectTitle: testProjectName }); + await dashboard.leftSidebar.createProject({ title: testProjectName, context }); + await dashboard.treeView.openProject({ title: testProjectName, context }); + await dashboard.treeView.quickImport({ title: 'Airtable', projectTitle: testProjectName, context }); await dashboard.importAirtable.import({ key: airtableApiKey, baseId: airtableApiBase, @@ -36,8 +36,8 @@ test.describe('Project operations', () => { } async function cleanupTestData(dupeProjectName: string, testProjectName: string) { - await dashboard.treeView.deleteProject({ title: dupeProjectName }); - await dashboard.treeView.deleteProject({ title: testProjectName }); + await dashboard.treeView.deleteProject({ title: dupeProjectName, context }); + await dashboard.treeView.deleteProject({ title: testProjectName, context }); } test.beforeEach(async ({ page }) => { @@ -58,29 +58,33 @@ test.describe('Project operations', () => { }); test('rename, delete', async () => { - await dashboard.leftSidebar.createProject({ title: 'project-firstName' }); - await dashboard.treeView.renameProject({ title: 'project-firstName', newTitle: 'project-rename' }); - await dashboard.treeView.openProject({ title: 'project-rename' }); - await dashboard.treeView.deleteProject({ title: 'project-rename' }); + await dashboard.leftSidebar.createProject({ title: 'project-firstName', context }); + await dashboard.treeView.renameProject({ title: 'project-firstName', newTitle: 'project-rename', context }); + await dashboard.treeView.openProject({ title: 'project-rename', context }); + await dashboard.treeView.deleteProject({ title: 'project-rename', context }); }); test('project_duplicate', async () => { // if project already exists, delete it to avoid test failures due to residual data const random = Math.floor(Math.random() * 1000000); const testProjectName = `Project-To-Import-Export-${random}`; + const scopedProjectName = dashboard.treeView.scopedProjectTitle({ + title: testProjectName, + context, + }); // // data creation for original test project await createTestProjectWithData(testProjectName); // duplicate duplicate - await dashboard.treeView.duplicateProject({ title: testProjectName }); - await dashboard.treeView.openProject({ title: testProjectName }); + await dashboard.treeView.duplicateProject({ title: testProjectName, context }); + await dashboard.treeView.openProject({ title: testProjectName, context }); // compare - const projectList = await getProjectList(context.workspace.id); + const projectList = await getProjectList(context.workspace?.id); - const testProjectId = projectList.list.find((p: any) => p.title === testProjectName); - const dupeProjectId = projectList.list.find((p: any) => p.title.startsWith(testProjectName + ' copy')); + const testProjectId = projectList.list.find((p: any) => p.title === scopedProjectName); + const dupeProjectId = projectList.list.find((p: any) => p.title.startsWith(scopedProjectName + ' copy')); const projectInfoOp: ProjectInfoApiUtil = new ProjectInfoApiUtil(context.token); const original: Promise = projectInfoOp.extractProjectInfo(testProjectId.id); const duplicate: Promise = projectInfoOp.extractProjectInfo(dupeProjectId.id); diff --git a/tests/playwright/tests/db/general/tableOperations.spec.ts b/tests/playwright/tests/db/general/tableOperations.spec.ts index b52fcf5b5f..eccb029edb 100644 --- a/tests/playwright/tests/db/general/tableOperations.spec.ts +++ b/tests/playwright/tests/db/general/tableOperations.spec.ts @@ -31,7 +31,7 @@ test.describe('Table Operations', () => { if (!isEE()) { // Audit logs in clickhouse; locally wont be accessible - await dashboard.treeView.openProject({ title: context.project.title }); + await dashboard.treeView.openProject({ title: context.project.title, context }); await dashboard.projectView.tab_dataSources.click(); await dashboard.projectView.dataSources.openAudit({ rowIndex: 0 }); diff --git a/tests/playwright/tests/db/views/viewForm.spec.ts b/tests/playwright/tests/db/views/viewForm.spec.ts index c4cfd6d7d4..3e6ba05f8f 100644 --- a/tests/playwright/tests/db/views/viewForm.spec.ts +++ b/tests/playwright/tests/db/views/viewForm.spec.ts @@ -381,7 +381,7 @@ test.describe('Form view with LTAR', () => { await dashboard.leftSidebar.openWorkspace({ title: context.workspace.title }); await dashboard.rootPage.waitForTimeout(500); } - await dashboard.treeView.openProject({ title: context.project.title }); + await dashboard.treeView.openProject({ title: context.project.title, context }); await dashboard.rootPage.waitForTimeout(500); await dashboard.treeView.openTable({ title: 'Country' });