Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
filiphric committed Dec 5, 2024
1 parent 3b236d6 commit b1a75c2
Show file tree
Hide file tree
Showing 27 changed files with 2,414 additions and 153 deletions.
73 changes: 73 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: CI

on:
push:
pull_request:
types: [opened, synchronize, reopened]

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
68 changes: 68 additions & 0 deletions src/components/ConsoleOutput.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
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"'
]

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 errors...')
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 errors...')
expect(searchInput).toBeInTheDocument()
expect(screen.queryByRole('button')).not.toBeInTheDocument()
})
})
125 changes: 125 additions & 0 deletions src/components/DevToolsUi.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import userEvent from '@testing-library/user-event'
import { describe, it, expect } from 'vitest'

import { DiagnosticData } from '@/types/DiagnosticData'

import { DevToolsUI } from './DevToolsUi'
import { render, screen } from '../test/test-utils'

describe('DevToolsUI', () => {
const sampleDiagnosticData: DiagnosticData = {
url: 'https://test.com',
description: 'Test description',
browserInfo: {
browserName: 'Chrome',
browserVersion: '100.0.0',
os: 'Windows',
osVersion: '10',
platform: 'Desktop',
language: 'en-US'
},
entityInfo: {
bugReportDetails: {},
'metabase-info': {},
'system-info': {}
},
frontendErrors: [
'"[webpack-dev-server] ERROR in ./components/ErrorPages/utils.ts\\n × Module not found: Error message 1"',
'"[webpack-dev-server] ERROR in ./components/ErrorPages/tab.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"'
],
backendErrors: [
{ message: 'Backend Error 1', timestamp: '2024-01-01' },
{ message: 'Backend Error 2', timestamp: '2024-01-02' }
],
userLogs: [
{ message: 'User Log 1', timestamp: '2024-01-01' }
],
logs: [
{ message: 'System Log 1', timestamp: '2024-01-01' }
]
}

it('renders without crashing', () => {
expect(() => render(<DevToolsUI diagnosticData={sampleDiagnosticData} />)).not.toThrow()
})

it('displays basic info correctly', () => {
render(<DevToolsUI diagnosticData={sampleDiagnosticData} />)

// Check URL and description
expect(screen.getByText('URL:')).toBeInTheDocument()
expect(screen.getByText('https://test.com')).toBeInTheDocument()
expect(screen.getByText('Description:')).toBeInTheDocument()
expect(screen.getByText('Test description')).toBeInTheDocument()

// Check browser info
expect(screen.getByText('Browser:')).toBeInTheDocument()
expect(screen.getByText('Chrome 100.0.0')).toBeInTheDocument()
expect(screen.getByText('OS:')).toBeInTheDocument()
expect(screen.getByText('Windows 10')).toBeInTheDocument()
})

it('switches tabs correctly', async () => {
const user = userEvent.setup()
render(<DevToolsUI diagnosticData={sampleDiagnosticData} />)

// Click Console output tab
await user.click(screen.getByRole('tab', { name: 'Console output' }))
expect(screen.getByText('[webpack-dev-server] ERROR in ./components/ErrorPages/utils.ts')).toBeInTheDocument()
})

it('displays error badges correctly', () => {
render(<DevToolsUI diagnosticData={sampleDiagnosticData} />)

// Check backend errors badge
const backendErrorsBadge = screen.getByText('2')
expect(backendErrorsBadge).toBeInTheDocument()
expect(backendErrorsBadge.className).toContain('destructive')
})

it('displays raw data correctly', async () => {
const user = userEvent.setup()
render(<DevToolsUI diagnosticData={sampleDiagnosticData} />)

// Switch to raw data tab
await user.click(screen.getByRole('tab', { name: 'Raw Data' }))

// Check if JSON data is displayed
expect(screen.getByText(/"url":/)).toBeInTheDocument()
expect(screen.getByText(/"description":/)).toBeInTheDocument()
})

it('handles empty diagnostic data', () => {
const emptyData: DiagnosticData = {
url: '',
description: '',
browserInfo: {},
entityInfo: {},
frontendErrors: [],
backendErrors: [],
userLogs: [],
logs: []
}

render(<DevToolsUI diagnosticData={emptyData} />)

// Should still render without errors
expect(screen.getByText('Unknown URL')).toBeInTheDocument()
expect(screen.queryByText('Unknown browser')).not.toBeInTheDocument()
})

it('updates frontend error count', async () => {
const user = userEvent.setup()
render(<DevToolsUI diagnosticData={sampleDiagnosticData} />)

// Switch to console output tab
await user.click(screen.getByRole('tab', { name: 'Console output' }))

// Check if error count badge is updated
const frontendErrorsBadge = screen.getByText('3')
expect(frontendErrorsBadge).toBeInTheDocument()
expect(frontendErrorsBadge.className).toContain('destructive')
})
})
18 changes: 9 additions & 9 deletions src/components/DevToolsUi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { DiagnosticData } from '@/types/DiagnosticData'

