Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: E2E Tests

on:
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * *'
workflow_dispatch:

jobs:
e2e:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Install Playwright browsers
run: pnpm exec playwright install --with-deps

- name: Start docker-compose stack
run: docker compose -f docker-compose.e2e.yml up -d

- name: Wait for dashboard
run: |
for i in {1..30}; do
if curl -fsS http://localhost:5173/dashboard/query > /dev/null; then
echo "✅ Dashboard ready"
exit 0
fi
echo "Waiting for dashboard (${i}/30)..."
sleep 5
done
exit 1

- name: Run Playwright tests
run: pnpm exec playwright test tests/e2e/query.real.spec.ts

- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-artifacts
path: |
playwright-report
e2e-screenshots
if-no-files-found: ignore

- name: Cleanup
if: always()
run: docker compose -f docker-compose.e2e.yml down -v
38 changes: 38 additions & 0 deletions docker-compose.e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
version: "3.8"

services:
greptimedb:
image: greptime/greptimedb:latest
command: "standalone start --http-addr=0.0.0.0:4000"
ports:
- "4000:4000"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
interval: 10s
timeout: 5s
retries: 12
networks:
- e2e-net

dashboard:
build:
context: .
dockerfile: docker/Dockerfile.e2e
environment:
VITE_API_BASE: "http://greptimedb:4000"
depends_on:
greptimedb:
condition: service_healthy
ports:
- "5173:5173"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5173/dashboard/query"]
interval: 10s
timeout: 5s
retries: 12
networks:
- e2e-net

networks:
e2e-net:
driver: bridge
12 changes: 12 additions & 0 deletions docker/Dockerfile.e2e
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
RUN corepack enable && pnpm install --frozen-lockfile
RUN pnpm build

FROM node:20-alpine
WORKDIR /app
RUN npm install -g serve
COPY --from=builder /app/dist ./dist
EXPOSE 5173
CMD ["serve", "-s", "dist", "-l", "5173"]
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"@babel/types": "^7.21.4",
"@commitlint/cli": "^17.4.2",
"@commitlint/config-conventional": "^17.6.5",
"@playwright/test": "^1.56.1",
"@prettier/plugin-pug": "^2.5.1",
"@tauri-apps/cli": "^2.2.7",
"@types/lodash": "^4.14.191",
Expand Down
38 changes: 38 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 13 additions & 11 deletions src/views/dashboard/query/editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ a-card.editor-card(:bordered="false")
a-dropdown-button(
type="primary"
position="bl"
data-test="run-query"
:disabled="isButtonDisabled || explainQueryRunning"
@click="runPartQuery()"
)
Expand Down Expand Up @@ -115,17 +116,18 @@ a-card.editor-card(:bordered="false")
a-resize-box(:directions="['bottom']")
a-tabs.query-tabs(:default-active-key="'sql'" :active-key="queryType")
a-tab-pane(key="sql")
CodeMirror(
v-model="codes.sql"
:style="style"
:spellcheck="spellcheck"
:autofocus="autofocus"
:indent-with-tab="indentWithTab"
:tabSize="tabSize"
:extensions="extensionsForSql"
@ready="handleReadySql"
@update="codeUpdate('sql')"
)
div(data-test="sql-input")
CodeMirror(
v-model="codes.sql"
:style="style"
:spellcheck="spellcheck"
:autofocus="autofocus"
:indent-with-tab="indentWithTab"
:tabSize="tabSize"
:extensions="extensionsForSql"
@ready="handleReadySql"
@update="codeUpdate('sql')"
)
a-tab-pane(key="promql")
CodeMirror(
v-model="codes.promql"
Expand Down
1 change: 1 addition & 0 deletions src/views/dashboard/query/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ a-layout.new-layout
DataView(
v-if="!!results?.length || !!explainResult"
ref="dataViewRef"
data-test="query-result"
:results="results"
:types="types"
:explainResult="explainResult"
Expand Down
50 changes: 50 additions & 0 deletions tests/e2e/query.real.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { expect, test } from '@playwright/test'
import { mkdir } from 'fs/promises'
import path from 'path'

const SCREENSHOT_PATH = path.join(process.cwd(), 'e2e-screenshots', 'query-fail.png')
const SQL_STATEMENT = 'SELECT 1;'

test.use({
baseURL: 'http://localhost:5173',
testIdAttribute: 'data-test',
})

test.setTimeout(15_000)

test.describe('Query Page (real backend)', () => {
test('executes SQL against live backend and renders results', async ({ page }) => {
await page.addInitScript(() => {
window.localStorage.setItem('tourStatus', JSON.stringify({ navbar: true }))
})

try {
await test.step('Navigate to query page', async () => {
await page.goto('/dashboard/query')
await page.waitForLoadState('networkidle')
})

await test.step('Input SQL statement', async () => {
const sqlInput = page.getByTestId('sql-input').locator('.cm-content')
await sqlInput.click()
await sqlInput.pressSequentially(SQL_STATEMENT)
})

await test.step('Run query', async () => {
await page.getByTestId('run-query').locator('button').first().click()
})

const resultArea = page.getByTestId('query-result')
await expect(resultArea).toContainText('1', { timeout: 15_000 })

const resultText = (await resultArea.textContent())?.trim() ?? ''
console.log('SQL executed:', SQL_STATEMENT)
console.log('Result text:', resultText)
} catch (error) {
console.error('Query test failed:', error)
await mkdir(path.dirname(SCREENSHOT_PATH), { recursive: true })
await page.screenshot({ path: SCREENSHOT_PATH, fullPage: true })
throw error
}
})
})
Loading