From 2c2af732159a1ef4ea16aa56c2ac94104c693eae Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Thu, 8 Jan 2026 13:08:29 +0100 Subject: [PATCH] refactoring --- frontend/e2e/DbManagerPage.ts | 179 ++++++++++++++++-------- frontend/e2e/datatable.spec.ts | 13 +- frontend/e2e/resource-dbmanager.spec.ts | 9 +- frontend/e2e/utils.ts | 20 +++ 4 files changed, 152 insertions(+), 69 deletions(-) create mode 100644 frontend/e2e/utils.ts diff --git a/frontend/e2e/DbManagerPage.ts b/frontend/e2e/DbManagerPage.ts index f871726558..f7d59561b3 100644 --- a/frontend/e2e/DbManagerPage.ts +++ b/frontend/e2e/DbManagerPage.ts @@ -1,18 +1,56 @@ -// Assumes the db manager was already opened +// Assume the db manager was already opened import { expect, Locator, Page } from '@playwright/test' import type { DbFeatures } from '../src/lib/components/apps/components/display/dbtable/dbFeatures' +import { ConfirmationModal, Toast } from './utils' + +export async function runDbManagerTests(page: Page, dbFeatures: DbFeatures) { + let dbManager = new DbManagerPage(page) + await dbManager.expectToBeVisible() + + // Create table + const tableEditor = await dbManager.openCreateTableDrawer() + await tableEditor.setTableName('friend') + await tableEditor.addColumn('name', 'TEXT') + await tableEditor.createTable() + + await Toast.expectSuccess(page, 'friend created') + + // Select and work with the table + await dbManager.selectTable('friend') + + // Insert a row + const insertDrawer = await dbManager.openInsertDrawer() + await insertDrawer.fillField('name', 'Alice') + await insertDrawer.insert() + + await Toast.expectSuccess(page, 'Row inserted') + + // Verify and edit the cell + const dataGrid = dbManager.dataGrid() + await dataGrid.expectCellValue('Alice') + await dataGrid.editCell('Alice', 'Bob') + + await Toast.expectSuccess(page, 'Value updated') + await dataGrid.expectCellValue('Bob') + + // Delete the table + const actionsMenu = await dbManager.openActionsMenu('friend') + await actionsMenu.deleteTable() + + await Toast.expectSuccess(page, "Table 'friend' deleted successfully") +} export class DbManagerPage { page: Page - features: DbFeatures - constructor(page: Page, features: DbFeatures) { + constructor(page: Page) { this.page = page - this.features = features } dbManager = () => this.page.locator('#db-manager-drawer') + expectToBeVisible = () => expect(this.dbManager()).toBeVisible() + async openCreateTableDrawer(): Promise { await this.dbManager().locator('button:has-text("New table")').click() const tableEditor = new TableEditorDrawer(this.page) @@ -20,64 +58,25 @@ export class DbManagerPage { return tableEditor } - async runTest() { - await expect(this.dbManager()).toBeVisible() - const page = this.page + async selectTable(tableName: string) { + const tableKey = this.page.locator('.db-manager-table-key', { hasText: tableName }) + await expect(tableKey).toBeVisible({ timeout: 10000 }) + await tableKey.click() + } - let tableEditor = await this.openCreateTableDrawer() - await tableEditor.setTableName('friend') - await tableEditor.addColumn('name', 'TEXT') - - await page.locator('button:has-text("Create table")').click() - - await page.locator('#db-table-editor-confirmation-modal button:has-text("Create")').click() - - const saveSuccessToast = page.locator(`.toast-success:has-text("friend created")`) - await expect(saveSuccessToast).toBeVisible({ timeout: 10000 }) - - const friendTableKey = page.locator('.db-manager-table-key', { hasText: 'friend' }) - await expect(friendTableKey).toBeVisible({ timeout: 10000 }) - await friendTableKey.click() - - // Add a new row + async openInsertDrawer(): Promise { await this.dbManager().locator('button:has-text("Insert")').click() - let insertRowDrawer = page.locator('#insert-row-drawer') - await expect(insertRowDrawer).toBeVisible({ timeout: 10000 }) - await insertRowDrawer.locator('textarea').fill('Alice', { force: true }) // Not sure why force is needed here - await insertRowDrawer.locator('button:has-text("Insert")').click() + return new InsertRowDrawer(this.page) + } - const rowInsertedToast = page.locator(`.toast-success:has-text("Row inserted")`) - await expect(rowInsertedToast).toBeVisible({ timeout: 10000 }) + dataGrid(): DataGrid { + return new DataGrid(this.page, this.dbManager()) + } - const insertedRow = this.dbManager().locator('.ag-cell-value', { hasText: 'Alice' }) - await expect(insertedRow).toBeVisible({ timeout: 10000 }) - - // Edit the row - await insertedRow.dblclick() - const cellEditor = this.dbManager().locator('.ag-cell-editor input') - await cellEditor.fill('Bob') - await cellEditor.press('Enter') - - let rowUpdatedToast = page.locator(`.toast-success:has-text("Value updated")`) - await expect(rowUpdatedToast).toBeVisible({ timeout: 10000 }) - - const updatedRow = this.dbManager().locator('.ag-cell-value', { hasText: 'Bob' }) - await expect(updatedRow).toBeVisible({ timeout: 10000 }) - - const actionsBtn = this.dbManager().locator('#db-manager-table-actions-friend') + async openActionsMenu(tableName: string): Promise { + const actionsBtn = this.dbManager().locator(`#db-manager-table-actions-${tableName}`) await actionsBtn.click() - - await page.locator('button:has-text("Delete table")').click() - - let deletePermanentlyBtn = page.locator( - '#db-manager-delete-table-confirmation-modal button:has-text("Delete")' - ) - await deletePermanentlyBtn.click() - - let tableDeletedToast = page.locator( - `.toast-success:has-text("Table 'friend' deleted successfully")` - ) - await expect(tableDeletedToast).toBeVisible({ timeout: 10000 }) + return new TableActionsMenu(this.page) } } @@ -110,6 +109,11 @@ class TableEditorDrawer { await column.setType(columnType) return column } + + async createTable() { + await this.page.locator('button:has-text("Create table")').click() + await ConfirmationModal.confirm(this.page, '#db-table-editor-confirmation-modal', 'Create') + } } class Column { @@ -144,3 +148,64 @@ class Column { await this.page.locator(`li:has(:text-is("${columnType}"))`).click() } } + +class InsertRowDrawer { + page: Page + + constructor(page: Page) { + this.page = page + } + + drawer = () => this.page.locator('#insert-row-drawer') + + async fillField(fieldName: string, value: string) { + await expect(this.drawer()).toBeVisible({ timeout: 10000 }) + // For now, assumes single field - could be enhanced to handle multiple fields + await this.drawer().locator('textarea').fill(value, { force: true }) + } + + async insert() { + await this.drawer().locator('button:has-text("Insert")').click() + } +} + +class DataGrid { + page: Page + dbManager: Locator + + constructor(page: Page, dbManager: Locator) { + this.page = page + this.dbManager = dbManager + } + + async expectCellValue(value: string) { + const cell = this.dbManager.locator('.ag-cell-value', { hasText: value }) + await expect(cell).toBeVisible({ timeout: 10000 }) + } + + async editCell(oldValue: string, newValue: string) { + const cell = this.dbManager.locator('.ag-cell-value', { hasText: oldValue }) + await cell.dblclick() + + const cellEditor = this.dbManager.locator('.ag-cell-editor input') + await cellEditor.fill(newValue) + await cellEditor.press('Enter') + } +} + +class TableActionsMenu { + page: Page + + constructor(page: Page) { + this.page = page + } + + async deleteTable() { + await this.page.locator('button:has-text("Delete table")').click() + await ConfirmationModal.confirm( + this.page, + '#db-manager-delete-table-confirmation-modal', + 'Delete' + ) + } +} diff --git a/frontend/e2e/datatable.spec.ts b/frontend/e2e/datatable.spec.ts index 4c29f244c4..af8569e29c 100644 --- a/frontend/e2e/datatable.spec.ts +++ b/frontend/e2e/datatable.spec.ts @@ -1,5 +1,5 @@ import { test, expect, Page } from '@playwright/test' -import { DbManagerPage } from './DbManagerPage' +import { DbManagerPage, runDbManagerTests } from './DbManagerPage' import { getDbFeatures } from '../src/lib/components/apps/components/display/dbtable/dbFeatures' async function setupNewDataTable(page: Page): Promise<{ datatableId: string }> { @@ -79,9 +79,10 @@ test('setup a datatable and ensure db manager works', async ({ page }) => { const lastRow = table.locator('tr').nth(-2) lastRow.locator('button:has-text("Manage")').click() - let dbManagerPage = new DbManagerPage( - page, - getDbFeatures({ type: 'database', resourceType: 'postgresql', resourcePath: '' }) - ) - await dbManagerPage.runTest() + const dbFeatures = getDbFeatures({ + type: 'database', + resourceType: 'postgresql', + resourcePath: '' + }) + await runDbManagerTests(page, dbFeatures) }) diff --git a/frontend/e2e/resource-dbmanager.spec.ts b/frontend/e2e/resource-dbmanager.spec.ts index cb464e57f5..0e10b4dc00 100644 --- a/frontend/e2e/resource-dbmanager.spec.ts +++ b/frontend/e2e/resource-dbmanager.spec.ts @@ -1,5 +1,5 @@ import { test, expect, Page } from '@playwright/test' -import { DbManagerPage } from './DbManagerPage' +import { DbManagerPage, runDbManagerTests } from './DbManagerPage' import { getDbFeatures } from '../src/lib/components/apps/components/display/dbtable/dbFeatures' import { DbType } from '../src/lib/components/dbTypes' import os from 'os' @@ -77,11 +77,8 @@ test.describe('DB Manager', () => { const manageButton = resourceRow.locator('button:has-text("Manage")') await manageButton.click() - let dbManagerPage = new DbManagerPage( - page, - getDbFeatures({ type: 'database', resourceType: dbType, resourcePath: '' }) - ) - await dbManagerPage.runTest() + const dbFeatures = getDbFeatures({ type: 'database', resourceType: dbType, resourcePath: '' }) + await runDbManagerTests(page, dbFeatures) }) } }) diff --git a/frontend/e2e/utils.ts b/frontend/e2e/utils.ts new file mode 100644 index 0000000000..d931b94f5c --- /dev/null +++ b/frontend/e2e/utils.ts @@ -0,0 +1,20 @@ +import { expect, Page } from '@playwright/test' + +export class Toast { + static async expectSuccess(page: Page, message: string) { + const toast = page.locator(`.toast-success:has-text("${message}")`) + await expect(toast).toBeVisible({ timeout: 10000 }) + } + + static async expectError(page: Page, message: string) { + const toast = page.locator(`.toast-error:has-text("${message}")`) + await expect(toast).toBeVisible({ timeout: 10000 }) + } +} + +export class ConfirmationModal { + static async confirm(page: Page, modalSelector: string, buttonText: string) { + const confirmBtn = page.locator(`${modalSelector} button:has-text("${buttonText}")`) + await confirmBtn.click() + } +}