import { ConsoleOutput } from './ConsoleOutput'
import LogsTable from './LogsTable'
import MetadataTable from './MetadataTable'
import { LogsTable } from './LogsTable'
import { MetadataTable } from './MetadataTable'
import { RawContent } from './RawContent'

interface DevToolsUIProps {
diagnosticData: DiagnosticData
}

export default function DevToolsUI({ diagnosticData }: DevToolsUIProps) {
export const DevToolsUI = ({ diagnosticData }: DevToolsUIProps) => {
const [frontendErrorCount, setFrontendErrorCount] = useState(0)

const jsonString = JSON.stringify(diagnosticData, null, 2)
Expand All @@ -27,7 +27,7 @@ export default function DevToolsUI({ diagnosticData }: DevToolsUIProps) {
<TabsTrigger value="basicInfo">Basic Info</TabsTrigger>
<TabsTrigger value="entityInfo">Entity Info</TabsTrigger>
<TabsTrigger value="frontendErrors" className="tabs-trigger relative">
Frontend Errors
Console output
{frontendErrorCount > 0 && (
<Badge variant="destructive" className="ml-2 absolute -top-2 -right-2 z-10">
{frontendErrorCount}
Expand All @@ -51,24 +51,24 @@ export default function DevToolsUI({ diagnosticData }: DevToolsUIProps) {
<TabsContent value="basicInfo" className="h-[calc(100%-3rem)]">
<div className="space-y-2">
<p>
<strong>URL:</strong> {diagnosticData.url}
<strong>URL:</strong> {diagnosticData.url || 'Unknown URL'}
</p>
<p>
<strong>Description:</strong> {diagnosticData.description}
</p>
{diagnosticData.browserInfo && (
<>
<p>
<strong>Browser:</strong> {diagnosticData.browserInfo.browserName} {diagnosticData.browserInfo.browserVersion}
<strong>Browser:</strong> {diagnosticData.browserInfo.browserName || 'Unknown browser'} {diagnosticData.browserInfo.browserVersion || 'Unknown version'}
</p>
<p>
<strong>OS:</strong> {diagnosticData.browserInfo.os} {diagnosticData.browserInfo.osVersion}
<strong>OS:</strong> {diagnosticData.browserInfo.os || 'Unknown OS'} {diagnosticData.browserInfo.osVersion || 'Unknown version'}
</p>
<p>
<strong>Platform:</strong> {diagnosticData.browserInfo.platform}
<strong>Platform:</strong> {diagnosticData.browserInfo.platform || 'Unknown platform'}
</p>
<p>
<strong>Language:</strong> {diagnosticData.browserInfo.language}
<strong>Language:</strong> {diagnosticData.browserInfo.language || 'Unknown language'}
</p>
</>
)}
Expand Down
Loading

0 comments on commit b1a75c2

Please sign in to comment.