Skip to content

Commit

Permalink
Merge pull request #12 from metabase/add-query-builder-viewer-and-fix…
Browse files Browse the repository at this point in the history
…-bugs

Add query builder viewer and fix bugs
  • Loading branch information
filiphric authored Dec 16, 2024
2 parents f3619cb + 4922613 commit d4b57c3
Show file tree
Hide file tree
Showing 31 changed files with 2,625 additions and 301 deletions.
70 changes: 70 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: CI

on: ['pull_request']

jobs:
test:
name: 🧪 Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: 🔧 Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'

- name: 📦 Install dependencies
run: yarn install --frozen-lockfile

- name: 🧪 Run tests
run: yarn test:ci

- name: 📊 Upload test results
if: always()
uses: actions/upload-artifact@v3
with:
name: test-results
path: |
junit.xml
coverage/
lint:
name: 🔍 Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: 🔧 Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'

- name: 📦 Install dependencies
run: yarn install --frozen-lockfile

- name: 🔍 Run ESLint
run: yarn lint

- name: 💅 Check formatting
run: yarn format:check

typecheck:
name: ʦ Type check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: 🔧 Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'

- name: 📦 Install dependencies
run: yarn install --frozen-lockfile

- name: ʦ Type check
run: yarn tsc --noEmit
17 changes: 15 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
"lint:fix": "next lint --fix",
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"",
"fix": "npm run format && npm run lint:fix"
"fix": "npm run format && npm run lint:fix",
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"test:ci": "vitest run --coverage --reporter=default --reporter=junit --outputFile=./junit.xml"
},
"dependencies": {
"@radix-ui/react-collapsible": "^1.1.1",
Expand All @@ -32,18 +36,27 @@
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^14.5.2",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"@vitejs/plugin-react": "^4.3.4",
"@vitest/coverage-v8": "^2.1.8",
"@vitest/ui": "^2.1.8",
"eslint": "^8.56.0",
"eslint-config-next": "14.0.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"jsdom": "^25.0.1",
"postcss": "^8",
"prettier": "^3.2.4",
"tailwindcss": "^3.4.1",
"typescript": "^5"
"typescript": "^5",
"vitest": "^2.1.8"
}
}
4 changes: 2 additions & 2 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import Image from 'next/image'
import { useState, useMemo } from 'react'

import DevToolsUI from '@/components/DevToolsUi'
import UploadDropzone from '@/components/UploadDropzone'
import { DevToolsUI } from '@/components/DevToolsUi'
import { UploadDropzone } from '@/components/UploadDropzone'
import { DiagnosticData } from '@/types/DiagnosticData'

