diff --git a/e2e/studio/features/database.spec.ts b/e2e/studio/features/database.spec.ts index 4e69ef126e7..423b9d199be 100644 --- a/e2e/studio/features/database.spec.ts +++ b/e2e/studio/features/database.spec.ts @@ -62,7 +62,7 @@ const deleteTable = async (page: Page, tableName: string) => { await expect( page.getByText(`Successfully deleted table "${tableName}"`), 'Delete confirmation toast should be visible' - ).toBeVisible() + ).toBeVisible({ timeout: 50000 }) } test.describe.serial('Database', () => { @@ -164,8 +164,14 @@ test.describe.serial('Database', () => { // change schema -> auth await page.getByTestId('schema-selector').click() await page.getByPlaceholder('Find schema...').fill('auth') + const authSchemaWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'tables?include_columns=true&included_schemas=auth' + ) await page.getByRole('option', { name: 'auth' }).click() - await waitForDatabaseToLoad(page, ref, 'auth') + await authSchemaWait await expect(page.getByText('sso_providers')).toBeVisible() // check new table button is not present in other schemas await expect(page.getByRole('button', { name: 'New table' })).not.toBeVisible() @@ -192,8 +198,14 @@ test.describe.serial('Database', () => { .click() await page.getByRole('menuitem', { name: 'Delete table' }).click() await page.getByRole('checkbox', { name: 'Drop table with cascade?' }).check() + const cleanupNewWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=table-delete' + ) await page.getByRole('button', { name: 'Delete' }).click() - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-delete') + await cleanupNewWait } if ((await page.getByText(databaseTableNameUpdated, { exact: true }).count()) > 0) { @@ -204,8 +216,14 @@ test.describe.serial('Database', () => { .click() await page.getByRole('menuitem', { name: 'Delete table' }).click() await page.getByRole('checkbox', { name: 'Drop table with cascade?' }).check() + const cleanupUpdatedWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=table-delete' + ) await page.getByRole('button', { name: 'Delete' }).click() - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-delete') + await cleanupUpdatedWait } if ((await page.getByText(databaseTableNameDuplicate, { exact: true }).count()) > 0) { @@ -216,17 +234,29 @@ test.describe.serial('Database', () => { .click() await page.getByRole('menuitem', { name: 'Delete table' }).click() await page.getByRole('checkbox', { name: 'Drop table with cascade?' }).check() + const cleanupDuplicateWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=table-delete' + ) await page.getByRole('button', { name: 'Delete' }).click() - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-delete') + await cleanupDuplicateWait } // create a new table await page.getByRole('button', { name: 'New table' }).click() await page.getByTestId('table-name-input').fill(databaseTableNameNew) + const createTableWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=table-create' + ) await page.getByRole('button', { name: 'Save' }).click() // validate table creation - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-create') + await createTableWait await waitForDatabaseToLoad(page, ref) await expect(page.getByText(databaseTableNameNew, { exact: true })).toBeVisible() @@ -234,10 +264,16 @@ test.describe.serial('Database', () => { await page.getByRole('row', { name: databaseTableNameNew }).getByRole('button').last().click() await page.getByRole('menuitem', { name: 'Edit table' }).click() await page.getByTestId('table-name-input').fill(databaseTableNameUpdated) + const updateTableWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=table-update' + ) await page.getByRole('button', { name: 'Save' }).click() // validate table update - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-update') + await updateTableWait await waitForDatabaseToLoad(page, ref) await expect(page.getByText(databaseTableNameUpdated, { exact: true })).toBeVisible() @@ -250,10 +286,11 @@ test.describe.serial('Database', () => { await page.getByRole('menuitem', { name: 'Duplicate Table' }).click() await page.getByTestId('table-name-input').fill(databaseTableNameDuplicate) await page.getByRole('textbox', { name: 'Optional' }).fill('') + const duplicateTableWait = createApiResponseWaiter(page, 'pg-meta', ref, 'query?key=') await page.getByRole('button', { name: 'Save' }).click() // validate table duplicate - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=') + await duplicateTableWait await waitForDatabaseToLoad(page, ref) await expect(page.getByText(databaseTableNameDuplicate, { exact: true })).toBeVisible() @@ -265,8 +302,14 @@ test.describe.serial('Database', () => { .click() await page.getByRole('menuitem', { name: 'Delete table' }).click() await page.getByRole('checkbox', { name: 'Drop table with cascade?' }).check() + const deleteDuplicateWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=table-delete' + ) await page.getByRole('button', { name: 'Delete' }).click() - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-delete') + await deleteDuplicateWait await page .getByRole('row', { name: `${databaseTableNameUpdated}` }) @@ -275,8 +318,14 @@ test.describe.serial('Database', () => { .click() await page.getByRole('menuitem', { name: 'Delete table' }).click() await page.getByRole('checkbox', { name: 'Drop table with cascade?' }).check() + const deleteUpdatedWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=table-delete' + ) await page.getByRole('button', { name: 'Delete' }).click() - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-delete') + await deleteUpdatedWait // validate navigating to table editor from database table page await page.getByRole('row', { name: databaseTableName }).getByRole('button').last().click() @@ -310,11 +359,23 @@ test.describe.serial('Database', () => { .fill('pw_database_column_2') await page.getByText('Choose a column type...').click() await page.getByText('numeric', { exact: true }).click() + const columnCreateWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=column-create' + ) + const columnCreateRefreshWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=table-editor-' + ) await page.getByRole('button', { name: 'Save' }).click() // wait for response + validate - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=column-create') - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-editor-') + await columnCreateWait + await columnCreateRefreshWait const columnDatabase2Row = page.getByRole('row', { name: databaseColumnName2 }) await expect(columnDatabase2Row).toContainText(databaseColumnName2) await expect(columnDatabase2Row).toContainText('numeric') @@ -323,22 +384,46 @@ test.describe.serial('Database', () => { await columnDatabase2Row.getByRole('button').click() await page.getByRole('button', { name: 'Edit column' }).click() await page.getByRole('textbox', { name: 'column_name' }).fill(databaseColumnName3) + const columnUpdateWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=column-update' + ) + const columnUpdateRefreshWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=table-editor-' + ) await page.getByRole('button', { name: 'Save' }).click() // wait for response + validate - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=column-update') - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-editor-') + await columnUpdateWait + await columnUpdateRefreshWait // delete table column const columnDatabase3Row = page.getByRole('row', { name: databaseColumnName3 }) await columnDatabase3Row.getByRole('button').click() await page.getByRole('button', { name: 'Delete column' }).click() await page.getByRole('checkbox', { name: 'Drop column with cascade?' }).check() + const columnDeleteWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=column-delete' + ) + const columnDeleteRefreshWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=table-editor-' + ) await page.getByRole('button', { name: 'Delete' }).click() // wait for response + validate - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=column-delete') - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=table-editor-') + await columnDeleteWait + await columnDeleteRefreshWait await expect( page.getByText(`Successfully deleted column "${databaseColumnName3}"`), 'Delete confirmation toast should be visible' @@ -394,7 +479,7 @@ test.describe.serial('Database', () => { ).toBeVisible({ timeout: 50000 }) } - // create new index + // create new trigger await page.getByRole('button', { name: 'New trigger' }).click() await page.getByRole('textbox', { name: 'Name of trigger' }).fill(databaseTriggerName) await page.getByRole('combobox').first().click() @@ -404,10 +489,16 @@ test.describe.serial('Database', () => { await page.getByRole('checkbox').nth(2).click() await page.getByRole('button', { name: 'Choose a function to trigger' }).click() await page.getByRole('paragraph').filter({ hasText: 'subscription_check_filters' }).click() + const triggerCreateWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=trigger-create' + ) await page.getByRole('button', { name: /^(Create|Save) trigger$/ }).click() // validate trigger creation - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=trigger-create') + await triggerCreateWait await expect( page.getByText(`Successfully created trigger`), 'Trigger creation confirmation toast should be visible' @@ -422,10 +513,16 @@ test.describe.serial('Database', () => { await triggerRow.getByRole('button', { name: 'More options' }).click() await page.getByRole('menuitem', { name: 'Edit trigger' }).click() await page.getByRole('textbox', { name: 'Name of trigger' }).fill(databaseTriggerNameUpdated) + const triggerUpdateWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=trigger-update' + ) await page.getByRole('button', { name: /^(Create|Save) trigger$/ }).click() // validate trigger update - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=trigger-update') + await triggerUpdateWait await expect( page.getByText(`Successfully updated trigger`), 'Trigger updated confirmation toast should be visible' @@ -537,8 +634,9 @@ test.describe.serial('Database', () => { // delete the index await newIndexRow.getByRole('button', { name: 'Delete index' }).click() + const indexDeleteWait = createApiResponseWaiter(page, 'pg-meta', ref, 'query?key=indexes') await page.getByRole('button', { name: 'Confirm delete' }).click() - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=indexes') + await indexDeleteWait await expect( page.getByText('Successfully deleted index'), 'Index deletion confirmation toast should be visible' @@ -597,8 +695,9 @@ test.describe.serial('Database', () => { // delete a role await page.getByRole('button', { name: databaseRoleName }).getByRole('button').click() await page.getByRole('menuitem', { name: 'Delete' }).click() + const roleDeleteWait = createApiResponseWaiter(page, 'pg-meta', ref, 'query?key=roles-delete') await page.getByRole('button', { name: 'Confirm' }).click() - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=roles-delete') + await roleDeleteWait await expect( page.getByText(`Successfully deleted role: ${databaseRoleName}`), 'Delete confirmation toast should be visible' @@ -663,10 +762,11 @@ test.describe.serial('Database Enumerated Types', () => { await page.locator('input[name="values.0.value"]').fill(databaseEnumValue1Name) await page.getByRole('button', { name: 'Add value' }).click() await page.locator('input[name="values.1.value"]').fill(databaseEnumValue2Name) + const enumCreateWait = createApiResponseWaiter(page, 'pg-meta', ref, 'types') await page.getByRole('button', { name: 'Create type' }).click() // Wait for enum response to be completed and validate it - await waitForApiResponse(page, 'pg-meta', ref, 'types') + await enumCreateWait const enumRow = page.getByRole('row', { name: `${databaseEnumName}` }) await expect(enumRow).toContainText(databaseEnumName) await expect(enumRow).toContainText(`${databaseEnumValue1Name}, ${databaseEnumValue2Name}`) @@ -753,11 +853,23 @@ test.describe.serial('Database Functions', () => { END;`) await page.waitForTimeout(500) // wait for text content to be visible expect(await page.getByRole('presentation').textContent()).toBe(`BEGINEND;`) + const functionCreateWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=functions-create' + ) + const functionCreateRefreshWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=database-functions' + ) await page.getByRole('button', { name: 'Create function' }).click() // validate function creation - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=functions-create') - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=database-functions') + await functionCreateWait + await functionCreateRefreshWait await expect( page.getByText(`Successfully created function`), 'Trigger creation confirmation toast should be visible' @@ -771,10 +883,16 @@ END;`) await functionRow.getByRole('button', { name: 'More options' }).click() await page.getByRole('menuitem', { name: 'Edit function', exact: true }).click() await page.getByRole('textbox', { name: 'Name of function' }).fill(databaseFunctionNameUpdated) + const functionUpdateWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=functions-update' + ) await page.getByRole('button', { name: 'Save function' }).click() // validate function update - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=functions-update') + await functionUpdateWait await expect( page.getByText(`Successfully updated function ${databaseFunctionNameUpdated}`), 'Function updated confirmation toast should be visible' @@ -788,10 +906,16 @@ END;`) await updatedFunctionRow.getByRole('button', { name: 'More options' }).click() await page.getByRole('menuitem', { name: 'Delete function' }).click() await page.getByPlaceholder('Type in name of function').fill(databaseFunctionNameUpdated) + const functionDeleteWait = createApiResponseWaiter( + page, + 'pg-meta', + ref, + 'query?key=functions-delete' + ) await page .getByRole('button', { name: `Delete function ${databaseFunctionNameUpdated}` }) .click() - await waitForApiResponse(page, 'pg-meta', ref, 'query?key=functions-delete') + await functionDeleteWait await expect( page.getByText(`Successfully removed function ${databaseFunctionNameUpdated}`), 'Delete confirmation toast should be visible' diff --git a/e2e/studio/utils/wait-for-response.ts b/e2e/studio/utils/wait-for-response.ts index cade0849525..c40feb5566c 100644 --- a/e2e/studio/utils/wait-for-response.ts +++ b/e2e/studio/utils/wait-for-response.ts @@ -61,7 +61,7 @@ export function createApiResponseWaiter( const matcher = buildUrlMatcher(basePath, ref, action, options?.method) return page - .waitForResponse(matcher, { timeout: options?.timeout }) + .waitForResponse(matcher, { timeout: options?.timeout ?? 30_000 }) .then(() => {}) .catch((error) => { const trimmedBasePath = basePath.replace(/^\/+|\/+$/g, '')