mirror of
https://github.com/supabase/supabase.git
synced 2026-10-15 06:09:08 +03:00
tests styles
This commit is contained in:
1 parent
c8eaaa8a50
commit
d2a310aa88
5 files changed
+85
-86
No files matched your search
@@ -31,6 +31,7 @@ interface TestRowProps {
|
||||
prependQuery?: DatabaseTest | null
|
||||
canRun?: boolean
|
||||
onSelectTest: (test: DatabaseTest) => void
|
||||
index: number
|
||||
}
|
||||
|
||||
// Utility function to validate test query format
|
||||
@@ -40,7 +41,7 @@ const isValidTestQuery = (query: string): boolean => {
|
||||
}
|
||||
|
||||
const TestRow = forwardRef<TestRowHandle, TestRowProps>(
|
||||
({ test, prependQuery, canRun = false, onSelectTest }, ref) => {
|
||||
({ test, prependQuery, canRun = false, onSelectTest, index }, ref) => {
|
||||
const project = useSelectedProject()
|
||||
const [isRunning, setIsRunning] = useState(false)
|
||||
const [status, setStatus] = useState<'queued' | 'passed' | 'failed' | undefined>()
|
||||
@@ -126,52 +127,58 @@ const TestRow = forwardRef<TestRowHandle, TestRowProps>(
|
||||
if (status === 'failed') {
|
||||
return <Badge variant="destructive">Failed</Badge>
|
||||
}
|
||||
return <Badge variant="outline">Not run</Badge>
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableRow key={test.id}>
|
||||
<TableCell>
|
||||
<p className="text-sm cursor-pointer" onClick={() => onSelectTest(test)}>
|
||||
{test.name}
|
||||
</p>
|
||||
</TableCell>
|
||||
<TableCell>{getStatusBadge()}</TableCell>
|
||||
<TableCell>{lastRun ? dayjs(lastRun).fromNow() : 'Never'}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
{canRun && (
|
||||
<ButtonTooltip
|
||||
type="default"
|
||||
icon={<Play />}
|
||||
loading={isRunning}
|
||||
disabled={isRunning || status === 'queued' || !isTestQueryValid}
|
||||
onClick={handleRunTest}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
text: !isTestQueryValid
|
||||
? 'Test query must start with BEGIN; and end with ROLLBACK;'
|
||||
: undefined,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="text" icon={<MoreVertical />} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={() => setIsDeleteModalOpen(true)}>
|
||||
<Trash size={14} />
|
||||
<span className="ml-2">Remove test</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<div
|
||||
key={test.id}
|
||||
className="flex text-sm items-center gap-3 border-b px-4 py-3 last:border-b-0"
|
||||
>
|
||||
<span className="flex items-center justify-center w-5 h-5 text-xs rounded bg border border-default text-foreground-lighter">
|
||||
{index + 1}
|
||||
</span>
|
||||
<p className="cursor-pointer flex-1" onClick={() => onSelectTest(test)}>
|
||||
{test.name}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="text-foreground-light text-xs">
|
||||
{lastRun ? dayjs(lastRun).fromNow() : null}
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{getStatusBadge()}
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
{canRun && (
|
||||
<ButtonTooltip
|
||||
type="default"
|
||||
icon={<Play />}
|
||||
loading={isRunning}
|
||||
disabled={isRunning || status === 'queued' || !isTestQueryValid}
|
||||
onClick={handleRunTest}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
text: !isTestQueryValid
|
||||
? 'Test query must start with BEGIN; and end with ROLLBACK;'
|
||||
: undefined,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="text" icon={<MoreVertical />} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={() => setIsDeleteModalOpen(true)}>
|
||||
<Trash size={14} />
|
||||
<span className="ml-2">Remove test</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
<ConfirmationModal
|
||||
visible={isDeleteModalOpen}
|
||||
title="Delete test"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { partition } from 'lodash'
|
||||
import { useRouter } from 'next/router'
|
||||
import { forwardRef, useImperativeHandle, useMemo, useRef, useState } from 'react'
|
||||
import { Search, MoreVertical, Trash } from 'lucide-react'
|
||||
import { Search, MoreVertical, Trash, Settings2, FlaskConical } from 'lucide-react'
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
@@ -39,10 +39,11 @@ const isValidTestQuery = (query: string): boolean => {
|
||||
// Simple component for setup test rows - just name and actions
|
||||
interface SetupTestRowProps {
|
||||
test: DatabaseTest
|
||||
index: number
|
||||
onSelectTest: (test: DatabaseTest) => void
|
||||
}
|
||||
|
||||
const SetupTestRow = ({ test, onSelectTest }: SetupTestRowProps) => {
|
||||
const SetupTestRow = ({ test, index, onSelectTest }: SetupTestRowProps) => {
|
||||
const project = useSelectedProject()
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false)
|
||||
|
||||
@@ -58,7 +59,10 @@ const SetupTestRow = ({ test, onSelectTest }: SetupTestRowProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full flex justify-between items-center p-4">
|
||||
<div className="w-full flex justify-between items-center p-4 gap-3 border-b last:border-b-0">
|
||||
<span className="flex items-center justify-center w-5 h-5 text-xs rounded bg border border-default text-foreground-lighter">
|
||||
{index + 1}
|
||||
</span>
|
||||
<p className="text-sm cursor-pointer w-full" onClick={() => onSelectTest(test)}>
|
||||
{test.name}
|
||||
</p>
|
||||
@@ -230,8 +234,8 @@ const DatabaseTestsList = forwardRef<TestsListHandle, DatabaseTestsListProps>(
|
||||
environment.
|
||||
</p>
|
||||
) : (
|
||||
tests.map((test) => (
|
||||
<SetupTestRow key={test.id} test={test} onSelectTest={onSelectTest} />
|
||||
tests.map((test, index) => (
|
||||
<SetupTestRow key={test.id} index={index} test={test} onSelectTest={onSelectTest} />
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
@@ -240,28 +244,19 @@ const DatabaseTestsList = forwardRef<TestsListHandle, DatabaseTestsListProps>(
|
||||
|
||||
// Regular tests view with full TestRow component
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Last Run</TableHead>
|
||||
<TableHead></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{tests.map((test) => (
|
||||
<TestRow
|
||||
ref={(el) => (testRowRefs.current[test.id] = el)}
|
||||
key={test.id}
|
||||
test={test}
|
||||
prependQuery={setupTest}
|
||||
canRun={true}
|
||||
onSelectTest={onSelectTest}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<div>
|
||||
{tests.map((test, index) => (
|
||||
<TestRow
|
||||
ref={(el) => (testRowRefs.current[test.id] = el)}
|
||||
key={test.id}
|
||||
index={index}
|
||||
test={test}
|
||||
prependQuery={setupTest}
|
||||
canRun={true}
|
||||
onSelectTest={onSelectTest}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -286,7 +281,10 @@ const DatabaseTestsList = forwardRef<TestsListHandle, DatabaseTestsListProps>(
|
||||
{/* Setup Tests Group - Always show */}
|
||||
<Card>
|
||||
<CardHeader className="px-4">
|
||||
<CardTitle className="text-sm font-medium">Before Tests</CardTitle>
|
||||
<div className="flex items-center gap-2">
|
||||
<Settings2 strokeWidth={1.5} size={16} className="text-foreground-lighter" />
|
||||
<CardTitle>Before Tests</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
{renderTestTable(filteredSetupTests, true)}
|
||||
</Card>
|
||||
@@ -295,7 +293,10 @@ const DatabaseTestsList = forwardRef<TestsListHandle, DatabaseTestsListProps>(
|
||||
{filteredRegularTests.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader className="px-4">
|
||||
<CardTitle className="text-sm font-medium">Tests</CardTitle>
|
||||
<div className="flex items-center gap-2">
|
||||
<FlaskConical strokeWidth={1.5} size={16} className="text-foreground-lighter" />
|
||||
<CardTitle>Tests</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
{renderTestTable(filteredRegularTests, false)}
|
||||
</Card>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useSqlTitleGenerateMutation } from 'data/ai/sql-title-mutation'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
import { databaseTestsKeys } from './database-tests-key'
|
||||
import { DatabaseTest } from './database-tests-query'
|
||||
@@ -18,20 +17,16 @@ export const useDatabaseTestCreateMutation = ({
|
||||
onSuccess?: () => void
|
||||
} = {}) => {
|
||||
const queryClient = useQueryClient()
|
||||
const { mutateAsync: generateSqlTitle } = useSqlTitleGenerateMutation()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ projectRef, query }: DatabaseTestCreateVariables) => {
|
||||
// AI title generation
|
||||
const { title } = await generateSqlTitle({ sql: query })
|
||||
|
||||
mutationFn: async ({ projectRef, query, name }: DatabaseTestCreateVariables) => {
|
||||
// This is a mock implementation.
|
||||
// In a real scenario, you would save the test to a file or a database table.
|
||||
const key = databaseTestsKeys.list(projectRef)
|
||||
const currentTests = queryClient.getQueryData<DatabaseTest[]>(key) ?? []
|
||||
const newTest: DatabaseTest = {
|
||||
id: uuidv4(),
|
||||
name: title,
|
||||
name,
|
||||
query,
|
||||
}
|
||||
queryClient.setQueryData(key, [...currentTests, newTest])
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useSqlTitleGenerateMutation } from 'data/ai/sql-title-mutation'
|
||||
import { databaseTestsKeys } from './database-tests-key'
|
||||
import { DatabaseTest } from './database-tests-query'
|
||||
|
||||
@@ -18,19 +17,13 @@ export const useDatabaseTestUpdateMutation = ({
|
||||
onSuccess?: () => void
|
||||
} = {}) => {
|
||||
const queryClient = useQueryClient()
|
||||
const { mutateAsync: generateSqlTitle } = useSqlTitleGenerateMutation()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ projectRef, id, query, name }: DatabaseTestUpdateVariables) => {
|
||||
// Generate AI title if name is not provided
|
||||
const finalName = name ?? (await generateSqlTitle({ sql: query })).title
|
||||
|
||||
// This is a mock implementation.
|
||||
// In a real scenario, you would update the test in a file or a database table.
|
||||
const key = databaseTestsKeys.list(projectRef)
|
||||
const currentTests = queryClient.getQueryData<DatabaseTest[]>(key) ?? []
|
||||
const updatedTests = currentTests.map((test) =>
|
||||
test.id === id ? { ...test, name: finalName, query } : test
|
||||
test.id === id ? { ...test, name, query } : test
|
||||
)
|
||||
queryClient.setQueryData(key, updatedTests)
|
||||
|
||||
|
||||
@@ -63,8 +63,10 @@ const DatabaseTestsPage: NextPageWithLayout = () => {
|
||||
updateTest({
|
||||
projectRef: project?.ref!,
|
||||
id: test.id,
|
||||
name: test.name,
|
||||
query,
|
||||
})
|
||||
setEditorPanel({ open: false })
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -84,6 +86,7 @@ ROLLBACK;
|
||||
onSave: (query) => {
|
||||
createTest({
|
||||
projectRef: project?.ref!,
|
||||
name: 'New test',
|
||||
connectionString: project?.connectionString,
|
||||
query,
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user