export default function Home({
Expand Down
91 changes: 91 additions & 0 deletions src/components/ConsoleOutput.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { describe, it, expect } from 'vitest'

import { ConsoleOutput } from './ConsoleOutput'
import { fireEvent, render, screen } from '../test/test-utils'

const sampleErrors = [
'"[webpack-dev-server] ERROR in ./components/ErrorPages/utils.ts\\n × Module not found: Error message 1"',
'"Warning: Something went wrong\\nStack trace for warning"',
'"[webpack-dev-server] Another error occurred\\nStack trace for error"',
'{"level": "info", "message": "Server started successfully", "stack": "Info stack trace"}',
'"Regular log message without specific level"',
]

describe('ConsoleOutput', () => {
it('renders without crashing', () => {
expect(() => render(<ConsoleOutput errors={[]} />)).not.toThrow()
})

it('displays errors correctly', () => {
render(<ConsoleOutput errors={sampleErrors} />)

// Using more precise text matching
expect(
screen.getByText('[webpack-dev-server] ERROR in ./components/ErrorPages/utils.ts')
).toBeInTheDocument()
expect(screen.getByText('Warning: Something went wrong')).toBeInTheDocument()
expect(screen.getByText('[webpack-dev-server] Another error occurred')).toBeInTheDocument()
})

it('filters errors based on search input', () => {
render(<ConsoleOutput errors={sampleErrors} />)

const searchInput = screen.getByPlaceholderText('Search logs...')
fireEvent.change(searchInput, { target: { value: 'message 1' } })

expect(
screen.getByText('[webpack-dev-server] ERROR in ./components/ErrorPages/utils.ts')
).toBeInTheDocument()
expect(screen.queryByText('Warning: Something went wrong')).not.toBeInTheDocument()
expect(
screen.queryByText('[webpack-dev-server] Another error occurred')
).not.toBeInTheDocument()
})

it('toggles error details when clicked', () => {
render(<ConsoleOutput errors={sampleErrors} />)

// Find and click the first error
const firstError = screen.getByText(
'[webpack-dev-server] ERROR in ./components/ErrorPages/utils.ts'
)
fireEvent.click(firstError)

// Check if stack trace is visible
expect(screen.getByText('× Module not found: Error message 1')).toBeInTheDocument()
})

it('applies correct styling based on error level', () => {
render(<ConsoleOutput errors={sampleErrors} />)

// Error should have red styling
const errorElement = screen
.getByText('[webpack-dev-server] ERROR in ./components/ErrorPages/utils.ts')
.closest('.bg-red-100')
expect(errorElement).toBeInTheDocument()

// Warning should have yellow styling
const warningElement = screen
.getByText('Warning: Something went wrong')
.closest('.bg-yellow-100')
expect(warningElement).toBeInTheDocument()
})

it('handles empty error list', () => {
render(<ConsoleOutput errors={[]} />)
const searchInput = screen.getByPlaceholderText('Search logs...')
expect(searchInput).toBeInTheDocument()
expect(screen.queryByRole('button')).not.toBeInTheDocument()
})

it('expands info logs to show stack trace', () => {
render(<ConsoleOutput errors={[sampleErrors[3]]} />)

// Find and click the info message
const infoMessage = screen.getByText('Server started successfully')
fireEvent.click(infoMessage)

// Check if stack trace is visible
expect(screen.getByText('Info stack trace')).toBeInTheDocument()
})
})
49 changes: 39 additions & 10 deletions src/components/ConsoleOutput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,53 @@ export const ConsoleOutput: React.FC<ConsoleOutputProps> = ({ errors, onErrorCou
const parsedErrors = useMemo(() => {
const parsed = errors.map((error, index) => {
const cleanError = error.replace(/^"|"$/g, '').replace(/\\n/g, '\n')
const parts = cleanError.split('"').filter(Boolean)
const format = parts[0]
const args = parts.slice(1)
let message = cleanError
let jsonData = null
let stack = ''
let level = 'error'

let message = format
args.forEach((arg) => {
message = message.replace('%s', arg)
})
try {
// Try to parse as JSON first
const parsedJson = JSON.parse(cleanError)
if (parsedJson.level && parsedJson.message) {
jsonData = parsedJson
message = parsedJson.message
stack = parsedJson.stack || ''
level = parsedJson.level
return {
id: index,
level,
message,
stack,
details: jsonData,
}
}
} catch (e) {
// If JSON parsing fails, try the other formats
try {
const jsonMatch = cleanError.match(/(?:Error:|Warning:)({.*})/i)
if (jsonMatch && jsonMatch[1]) {
jsonData = JSON.parse(jsonMatch[1])
message = cleanError.split('{')[0].trim()
if (jsonData.status) message += ` Status: ${jsonData.status}`
if (jsonData.data) message += ` ${jsonData.data}`
}
} catch (innerE) {
// If all JSON parsing fails, fall back to original parsing logic
const parts = cleanError.split('"').filter(Boolean)
message = parts[0] || cleanError
}
}

return {
id: index,
level: message.startsWith('Warning:') ? 'warn' : 'error',
message: message,
message,
stack: message.split('\n').slice(1).join('\n'),
details: jsonData,
}
})

// Count actual errors and notify parent
const errorCount = parsed.filter((error) => error.level === 'error').length
onErrorCountChange?.(errorCount)

Expand All @@ -72,7 +101,7 @@ export const ConsoleOutput: React.FC<ConsoleOutputProps> = ({ errors, onErrorCou
<>
<Input
type="text"
placeholder="Search errors..."
placeholder="Search logs..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="mb-4"
Expand Down
Loading

0 comments on commit d4b57c3

Please sign in to comment.