diff --git a/cypress/component/ContentReferenceWidget.cy.js b/cypress/component/ContentReferenceWidget.cy.js index acd16c04d5..8e45fc633e 100644 --- a/cypress/component/ContentReferenceWidget.cy.js +++ b/cypress/component/ContentReferenceWidget.cy.js @@ -80,8 +80,9 @@ describe('ContentReferenceWidget', () => { cy.reply('**/index.php/apps/tables/row/*', rowData) }) - // Click the edit button on the first row - cy.get('@rows').first().find('td.sticky button').click({ force: true }) + // Open the row action menu on the first row, then click Edit + cy.get('@rows').first().find('[data-cy="rowActionMenu"] button').click({ force: true }) + cy.get('[data-cy="editRowBtn"]').click() // Get the first field of the Edit Row modal cy.get('.modal__content').as('editRowModal') diff --git a/cypress/e2e/view-filtering-selection-row-removal.cy.js b/cypress/e2e/view-filtering-selection-row-removal.cy.js index dd6cdb76db..72d04cfe1f 100644 --- a/cypress/e2e/view-filtering-selection-row-removal.cy.js +++ b/cypress/e2e/view-filtering-selection-row-removal.cy.js @@ -170,7 +170,8 @@ describe('Filtering in a view by selection columns (Cypress supplement – row r // # edit checked row // ## uncheck cy.intercept({ method: 'PUT', url: '**/apps/tables/row/*' }).as('updateCheckedRow') - cy.contains('[data-cy="ncTable"] [data-cy="customTableRow"]', 'checked row').closest('[data-cy="customTableRow"]').find('[data-cy="editRowBtn"]').click() + cy.contains('[data-cy="ncTable"] [data-cy="customTableRow"]', 'checked row').closest('[data-cy="customTableRow"]').find('[data-cy="rowActionMenu"] button').click() + cy.get('[data-cy="editRowBtn"]').click() cy.get('[data-cy="editRowModal"] .checkbox-radio-switch').click() cy.get('[data-cy="editRowSaveButton"]').click() diff --git a/playwright/e2e/column-datetime.spec.ts b/playwright/e2e/column-datetime.spec.ts index 66678c738b..69f27240d9 100644 --- a/playwright/e2e/column-datetime.spec.ts +++ b/playwright/e2e/column-datetime.spec.ts @@ -4,7 +4,7 @@ */ import { test, expect } from '../support/fixtures' -import { createDatetimeColumn, createTable, loadTable, removeColumn } from '../support/commands' +import { createDatetimeColumn, createTable, loadTable, openRowActionMenu, removeColumn } from '../support/commands' const columnTitle = 'date and time' const tableTitle = 'Test datetime' @@ -32,9 +32,10 @@ test.describe('Test column ' + columnTitle, () => { await expect(page.locator('.custom-table table tr td div').filter({ hasText: '5:15' }).first()).toBeVisible() // delete row - await page.locator('.NcTable tr td button').first().click() - await page.locator('button').filter({ hasText: 'Delete' }).click() - await page.locator('button').filter({ hasText: /I really/ }).click({ force: true }) + await openRowActionMenu(page, page.locator('[data-cy="customTableRow"]').first()) + await page.locator('[data-cy="deleteRowBtn"]').click() + await page.locator('[data-cy="confirmDialog"]').getByRole('button', { name: 'Confirm' }).click() + await expect(page.locator('[data-cy="customTableRow"]')).toHaveCount(0, { timeout: 10000 }) await removeColumn(page, columnTitle) }) diff --git a/playwright/e2e/column-datetimeDate.spec.ts b/playwright/e2e/column-datetimeDate.spec.ts index c81e15a107..8ea402b9c4 100644 --- a/playwright/e2e/column-datetimeDate.spec.ts +++ b/playwright/e2e/column-datetimeDate.spec.ts @@ -4,7 +4,7 @@ */ import { test, expect } from '../support/fixtures' -import { createDatetimeDateColumn, createTable, loadTable, removeColumn } from '../support/commands' +import { createDatetimeDateColumn, createTable, loadTable, openRowActionMenu, removeColumn } from '../support/commands' const columnTitle = 'date' const tableTitle = 'Test datetimeDate' @@ -29,9 +29,10 @@ test.describe('Test column ' + columnTitle, () => { await expect(page.locator('.custom-table table tr td div').filter({ hasText: '2023' }).first()).toBeVisible() // delete row - await page.locator('.NcTable tr td button').first().click() - await page.locator('button').filter({ hasText: 'Delete' }).click() - await page.locator('button').filter({ hasText: /I really/ }).click({ force: true }) + await openRowActionMenu(page, page.locator('[data-cy="customTableRow"]').first()) + await page.locator('[data-cy="deleteRowBtn"]').click() + await page.locator('[data-cy="confirmDialog"]').getByRole('button', { name: 'Confirm' }).click() + await expect(page.locator('[data-cy="customTableRow"]')).toHaveCount(0, { timeout: 10000 }) await removeColumn(page, columnTitle) }) diff --git a/playwright/e2e/column-datetimeTime.spec.ts b/playwright/e2e/column-datetimeTime.spec.ts index 460c4b7416..d69935a205 100644 --- a/playwright/e2e/column-datetimeTime.spec.ts +++ b/playwright/e2e/column-datetimeTime.spec.ts @@ -4,7 +4,7 @@ */ import { test, expect } from '../support/fixtures' -import { createDatetimeTimeColumn, createTable, loadTable, removeColumn } from '../support/commands' +import { createDatetimeTimeColumn, createTable, loadTable, openRowActionMenu, removeColumn } from '../support/commands' const columnTitle = 'time' const tableTitle = 'Test datetimeTime' @@ -27,9 +27,10 @@ test.describe('Test column ' + columnTitle, () => { await expect(page.locator('.custom-table table tr td div').filter({ hasText: '5:15' }).first()).toBeVisible() // delete row - await page.locator('.NcTable tr td button').first().click() - await page.locator('button').filter({ hasText: 'Delete' }).click() - await page.locator('button').filter({ hasText: /I really/ }).click({ force: true }) + await openRowActionMenu(page, page.locator('[data-cy="customTableRow"]').first()) + await page.locator('[data-cy="deleteRowBtn"]').click() + await page.locator('[data-cy="confirmDialog"]').getByRole('button', { name: 'Confirm' }).click() + await expect(page.locator('[data-cy="customTableRow"]')).toHaveCount(0, { timeout: 10000 }) await removeColumn(page, columnTitle) }) diff --git a/playwright/e2e/column-number.spec.ts b/playwright/e2e/column-number.spec.ts index 1b8fcf22f4..1168459d24 100644 --- a/playwright/e2e/column-number.spec.ts +++ b/playwright/e2e/column-number.spec.ts @@ -4,7 +4,7 @@ */ import { test, expect } from '../support/fixtures' -import { createNumberColumn, createTable, loadTable, removeColumn } from '../support/commands' +import { createNumberColumn, createTable, loadTable, openRowActionMenu, removeColumn } from '../support/commands' const columnTitle = 'num1' const tableTitle = 'Test number column' @@ -26,9 +26,10 @@ test.describe('Test column number', () => { await expect(page.locator('.custom-table table tr td div').filter({ hasText: '21.00' }).first()).toBeVisible() // delete row - await page.locator('.NcTable tr td button').first().click() - await page.locator('button').filter({ hasText: 'Delete' }).click() - await page.locator('button').filter({ hasText: /I really/ }).click({ force: true }) + await openRowActionMenu(page, page.locator('[data-cy="customTableRow"]').first()) + await page.locator('[data-cy="deleteRowBtn"]').click() + await page.locator('[data-cy="confirmDialog"]').getByRole('button', { name: 'Confirm' }).click() + await expect(page.locator('[data-cy="customTableRow"]')).toHaveCount(0, { timeout: 10000 }) // insert row with float value await page.locator('button').filter({ hasText: 'Create row' }).click() @@ -38,9 +39,10 @@ test.describe('Test column number', () => { await expect(page.locator('.custom-table table tr td div').filter({ hasText: '21.30' }).first()).toBeVisible() // delete row - await page.locator('.NcTable tr td button').first().click() - await page.locator('button').filter({ hasText: 'Delete' }).click() - await page.locator('button').filter({ hasText: /I really/ }).click({ force: true }) + await openRowActionMenu(page, page.locator('[data-cy="customTableRow"]').first()) + await page.locator('[data-cy="deleteRowBtn"]').click() + await page.locator('[data-cy="confirmDialog"]').getByRole('button', { name: 'Confirm' }).click() + await expect(page.locator('[data-cy="customTableRow"]')).toHaveCount(0, { timeout: 10000 }) await removeColumn(page, columnTitle) }) diff --git a/playwright/e2e/column-selection-multi.spec.ts b/playwright/e2e/column-selection-multi.spec.ts index e0aefa2e3b..559f2c1a35 100644 --- a/playwright/e2e/column-selection-multi.spec.ts +++ b/playwright/e2e/column-selection-multi.spec.ts @@ -4,7 +4,7 @@ */ import { test, expect } from '../support/fixtures' -import { createSelectionMultiColumn, createTable, loadTable, removeColumn } from '../support/commands' +import { createSelectionMultiColumn, createTable, loadTable, openRowActionMenu, removeColumn } from '../support/commands' const columnTitle = 'multi selection' const tableTitle = 'Test number column' @@ -42,15 +42,17 @@ test.describe('Test column ' + columnTitle, () => { await expect(page.locator('.custom-table table tr td .cell-multi-selection').filter({ hasText: 'third option' }).first()).toBeVisible() // delete first row - await page.locator('.NcTable tr td button').first().click() - await page.locator('button').filter({ hasText: 'Delete' }).click() - await page.locator('button').filter({ hasText: /I really/ }).click({ force: true }) + await openRowActionMenu(page, page.locator('[data-cy="customTableRow"]').first()) + await page.locator('[data-cy="deleteRowBtn"]').click() + await page.locator('[data-cy="confirmDialog"]').getByRole('button', { name: 'Confirm' }).click() + await expect(page.locator('[data-cy="customTableRow"]')).toHaveCount(1, { timeout: 10000 }) await expect(page.locator('.custom-table table tr td .cell-multi-selection', { hasText: 'first option' })).toBeHidden() await expect(page.locator('.custom-table table tr td .cell-multi-selection', { hasText: 'second option' })).toBeHidden() // edit second row (which is now first row) - await page.locator('.NcTable tr td button').first().click() + await openRowActionMenu(page, page.locator('[data-cy="customTableRow"]').first()) + await page.locator('[data-cy="editRowBtn"]').click() await page.locator('.modal__content .slot input').first().click() await page.locator('ul.vs__dropdown-menu li span[title="first option"]').first().click() await page.locator('.modal__content .title').first().click() @@ -60,9 +62,10 @@ test.describe('Test column ' + columnTitle, () => { await expect(page.locator('.custom-table table tr td .cell-multi-selection').filter({ hasText: 'third option' }).first()).toBeVisible() // delete first row - await page.locator('.NcTable tr td button').first().click() - await page.locator('button').filter({ hasText: 'Delete' }).click() - await page.locator('button').filter({ hasText: /I really/ }).click({ force: true }) + await openRowActionMenu(page, page.locator('[data-cy="customTableRow"]').first()) + await page.locator('[data-cy="deleteRowBtn"]').click() + await page.locator('[data-cy="confirmDialog"]').getByRole('button', { name: 'Confirm' }).click() + await expect(page.locator('[data-cy="customTableRow"]')).toHaveCount(0, { timeout: 10000 }) await removeColumn(page, columnTitle) }) @@ -79,6 +82,6 @@ test.describe('Test column ' + columnTitle, () => { await page.locator('button').filter({ hasText: 'Save' }).click() await expect(page.locator('.custom-table table tr td .cell-multi-selection').first()).toBeVisible() - await expect(page.locator('.NcTable tr td button').first()).toBeVisible() + await expect(page.locator('[data-cy="customTableRow"]').first()).toBeVisible() }) }) diff --git a/playwright/e2e/column-selection.spec.ts b/playwright/e2e/column-selection.spec.ts index 7382438360..88e73acd9d 100644 --- a/playwright/e2e/column-selection.spec.ts +++ b/playwright/e2e/column-selection.spec.ts @@ -4,7 +4,7 @@ */ import { test, expect } from '../support/fixtures' -import { createSelectionColumn, createTable, deleteTable, loadTable } from '../support/commands' +import { createSelectionColumn, createTable, deleteTable, loadTable, openRowActionMenu } from '../support/commands' const columnTitle = 'single selection' const tableTitle = 'Test number column' @@ -37,7 +37,9 @@ test.describe('Test column ' + columnTitle, () => { await expect(page.locator('[data-cy="ncTable"] tr td div').filter({ hasText: 'third option' }).first()).toBeVisible() // edit the explicitly created row - await page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]:has-text("👋 third option")').locator('[data-cy="editRowBtn"]').click() + const thirdOptionRow = page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').filter({ hasText: '👋 third option' }).first() + await openRowActionMenu(page, thirdOptionRow) + await page.locator('[data-cy="editRowBtn"]').click() await page.locator('[data-cy="editRowModal"] .slot input').first().click() await page.locator('ul.vs__dropdown-menu li span[title="first option"]').first().click() await page.locator('[data-cy="editRowSaveButton"]').click() @@ -59,6 +61,6 @@ test.describe('Test column ' + columnTitle, () => { await page.locator('[data-cy="createRowSaveButton"]').click() await expect(page.locator('[data-cy="ncTable"] tr td div').first()).toBeVisible() - await expect(page.locator('[data-cy="ncTable"] [data-cy="editRowBtn"]').first()).toBeVisible() + await expect(page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').first()).toBeVisible() }) }) diff --git a/playwright/e2e/column-text-link.spec.ts b/playwright/e2e/column-text-link.spec.ts index 7276deea89..ac93214cd4 100644 --- a/playwright/e2e/column-text-link.spec.ts +++ b/playwright/e2e/column-text-link.spec.ts @@ -8,7 +8,7 @@ import * as fs from 'fs' import * as path from 'path' import { fileURLToPath } from 'url' import { uploadFile } from '../support/api' -import { createTable, createTextLinkColumn, loadTable } from '../support/commands' +import { createTable, createTextLinkColumn, loadTable, openRowActionMenu } from '../support/commands' const __dirname = path.dirname(fileURLToPath(import.meta.url)) test.describe('Test column text-link', () => { @@ -44,7 +44,9 @@ test.describe('Test column text-link', () => { await expect(page.locator('tr td a').filter({ hasText: 'nextcloud' }).first()).toBeVisible() await expect(page.locator('tr td a').filter({ hasText: 'NC_server_test' }).first()).toBeVisible() - await page.locator('[data-cy="ncTable"] [data-cy="editRowBtn"]').first().click({ force: true }) + const firstRow = page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').first() + await openRowActionMenu(page, firstRow) + await page.locator('[data-cy="editRowBtn"]').click() const editDialog = page.getByRole('dialog', { name: 'Edit row' }) await editDialog.waitFor({ state: 'visible' }) diff --git a/playwright/e2e/column-usergroup.spec.ts b/playwright/e2e/column-usergroup.spec.ts index b614e86ad6..6dca024cd3 100644 --- a/playwright/e2e/column-usergroup.spec.ts +++ b/playwright/e2e/column-usergroup.spec.ts @@ -5,7 +5,7 @@ import { test, expect } from '../support/fixtures' import { createRandomUser } from '../support/api' -import { createTable, createUsergroupColumn, loadTable } from '../support/commands' +import { createTable, createUsergroupColumn, loadTable, openRowActionMenu } from '../support/commands' const columnTitle = 'usergroup' const tableTitlePrefix = 'Test usergroup' @@ -59,7 +59,9 @@ test.describe('Test column ' + columnTitle, () => { await page.locator('[data-cy="createRowSaveButton"]').click() await expect(page.locator('[data-cy="ncTable"] table tr td .user-bubble__name').filter({ hasText: user.userId }).first()).toBeVisible() - await page.locator('[data-cy="ncTable"] [data-cy="editRowBtn"]').first().click() + const firstRow = page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').first() + await openRowActionMenu(page, firstRow) + await page.locator('[data-cy="editRowBtn"]').click() // deselect all const deselectButtons = await page.locator('[data-cy="usergroupRowSelect"] .vs__deselect').all() for (const button of deselectButtons) { diff --git a/playwright/e2e/context.spec.ts b/playwright/e2e/context.spec.ts index 5b8e6f6200..c350a4ec42 100644 --- a/playwright/e2e/context.spec.ts +++ b/playwright/e2e/context.spec.ts @@ -15,6 +15,7 @@ import { loadContext, loadTable, openContextEditModal, + openRowActionMenu, } from '../support/commands' import { login } from '../support/login' @@ -300,9 +301,11 @@ test.describe('Manage a context', () => { await expect(page.locator('[data-cy="ncTable"] table').filter({ hasText: 'first row' })).toBeVisible() - await page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').filter({ hasText: 'first row' }).locator('[data-cy="editRowBtn"]').click() - await page.locator('[data-cy="editRowDeleteButton"]').click() - await page.locator('[data-cy="editRowDeleteConfirmButton"]').click() + const firstRow = page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').filter({ hasText: 'first row' }).first() + await openRowActionMenu(page, firstRow) + await page.locator('[data-cy="deleteRowBtn"]').click() + await page.locator('[data-cy="confirmDialog"]').getByRole('button', { name: 'Confirm' }).click() + await page.locator('[data-cy="confirmDialog"]').waitFor({ state: 'hidden', timeout: 10000 }).catch(() => {}) await expect(page.locator('[data-cy="ncTable"] table', { hasText: 'first row' })).toBeHidden() }) diff --git a/playwright/e2e/tables-rows.spec.ts b/playwright/e2e/tables-rows.spec.ts index 957518daf1..62fde00b72 100644 --- a/playwright/e2e/tables-rows.spec.ts +++ b/playwright/e2e/tables-rows.spec.ts @@ -8,7 +8,7 @@ import { test, expect } from '../support/fixtures' import type { BrowserContext, Page } from '@playwright/test' import { createRandomUser } from '../support/api' import { login } from '../support/login' -import { createTable, createTextLineColumn, fillInValueTextLine, loadTable, openCreateRowModal } from '../support/commands' +import { createTable, createTextLineColumn, fillInValueTextLine, loadTable, openCreateRowModal, openRowActionMenu } from '../support/commands' test.describe('Rows for a table', () => { test.describe.configure({ mode: 'serial' }) @@ -91,7 +91,9 @@ test.describe('Rows for a table', () => { ).toHaveCount(0, { timeout: 10000 }) // Delete - await page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').first().locator('[data-cy="editRowBtn"]').click() + const rowToDelete = page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').first() + await openRowActionMenu(page, rowToDelete) + await page.locator('[data-cy="editRowBtn"]').click() const deleteReqPromise = page.waitForResponse(r => r.url().includes('/apps/tables/row/') && r.request().method() === 'DELETE') await page.locator('[data-cy="editRowDeleteButton"]').click() @@ -129,7 +131,9 @@ test.describe('Rows for a table', () => { await page.locator('[data-cy="createRowSaveButton"]').click() await loadTable(page, 'to do list') - await page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]:has-text("My first task")').locator('[data-cy="editRowBtn"]').click() + const mandatoryRow = page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').filter({ hasText: 'My first task' }).first() + await openRowActionMenu(page, mandatoryRow) + await page.locator('[data-cy="editRowBtn"]').click() await expect(page.locator('[data-cy="editRowModal"] .notecard--error')).toBeHidden() await page.locator('[data-cy="editRowModal"] .slot input').first().clear() @@ -137,6 +141,57 @@ test.describe('Rows for a table', () => { await expect(page.locator('[data-cy="editRowSaveButton"]')).toBeDisabled() }) + test('Delete row via row action menu', async () => { + await page.goto('/index.php/apps/tables') + await createTable(page, 'Row delete menu table') + await createTextLineColumn(page, 'title', '', '', true) + + await openCreateRowModal(page) + await fillInValueTextLine(page, 'title', 'Row to delete') + await page.locator('[data-cy="createRowSaveButton"]').click() + await expect(page.locator('[data-cy="createRowModal"]')).toBeHidden() + + const rowToDelete = page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').filter({ hasText: 'Row to delete' }).first() + await expect(rowToDelete).toBeVisible({ timeout: 10000 }) + + const deleteReqPromise = page.waitForResponse(r => r.url().includes('/apps/tables/row/') && r.request().method() === 'DELETE') + await openRowActionMenu(page, rowToDelete) + await page.locator('[data-cy="deleteRowBtn"]').click() + await page.locator('[data-cy="confirmDialog"]').getByRole('button', { name: 'Confirm' }).click() + await deleteReqPromise + + await expect(page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]')).toHaveCount(0, { timeout: 10000 }) + }) + + test('Copy row via row action menu', async () => { + await page.goto('/index.php/apps/tables') + await createTable(page, 'Row copy menu table') + await createTextLineColumn(page, 'title', '', '', true) + + await openCreateRowModal(page) + await fillInValueTextLine(page, 'title', 'Original row') + await page.locator('[data-cy="createRowSaveButton"]').click() + await expect(page.locator('[data-cy="createRowModal"]')).toBeHidden() + + const originalRow = page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').filter({ hasText: 'Original row' }).first() + await expect(originalRow).toBeVisible({ timeout: 10000 }) + + // Open copy dialog via the row action menu + await openRowActionMenu(page, originalRow) + await page.locator('[data-cy="copyRowBtn"]').click() + + // Verify create modal opens with pre-filled value + await expect(page.locator('[data-cy="createRowModal"]')).toBeVisible({ timeout: 10000 }) + await expect(page.locator('[data-cy="createRowModal"] input').first()).toHaveValue('Original row') + + // Save the copy + await page.locator('[data-cy="createRowSaveButton"]').click() + await expect(page.locator('[data-cy="createRowModal"]')).toBeHidden() + + // Both original and copy should be visible + await expect(page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').filter({ hasText: 'Original row' })).toHaveCount(2, { timeout: 10000 }) + }) + test('Inline Edit', async () => { await page.goto('/index.php/apps/tables') // Create a test row first diff --git a/playwright/e2e/view-mandatory-state.spec.ts b/playwright/e2e/view-mandatory-state.spec.ts index 4da6521a5b..111db6ddc0 100644 --- a/playwright/e2e/view-mandatory-state.spec.ts +++ b/playwright/e2e/view-mandatory-state.spec.ts @@ -8,7 +8,7 @@ import { test, expect } from '../support/fixtures' import type { BrowserContext, Page } from '@playwright/test' import { createRandomUser } from '../support/api' import { login } from '../support/login' -import { createTable, createTextLineColumn, fillInValueTextLine, loadTable } from '../support/commands' +import { createTable, createTextLineColumn, fillInValueTextLine, loadTable, openRowActionMenu } from '../support/commands' const tableTitle = 'Mandatory test table' @@ -131,7 +131,9 @@ test.describe('Mandatory Column Functionality', () => { await expect(page.locator('.icon-loading').first()).toBeHidden() // Now open edit row dialog - await page.locator('[data-cy="editRowBtn"]').first().click() + const firstRow = page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').first() + await openRowActionMenu(page, firstRow) + await page.locator('[data-cy="editRowBtn"]').click() await expect(page.locator('[data-cy="editRowModal"]')).toBeVisible() // should show error when mandatory field is empty diff --git a/playwright/e2e/view.spec.ts b/playwright/e2e/view.spec.ts index 4f9e8d64b3..05ff6b104a 100644 --- a/playwright/e2e/view.spec.ts +++ b/playwright/e2e/view.spec.ts @@ -8,7 +8,7 @@ import { test, expect } from '../support/fixtures' import type { BrowserContext, Page } from '@playwright/test' import { createRandomUser } from '../support/api' import { login } from '../support/login' -import { createSelectionColumn, createTable, createTextLineColumn, fillInValueSelection, fillInValueTextLine, loadTable } from '../support/commands' +import { createSelectionColumn, createTable, createTextLineColumn, fillInValueSelection, fillInValueTextLine, loadTable, openRowActionMenu } from '../support/commands' const firstTitle = 'Test view' const secondTitle = 'Test view 2' @@ -192,14 +192,14 @@ test.describe('Interact with views', () => { await expect(page.locator('.icon-loading').first()).toBeHidden() // Delete the first row - await page.locator('[data-cy="customTableRow"]').first().locator('[data-cy="editRowBtn"]').click() - await page.locator('[data-cy="editRowModal"] [data-cy="editRowDeleteButton"]').click() - await page.locator('[data-cy="editRowModal"] [data-cy="editRowDeleteConfirmButton"]').click() - - await expect(page.locator('[data-cy="editRowModal"]')).toBeHidden() - - // Verify one row was deleted by checking the count decreased - const count = await page.locator('[data-cy="customTableRow"]').count() - expect(count).toBeLessThan(4) + const rowCountBefore = await page.locator('[data-cy="customTableRow"]').count() + const firstRow = page.locator('[data-cy="customTableRow"]').first() + await openRowActionMenu(page, firstRow) + await page.locator('[data-cy="deleteRowBtn"]').click() + await page.locator('[data-cy="confirmDialog"]').getByRole('button', { name: 'Confirm' }).click() + await page.locator('[data-cy="confirmDialog"]').waitFor({ state: 'hidden', timeout: 10000 }).catch(() => {}) + + // Verify one row was deleted — toHaveCount retries until the DOM settles + await expect(page.locator('[data-cy="customTableRow"]')).toHaveCount(rowCountBefore - 1, { timeout: 10000 }) }) }) diff --git a/playwright/support/commands.ts b/playwright/support/commands.ts index da6c499819..97b362e7e4 100644 --- a/playwright/support/commands.ts +++ b/playwright/support/commands.ts @@ -202,30 +202,25 @@ export async function deleteTable(page: Page, title: string) { ).not.toBeVisible() } +export async function openRowActionMenu(page: Page, rowLocator: Locator) { + await rowLocator.hover() + await rowLocator.locator('[data-cy="rowActionMenu"] button').click() +} + export async function deleteRow(page: Page, rowIndex: number) { const deleteResponse = page.waitForResponse( (response) => /\/apps\/tables\/(row|view\/\d+\/row)\//.test(response.url()) && response.request().method() === 'DELETE', ).catch(() => null) - await page - .locator('[data-cy="ncTable"] [data-cy="editRowBtn"]') - .nth(rowIndex) - .click() - await page.locator('[data-cy="editRowDeleteButton"]').click({ force: true }) - const confirmDeleteButton = page.locator('[data-cy="editRowDeleteConfirmButton"]') - if (await confirmDeleteButton.isVisible().catch(() => false)) { - await confirmDeleteButton.click({ force: true }) - } + const row = page.locator('[data-cy="ncTable"] [data-cy="customTableRow"]').nth(rowIndex) + await openRowActionMenu(page, row) + const deleteBtn = page.locator('[data-cy="deleteRowBtn"]') + await deleteBtn.waitFor({ state: 'visible', timeout: 5000 }) + await deleteBtn.click() + await page.locator('[data-cy="confirmDialog"]').getByRole('button', { name: 'Confirm' }).click() await deleteResponse - await page - .locator('[data-cy="editRowModal"]') - .waitFor({ state: 'hidden', timeout: 10000 }) - .catch(async () => { - if (await page.locator('[data-cy="editRowModal"]').isVisible().catch(() => false)) { - await page.locator('[data-cy="editRowModal"] button[aria-label="Close"]').click().catch(() => {}) - } - }) + await page.locator('[data-cy="confirmDialog"]').waitFor({ state: 'hidden', timeout: 10000 }).catch(() => {}) } export async function createView(page: Page, title: string) { diff --git a/src/modules/main/partials/TableView.vue b/src/modules/main/partials/TableView.vue index 741f1cebc7..1b5ecee6a4 100644 --- a/src/modules/main/partials/TableView.vue +++ b/src/modules/main/partials/TableView.vue @@ -24,6 +24,8 @@ @delete-column="deleteColumn" @create-row="createRow" @edit-row="editRow" + @copy-row="copyRow" + @delete-row="deleteRow" @delete-selected-rows="deleteSelectedRows">