From 2a3a2a597ea76c5175d5da76a148bb22ce12bc9a Mon Sep 17 00:00:00 2001 From: Jordi Enric <37541088+jordienr@users.noreply.github.com> Date: Fri, 14 Feb 2025 08:06:16 +0100 Subject: [PATCH] Add E2E tests for local studio logs (#33592) * add e2e tests for local studio logs * fix auth schema test * comment out log pages without logs * fix tests rm react query from dom, it blocks view of definition button * undo auth table changes --- .../DefaultPreviewSelectionRenderer.tsx | 21 ++++- tests/local-studio-tests/playwright.config.ts | 7 +- .../tests/snapshot/spec/logs.spec.ts | 81 +++++++++++++++++++ .../tests/snapshot/spec/table-editor.spec.ts | 22 ++++- 4 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 tests/local-studio-tests/tests/snapshot/spec/logs.spec.ts diff --git a/apps/studio/components/interfaces/Settings/Logs/LogSelectionRenderers/DefaultPreviewSelectionRenderer.tsx b/apps/studio/components/interfaces/Settings/Logs/LogSelectionRenderers/DefaultPreviewSelectionRenderer.tsx index d4d8792c224..08c5a7f281b 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogSelectionRenderers/DefaultPreviewSelectionRenderer.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogSelectionRenderers/DefaultPreviewSelectionRenderer.tsx @@ -16,7 +16,15 @@ import { useState, useEffect } from 'react' const LogRowSeparator = () => -const PropertyRow = ({ keyName, value }: { keyName: string; value: any }) => { +const PropertyRow = ({ + keyName, + value, + dataTestId, +}: { + keyName: string + value: any + dataTestId?: string +}) => { const isTimestamp = keyName === 'timestamp' || keyName === 'created_at' || keyName === 'updated_at' @@ -83,7 +91,7 @@ const PropertyRow = ({ keyName, value }: { keyName: string; value: any }) => { return ( - +
{ const { timestamp, event_message, metadata, id, ...rest } = log return ( -
+
{log?.id && } {log?.timestamp && ( - + )} {log?.event_message && ( diff --git a/tests/local-studio-tests/playwright.config.ts b/tests/local-studio-tests/playwright.config.ts index 2568a2cdcee..b3a260763ef 100644 --- a/tests/local-studio-tests/playwright.config.ts +++ b/tests/local-studio-tests/playwright.config.ts @@ -30,13 +30,18 @@ export default defineConfig({ trace: 'on-first-retry', // record a video for failed tests, but only for local testing. We can't store videos on CI atm. video: 'retain-on-failure', + launchOptions: { + env: { + NODE_ENV: 'test', + }, + }, }, /* Configure projects for major browsers */ projects: [ { name: 'chromium', - use: { ...devices['Desktop Chrome'] }, + use: { ...devices['Desktop Chrome'], viewport: { width: 1366, height: 768 } }, }, // { diff --git a/tests/local-studio-tests/tests/snapshot/spec/logs.spec.ts b/tests/local-studio-tests/tests/snapshot/spec/logs.spec.ts new file mode 100644 index 00000000000..ab7c8f5b37b --- /dev/null +++ b/tests/local-studio-tests/tests/snapshot/spec/logs.spec.ts @@ -0,0 +1,81 @@ +import { test, expect } from '@playwright/test' + +const LOGS_PAGES = [ + 'API Gateway', + 'Postgres', + 'PostgREST', + // 'Auth', Wont have logs on first load + // 'Storage', Wont have logs on first load + //'Realtime' Wont have logs on first load +] + +test.describe('Logs', async () => { + for (const logPage of LOGS_PAGES) { + test.describe(`${logPage} logs page`, () => { + test('can navigate to logs page', async ({ page }) => { + await page.goto('http://localhost:8082/project/default') + await page.locator('a', { hasText: 'Logs' }).click({ timeout: 4000 }) + await expect(page.getByText('Logs & Analytics')).toBeVisible() + + // Click anywhere on the screen to close the sidebar + await page.click('body') + + await page + .getByRole('link', { name: logPage, exact: true }) + .click() + .catch((e) => { + console.log('🔴 Error clicking', logPage, e) + throw e + }) + + // Wait for and verify the logs table is present + const logsTable = page.getByRole('table') + await expect(logsTable).toBeVisible() + }) + + test('shows logs data without errors', async ({ page }) => { + // Navigate to page first + await page.goto('http://localhost:8082/project/default') + await page.locator('a', { hasText: 'Logs' }).click({ timeout: 4000 }) + await page.click('body') + await page.getByRole('link', { name: logPage, exact: true }).click() + + // Wait a bit and check for errors with a longer timeout + const error = page.getByText('Error fetching logs') + await expect(error).not.toBeVisible({ timeout: 10000 }) + + // Check if the logs table has any rows + const gridcells = page.getByRole('gridcell') + await expect(gridcells.first()).toBeVisible() + }) + + test('can select and view log details', async ({ page }) => { + // Navigate to page first + await page.goto('http://localhost:8082/project/default') + await page.locator('a', { hasText: 'Logs' }).click({ timeout: 4000 }) + await page.click('body') + await page.getByRole('link', { name: logPage, exact: true }).click() + + const gridcells = page.getByRole('gridcell') + + // Click first row and verify details + await gridcells.first().click() + const tabPanel = page.getByTestId('log-selection') + await expect(tabPanel).toBeVisible({ timeout: 2000 }) + + const selectionPanelTimestamp = tabPanel.getByTestId('log-selection-timestamp') + await expect(selectionPanelTimestamp).toBeVisible() + + const rawTimestamp = await selectionPanelTimestamp.textContent() + const timestamp = rawTimestamp?.replace('timestamp', '') + const rowText = await gridcells.first().textContent() + expect(rowText).toContain(timestamp) + + // Click second row and verify different content + await gridcells.nth(1).click() + const tabPanelText2 = await tabPanel.textContent() + expect(tabPanelText2).not.toBe(rowText) + }) + }) + } +}) diff --git a/tests/local-studio-tests/tests/snapshot/spec/table-editor.spec.ts b/tests/local-studio-tests/tests/snapshot/spec/table-editor.spec.ts index 6323716141f..a689cdcbf90 100644 --- a/tests/local-studio-tests/tests/snapshot/spec/table-editor.spec.ts +++ b/tests/local-studio-tests/tests/snapshot/spec/table-editor.spec.ts @@ -19,7 +19,7 @@ test.describe('Table Editor page', () => { test('should create a new table, view its definition, add new rows, sort and filter', async ({ page, }, testInfo) => { - const tableName = `${kebabCase(testInfo.title).slice(0, 24)}-${testInfo.retry}-${Math.floor(Math.random() * 100)}` + const tableName = `${kebabCase(testInfo.title).slice(0, 24)}-${testInfo.retry}-${Math.floor(Math.random() * 10000)}` // The page has been loaded with the table data, we can now interact with the page await page.getByRole('button', { name: 'New table', exact: true }).click() @@ -46,6 +46,14 @@ test.describe('Table Editor page', () => { await page.getByRole('button', { name: 'Save' }).click() await dismissToast(page) + // hide React Query DevTools if present + await page.evaluate(() => { + const devtools = document.querySelector('.ReactQueryDevtools') + if (devtools) { + devtools.remove() + } + }) + // view its definition await page.getByText('definition').click() await expect(page.locator('div.view-lines')).toContainText( @@ -69,9 +77,17 @@ test.describe('Table Editor page', () => { await page.getByTestId('action-bar-save-row').click() await dismissToast(page) + // Wait for both rows to be visible in the grid + await page.waitForResponse((response) => + response.url().includes('/api/platform/pg-meta/default/query') + ) await expect(page.getByRole('grid')).toContainText('2') await expect(page.getByRole('grid')).toContainText('100') + // Make sure we can see both rows in the grid before sorting + const rows = page.getByRole('row') + await expect(rows).toHaveCount(3) // header row + 2 data rows + // sort by the a column await page.getByRole('button', { name: 'Sort' }).click() await page.getByTestId('table-editor-pick-column-to-sort-button').click() @@ -82,8 +98,8 @@ test.describe('Table Editor page', () => { await page.locator('#spec-click-target').click() // expect the row to be sorted by defaultValueColumn. They're inserted in the order 100, 2 - await expect(page.locator('div.rdg-row:nth-child(2)')).toContainText('2') - await expect(page.locator('div.rdg-row:nth-child(3)')).toContainText('100') + await expect(rows.nth(1)).toContainText('2') + await expect(rows.nth(2)).toContainText('100') // remove the sorting await page.getByRole('button', { name: 'Sorted by 1 rule' }).click() await page.getByRole('dialog').getByRole('button').nth(1).click()