import { BookOpenIcon, MagnifyingGlassIcon } from "@heroicons/react/20/solid"; import { type MetaFunction, Outlet, useParams } from "@remix-run/react"; import { type LoaderFunctionArgs } from "@remix-run/server-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { TestHasNoTasks } from "~/components/BlankStatePanels"; import { MainCenteredContainer, PageBody, PageContainer } from "~/components/layout/AppLayout"; import { LinkButton } from "~/components/primitives/Buttons"; import { Callout } from "~/components/primitives/Callout"; import { Input } from "~/components/primitives/Input"; import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader"; import { Paragraph } from "~/components/primitives/Paragraph"; import { RadioButtonCircle } from "~/components/primitives/RadioButton"; import { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "~/components/primitives/Resizable"; import { Table, TableBlankRow, TableBody, TableCell, TableHeader, TableHeaderCell, TableRow, } from "~/components/primitives/Table"; import { TaskTriggerSourceIcon } from "~/components/runs/v3/TaskTriggerSource"; import { useEnvironment } from "~/hooks/useEnvironment"; import { useFuzzyFilter } from "~/hooks/useFuzzyFilter"; import { useLinkStatus } from "~/hooks/useLinkStatus"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { findProjectBySlug } from "~/models/project.server"; import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server"; import { type TaskListItem, TestPresenter } from "~/presenters/v3/TestPresenter.server"; import { requireUserId } from "~/services/session.server"; import { cn } from "~/utils/cn"; import { docsPath, EnvironmentParamSchema, v3TestTaskPath } from "~/utils/pathBuilder"; export const meta: MetaFunction = () => { return [ { title: `Test | Trigger.dev`, }, ]; }; export const loader = async ({ request, params }: LoaderFunctionArgs) => { const userId = await requireUserId(request); const { projectParam, organizationSlug, envParam } = EnvironmentParamSchema.parse(params); const project = await findProjectBySlug(organizationSlug, projectParam, userId); if (!project) { throw new Response(undefined, { status: 404, statusText: "Project not found", }); } const environment = await findEnvironmentBySlug(project.id, envParam, userId); if (!environment) { throw new Response(undefined, { status: 404, statusText: "Environment not found", }); } const presenter = new TestPresenter(); const result = await presenter.call({ userId, projectId: project.id, environmentId: environment.id, environmentType: environment.type, }); return typedjson(result); }; export default function Page() { const { tasks } = useTypedLoaderData(); const { taskParam } = useParams(); return ( Test docs {tasks.length === 0 ? ( ) : taskParam ? ( // Task selected via URL → skip the picker; the child route owns the page. ) : ( // No task in URL: show the picker as a fallback so users landing on // /test (e.g. from the runs blank state with multiple task filters) // can still choose one.
)}
); } function TaskSelector({ tasks, activeTaskIdentifier, }: { tasks: TaskListItem[]; activeTaskIdentifier?: string; }) { const { filterText, setFilterText, filteredItems } = useFuzzyFilter({ items: tasks, keys: ["taskIdentifier", "friendlyId", "id", "filePath", "triggerSource"], }); const hasTaskInEnvironment = activeTaskIdentifier ? tasks.some((t) => t.taskIdentifier === activeTaskIdentifier) : undefined; return (
setFilterText(e.target.value)} />
{hasTaskInEnvironment === false && (
There is no "{activeTaskIdentifier}" task in the selected environment.
)} Task {filteredItems.length > 0 ? ( filteredItems.map((t) => ) ) : ( No tasks match "{filterText}" )}
); } function TaskRow({ task }: { task: TaskListItem }) { const organization = useOrganization(); const project = useProject(); const environment = useEnvironment(); const path = v3TestTaskPath(organization, project, environment, task); const { isActive, isPending } = useLinkStatus(path); return (
{task.taskIdentifier}
); }